246 lines
6.4 KiB
Python
246 lines
6.4 KiB
Python
from enum import Enum
|
||
import colorama
|
||
from colorama import Fore, Back, Style
|
||
|
||
class FieldType(Enum):
|
||
empty = 1
|
||
water = 2
|
||
ship = 3
|
||
|
||
class ShipOrientation(Enum):
|
||
unknown = 0
|
||
none = 1
|
||
north = 2
|
||
south = 3
|
||
east = 4
|
||
west = 5
|
||
both = 6
|
||
|
||
class Field(object):
|
||
def __init__(self):
|
||
self.__fixed = False
|
||
self.__type = FieldType.empty
|
||
self.__marked_type = FieldType.empty
|
||
self.__orientation = ShipOrientation.none
|
||
|
||
@property
|
||
def marked_type(self):
|
||
return self.__type if self.__fixed else self.__marked_type
|
||
|
||
def set_fixed(self, typ=None, orientation=ShipOrientation.unknown):
|
||
self.__fixed = True
|
||
|
||
if typ is not None:
|
||
self.__type = typ
|
||
self.__marked_type = typ
|
||
self.__orientation = orientation
|
||
|
||
def __str__(self):
|
||
typ = self.__type if self.__fixed else self.__marked_type
|
||
|
||
if typ == FieldType.empty:
|
||
return ' '
|
||
elif typ == FieldType.water:
|
||
return '~~'
|
||
elif typ == FieldType.ship:
|
||
if self.__orientation == ShipOrientation.unknown:
|
||
return '??'
|
||
elif self.__orientation == ShipOrientation.none:
|
||
return '<>'
|
||
elif self.__orientation == ShipOrientation.north:
|
||
return '^^'
|
||
elif self.__orientation == ShipOrientation.south:
|
||
return 'vv'
|
||
elif self.__orientation == ShipOrientation.west:
|
||
return ' <'
|
||
elif self.__orientation == ShipOrientation.east:
|
||
return '> '
|
||
elif self.__orientation == ShipOrientation.both:
|
||
return 'XX'
|
||
|
||
def set_type(self, typ, orientation=ShipOrientation.unknown):
|
||
self.__type = typ
|
||
self.__orientation = orientation
|
||
|
||
class Table(object):
|
||
def __init__(self, width, height):
|
||
self.__fields = []
|
||
self.__width = width
|
||
self.__height = height
|
||
self.__col_counts = []
|
||
self.__row_counts = []
|
||
self.__ships = []
|
||
|
||
for count in range(0, height):
|
||
self.__fields.append([])
|
||
self.__row_counts.append(0)
|
||
|
||
for count in range(0, width):
|
||
self.__col_counts.append(0)
|
||
|
||
for row in range(0, height):
|
||
self.__fields[row].append(Field())
|
||
|
||
self.clean()
|
||
|
||
@property
|
||
def width(self):
|
||
return self.__width
|
||
|
||
@property
|
||
def height(self):
|
||
return self.__height
|
||
|
||
def __check_height(self, row):
|
||
if row not in range(0, self.__height):
|
||
raise IndexError("Invalid row number")
|
||
|
||
def __check_width(self, col):
|
||
if col not in range(0, self.__width):
|
||
raise IndexError("Invalid column number")
|
||
|
||
def row(self, row):
|
||
self.__check_height(row)
|
||
|
||
# TODO: This doesn’t feel right
|
||
return self.__fields[row]
|
||
|
||
def col(self, col):
|
||
self.__check_width(col)
|
||
|
||
# TODO: This doesn’t feel right
|
||
return [f[col] for f in f.__fields]
|
||
|
||
def clean(self):
|
||
for row in self.__fields:
|
||
for field in row:
|
||
field.hidden_type = FieldType.water
|
||
field.player_type = FieldType.empty
|
||
field.fixed = False
|
||
|
||
def __check_collision(self, parts):
|
||
pass
|
||
|
||
def add_ship(self, start_row, start_col, length, vertical):
|
||
row, col = start_row - 1, start_col - 1
|
||
parts = []
|
||
|
||
for i in range(0, length):
|
||
parts.append((row, col))
|
||
|
||
if vertical:
|
||
row += 1
|
||
else:
|
||
col += 1
|
||
|
||
count = 0
|
||
|
||
for row, col in parts:
|
||
count += 1
|
||
orientation = ShipOrientation.unknown
|
||
|
||
if length == 1:
|
||
orientation=ShipOrientation.none
|
||
else:
|
||
if count == 1:
|
||
orientation = ShipOrientation.north if vertical \
|
||
else ShipOrientation.west
|
||
elif count == length:
|
||
orientation = ShipOrientation.south if vertical \
|
||
else ShipOrientation.east
|
||
else:
|
||
orientation = ShipOrientation.both
|
||
|
||
self.__fields[row][col].set_type(FieldType.ship,
|
||
orientation=orientation)
|
||
|
||
def reveal(self, row, col):
|
||
self.__fields[row - 1][col - 1].set_fixed()
|
||
|
||
def reveal_all(self):
|
||
for row in self.__fields:
|
||
for field in row:
|
||
field.fixed = True
|
||
|
||
@property
|
||
def solved(self):
|
||
# Check if all fileds have been marked
|
||
for row in self.__fields:
|
||
for field in row:
|
||
if field.player_type == FieldType.empty:
|
||
return False
|
||
|
||
# TODO: Check if marked ships are placed sanely
|
||
# TODO: Check if side-numbers equal the number of marked ship-parts
|
||
|
||
return True
|
||
|
||
def mark(self, row, col, typ):
|
||
field = self.__field(row, col)
|
||
|
||
if field is not None:
|
||
field.player_type = typ
|
||
|
||
def is_ship(self, row, col):
|
||
return self.__fields[row][col].player_type == FieldType.ship
|
||
|
||
def __str__(self):
|
||
def divider():
|
||
ret = '+'
|
||
|
||
for i in range(0, self.__width):
|
||
ret += '--+'
|
||
|
||
ret += "\n"
|
||
|
||
return ret
|
||
|
||
ret = divider()
|
||
|
||
for row in self.__fields:
|
||
ret += '|'
|
||
|
||
for field in row:
|
||
ret += '{}|'.format(field)
|
||
|
||
ret += "\n"
|
||
ret += divider()
|
||
|
||
return ret
|
||
|
||
class Solver(object):
|
||
def __init__(self, table):
|
||
self.table = table
|
||
|
||
def mark_edges(self):
|
||
for row in range(0, self.table.height):
|
||
for col in range(0, self.table.width):
|
||
if self.table.is_ship(row, col):
|
||
print("MARK!")
|
||
self.table.mark(row - 1, col - 1, FieldType.water)
|
||
self.table.mark(row - 1, col + 1, FieldType.water)
|
||
self.table.mark(row + 1, col - 1, FieldType.water)
|
||
self.table.mark(row + 1, col + 1, FieldType.water)
|
||
|
||
def show(self):
|
||
print(str(self.table))
|
||
|
||
colorama.init()
|
||
|
||
t = Table(6, 6)
|
||
t.add_ship(1, 3, 1, False)
|
||
t.add_ship(2, 5, 2, False)
|
||
t.add_ship(3, 1, 3, False)
|
||
t.add_ship(5, 1, 1, False)
|
||
t.add_ship(5, 3, 2, True)
|
||
t.add_ship(6, 5, 1, False)
|
||
t.reveal(1, 2)
|
||
t.reveal(3, 3)
|
||
t.reveal(5, 1)
|
||
t.reveal(5, 2)
|
||
|
||
s = Solver(t)
|
||
s.show()
|
||
s.mark_edges()
|
||
s.show()
|