|
@@ -0,0 +1,151 @@
|
|
|
|
+def empty_board(board_size):
|
|
|
|
+ return {(x, y): 0 for x in range(1, board_size + 1) for y in range(1, board_size + 1)}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def empty_history():
|
|
|
|
+ return []
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class IllegalMoveError(ValueError):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class ChessState:
|
|
|
|
+ def __init__(self, board=None, history=empty_history(), predecessor=None, board_size=8):
|
|
|
|
+ self.__board_size = board_size
|
|
|
|
+ self.__history = history
|
|
|
|
+ if board is not None:
|
|
|
|
+ self.__board = board
|
|
|
|
+ else:
|
|
|
|
+ self.__board = empty_board(board_size)
|
|
|
|
+ self.__predecessor = predecessor
|
|
|
|
+ self._bool_board_cache = None
|
|
|
|
+ self._bool_board_mirrored_cache = None
|
|
|
|
+
|
|
|
|
+ def put(self, x, y, direction):
|
|
|
|
+ positions = self.move_positions(x, y, direction)
|
|
|
|
+
|
|
|
|
+ if not self.possible(x, y, direction):
|
|
|
|
+ raise IllegalMoveError
|
|
|
|
+
|
|
|
|
+ board = self.__board.copy()
|
|
|
|
+ for pos in positions:
|
|
|
|
+ board[pos] = len(self.__history) + 1
|
|
|
|
+
|
|
|
|
+ return ChessState(board=board,
|
|
|
|
+ history=self.__history + [positions],
|
|
|
|
+ board_size=self.__board_size,
|
|
|
|
+ predecessor=self)
|
|
|
|
+
|
|
|
|
+ def undo(self):
|
|
|
|
+ return self.__predecessor
|
|
|
|
+
|
|
|
|
+ def move_positions(self, x, y, direction):
|
|
|
|
+ if direction not in ['down', 'right']:
|
|
|
|
+ raise ValueError
|
|
|
|
+ if direction == 'down':
|
|
|
|
+ mps = [(x, y), (x, y + 1), (x, y + 2)]
|
|
|
|
+ else: # right
|
|
|
|
+ mps = [(x, y), (x + 1, y), (x + 2, y)]
|
|
|
|
+ for pos in mps:
|
|
|
|
+ if pos not in self.__board:
|
|
|
|
+ raise IndexError
|
|
|
|
+ return mps
|
|
|
|
+
|
|
|
|
+ def possible(self, x, y, direction):
|
|
|
|
+ try:
|
|
|
|
+ for pos in self.move_positions(x, y, direction):
|
|
|
|
+ if self.__board[pos]:
|
|
|
|
+ return False
|
|
|
|
+ except IndexError:
|
|
|
|
+ return False
|
|
|
|
+
|
|
|
|
+ return True
|
|
|
|
+
|
|
|
|
+ def board(self):
|
|
|
|
+ return self.__board.copy()
|
|
|
|
+
|
|
|
|
+ def history(self):
|
|
|
|
+ return self.__history.copy()
|
|
|
|
+
|
|
|
|
+ def predecessor(self):
|
|
|
|
+ return self.__predecessor.copy()
|
|
|
|
+
|
|
|
|
+ def board_size(self):
|
|
|
|
+ return self.__board_size
|
|
|
|
+
|
|
|
|
+ def board_label(self):
|
|
|
|
+ s = ''
|
|
|
|
+ for x in range(1, self.__board_size + 1):
|
|
|
|
+ s += '{'
|
|
|
|
+ for y in range(1, self.__board_size + 1):
|
|
|
|
+ if self.__board[(x, y)]:
|
|
|
|
+ s += str(self.__board[(x, y)])
|
|
|
|
+ if y < self.__board_size:
|
|
|
|
+ s += '|'
|
|
|
|
+ s += '}'
|
|
|
|
+ if x < self.__board_size:
|
|
|
|
+ s += '|'
|
|
|
|
+ return s
|
|
|
|
+
|
|
|
|
+ def bool_board(self):
|
|
|
|
+ if self._bool_board_cache is None:
|
|
|
|
+ self._bool_board_cache = {x: self.__board[x] != 0 for x in self.__board}
|
|
|
|
+
|
|
|
|
+ return self._bool_board_cache
|
|
|
|
+
|
|
|
|
+ def bool_board_mirrored(self):
|
|
|
|
+ if self._bool_board_mirrored_cache is None:
|
|
|
|
+ self._bool_board_mirrored_cache = [
|
|
|
|
+ {self.bool_board()[x, self.__board_size + 1 - y] for (x, y) in self.bool_board()},
|
|
|
|
+ {self.bool_board()[self.__board_size + 1 - x, y] for (x, y) in self.bool_board()},
|
|
|
|
+ {self.bool_board()[self.__board_size + 1 - x, self.__board_size + 1 - y] for (x, y) in self.bool_board()},
|
|
|
|
+ ]
|
|
|
|
+ return self._bool_board_mirrored_cache
|
|
|
|
+
|
|
|
|
+ def symmetric_to(self, other):
|
|
|
|
+ if self.bool_board() == other.bool_board():
|
|
|
|
+ return True
|
|
|
|
+ # # no gain
|
|
|
|
+ # for board in other.bool_board_mirrored():
|
|
|
|
+ # if self.bool_board() == board:
|
|
|
|
+ # return True
|
|
|
|
+ return False
|
|
|
|
+
|
|
|
|
+ def next_move(self):
|
|
|
|
+ # try to find a move where no direction is possible
|
|
|
|
+ for x in range(1, self.__board_size + 1):
|
|
|
|
+ for y in range(1, self.__board_size + 1):
|
|
|
|
+ if (x == 1) and (y == 1):
|
|
|
|
+ continue
|
|
|
|
+ if self.board()[x, y]:
|
|
|
|
+ continue
|
|
|
|
+ left_full = x == 1 or self.board()[x - 1, y]
|
|
|
|
+ top_full = y == 1 or self.board()[x, y - 1]
|
|
|
|
+ if not self.possible(x, y, 'down') and not self.possible(x, y, 'right') and left_full and top_full:
|
|
|
|
+ return x, y
|
|
|
|
+
|
|
|
|
+ # try to find a move where only one direction is possible
|
|
|
|
+ for x in range(1, self.__board_size - 1):
|
|
|
|
+ for y in range(1, self.__board_size - 1):
|
|
|
|
+ if (x == 1) and (y == 1):
|
|
|
|
+ continue
|
|
|
|
+ if self.board()[x, y]:
|
|
|
|
+ continue
|
|
|
|
+ left_full = x == 1 or self.board()[x - 1, y]
|
|
|
|
+ top_full = y == 1 or self.board()[x, y - 1]
|
|
|
|
+ if not self.possible(x, y, 'down') and left_full and top_full:
|
|
|
|
+ return x, y
|
|
|
|
+ if not self.possible(x, y, 'right') and left_full and top_full:
|
|
|
|
+ return x, y
|
|
|
|
+
|
|
|
|
+ # just take the next move in this row
|
|
|
|
+ for y in range(1, self.__board_size + 1):
|
|
|
|
+ for x in range(1, self.__board_size + 1):
|
|
|
|
+ if (x == 1) and (y == 1):
|
|
|
|
+ continue
|
|
|
|
+ if self.board()[x, y]:
|
|
|
|
+ continue
|
|
|
|
+ return x, y
|
|
|
|
+
|
|
|
|
+ return None, None
|