chess_state.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. def empty_board(board_size):
  2. return {(x, y): 0 for x in range(1, board_size + 1) for y in range(1, board_size + 1)}
  3. def empty_history():
  4. return []
  5. class IllegalMoveError(ValueError):
  6. pass
  7. class ChessState:
  8. def __init__(self, board=None, history=empty_history(), predecessor=None, board_size=8):
  9. self.__board_size = board_size
  10. self.__history = history
  11. if board is not None:
  12. self.__board = board
  13. else:
  14. self.__board = empty_board(board_size)
  15. self.__predecessor = predecessor
  16. self._bool_board_cache = None
  17. self._bool_board_mirrored_cache = None
  18. def put(self, x, y, direction):
  19. positions = self.move_positions(x, y, direction)
  20. if not self.possible(x, y, direction):
  21. raise IllegalMoveError
  22. board = self.__board.copy()
  23. for pos in positions:
  24. board[pos] = len(self.__history) + 1
  25. return ChessState(board=board,
  26. history=self.__history + [positions],
  27. board_size=self.__board_size,
  28. predecessor=self)
  29. def undo(self):
  30. return self.__predecessor
  31. def move_positions(self, x, y, direction):
  32. if direction not in ['down', 'right']:
  33. raise ValueError
  34. if direction == 'down':
  35. mps = [(x, y), (x, y + 1), (x, y + 2)]
  36. else: # right
  37. mps = [(x, y), (x + 1, y), (x + 2, y)]
  38. for pos in mps:
  39. if pos not in self.__board:
  40. raise IndexError
  41. return mps
  42. def possible(self, x, y, direction):
  43. try:
  44. for pos in self.move_positions(x, y, direction):
  45. if self.__board[pos]:
  46. return False
  47. except IndexError:
  48. return False
  49. return True
  50. def board(self):
  51. return self.__board.copy()
  52. def history(self):
  53. return self.__history.copy()
  54. def predecessor(self):
  55. return self.__predecessor.copy()
  56. def board_size(self):
  57. return self.__board_size
  58. def board_label(self):
  59. s = ''
  60. for x in range(1, self.__board_size + 1):
  61. s += '{'
  62. for y in range(1, self.__board_size + 1):
  63. if self.__board[(x, y)]:
  64. s += str(self.__board[(x, y)])
  65. if y < self.__board_size:
  66. s += '|'
  67. s += '}'
  68. if x < self.__board_size:
  69. s += '|'
  70. return s
  71. def bool_board(self):
  72. if self._bool_board_cache is None:
  73. self._bool_board_cache = {x: self.__board[x] != 0 for x in self.__board}
  74. return self._bool_board_cache
  75. def bool_board_mirrored(self):
  76. if self._bool_board_mirrored_cache is None:
  77. self._bool_board_mirrored_cache = [
  78. {self.bool_board()[x, self.__board_size + 1 - y] for (x, y) in self.bool_board()},
  79. {self.bool_board()[self.__board_size + 1 - x, y] for (x, y) in self.bool_board()},
  80. {self.bool_board()[self.__board_size + 1 - x, self.__board_size + 1 - y] for (x, y) in self.bool_board()},
  81. ]
  82. return self._bool_board_mirrored_cache
  83. def symmetric_to(self, other):
  84. if self.bool_board() == other.bool_board():
  85. return True
  86. # # no gain
  87. # for board in other.bool_board_mirrored():
  88. # if self.bool_board() == board:
  89. # return True
  90. return False
  91. def next_move(self):
  92. # try to find a move where no direction is possible
  93. for x in range(1, self.__board_size + 1):
  94. for y in range(1, self.__board_size + 1):
  95. if (x == 1) and (y == 1):
  96. continue
  97. if self.board()[x, y]:
  98. continue
  99. left_full = x == 1 or self.board()[x - 1, y]
  100. top_full = y == 1 or self.board()[x, y - 1]
  101. if not self.possible(x, y, 'down') and not self.possible(x, y, 'right') and left_full and top_full:
  102. return x, y
  103. # try to find a move where only one direction is possible
  104. for x in range(1, self.__board_size - 1):
  105. for y in range(1, self.__board_size - 1):
  106. if (x == 1) and (y == 1):
  107. continue
  108. if self.board()[x, y]:
  109. continue
  110. left_full = x == 1 or self.board()[x - 1, y]
  111. top_full = y == 1 or self.board()[x, y - 1]
  112. if not self.possible(x, y, 'down') and left_full and top_full:
  113. return x, y
  114. if not self.possible(x, y, 'right') and left_full and top_full:
  115. return x, y
  116. # just take the next move in this row
  117. for y in range(1, self.__board_size + 1):
  118. for x in range(1, self.__board_size + 1):
  119. if (x == 1) and (y == 1):
  120. continue
  121. if self.board()[x, y]:
  122. continue
  123. return x, y
  124. return None, None