From 3c8124e26898e58ea835194a255da0c04b2ecfac Mon Sep 17 00:00:00 2001 From: InigoGutierrez Date: Wed, 10 Feb 2021 19:25:34 +0100 Subject: logic: Reorganized move evaluation. GameBoard able to know if a move is valid, given the previous boards. GameMove no longer stores player, it gets it from board's last move. --- imago/gameLogic/gameBoard.py | 19 +++++++++++++++++++ imago/gameLogic/gameMove.py | 19 ++++++++++++++++--- imago/gameLogic/gameState.py | 36 ++++++++---------------------------- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/imago/gameLogic/gameBoard.py b/imago/gameLogic/gameBoard.py index 327728b..c6a6007 100644 --- a/imago/gameLogic/gameBoard.py +++ b/imago/gameLogic/gameBoard.py @@ -31,6 +31,12 @@ class GameBoard: be the same for all the rows.""" return len(self.board[0]) + def getLastPlayer(self): + """Returns the player who placed the last stone.""" + if self.lastStone is None: + return Player.EMPTY + return self.board[self.lastStone[0]][self.lastStone[1]] + def getDeepCopy(self): """Returns a copy GameBoard.""" newBoard = GameBoard(self.getBoardHeight(), self.getBoardWidth()) @@ -124,6 +130,7 @@ class GameBoard: """ self.board[row][col] = player + self.lastStone = [row, col] captured = set() @@ -219,6 +226,18 @@ class GameBoard: self.board[row][col] = Player.EMPTY return illegal + def isPlayable(self, row, col, player, prevBoards): + """Determines if a move is playable.""" + if not self.isMoveInBoardBounds(row, col): + return False, "Move outside board bounds." + if not self.isCellEmpty(row, col): + return False, "Vertex is not empty." + if self.isMoveSuicidal(row, col, player): + return False, "Move is suicidal." + if self.isMoveKoIllegal(row, col, player, prevBoards): + return False, "Illegal by ko rule." + return True, "" + def equals(self, otherBoard): """Returns true if this board is equal to another board. Only takes into account dimensions and placed stones. diff --git a/imago/gameLogic/gameMove.py b/imago/gameLogic/gameMove.py index b2db34f..90ec4bf 100644 --- a/imago/gameLogic/gameMove.py +++ b/imago/gameLogic/gameMove.py @@ -8,7 +8,6 @@ class GameMove: removed stones.""" def __init__(self, player, board): - self.player = player self.board = board self.nextMoves = [] self.previousMove = None @@ -21,6 +20,10 @@ class GameMove: """Returns the column of the vertex the move was played on.""" return self.board.lastStone[1] + def getLastPlayer(self): + """Returns the player who placed the last stone.""" + return self.board.getLastPlayer() + def getThisAndPrevBoards(self): """Returns an array with all the boards of this and previous moves.""" prevBoards = [] @@ -30,14 +33,24 @@ class GameMove: checkedMove = checkedMove.previousMove return prevBoards + def getPlayableVertices(self): + """Returns a set with the playable vertices.""" + playables = set() + player = Player.otherPlayer(self.getLastPlayer()) + prevBoards = self.getThisAndPrevBoards() + for row in range(self.board.getBoardHeight()): + for col in range(self.board.getBoardWidth()): + if self.board.isPlayable(row, col, player, prevBoards): + playables.add((row, col)) + def addMove(self, row, col): """Adds a move to the next moves list creating its board from this move's board plus a new stone at the specified row and column. """ - if self.player == Player.EMPTY: + if self.getLastPlayer() == Player.EMPTY: player = Player.BLACK else: - player = Player.otherPlayer(self.player) + player = Player.otherPlayer(self.getLastPlayer()) return self.addMoveForPlayer(row, col, player) def addMoveForPlayer(self, row, col, player): diff --git a/imago/gameLogic/gameState.py b/imago/gameLogic/gameState.py index cc65c75..e4dd629 100644 --- a/imago/gameLogic/gameState.py +++ b/imago/gameLogic/gameState.py @@ -19,9 +19,9 @@ class GameState: """Gets the player who should make the next move.""" if self.lastMove is None: return Player.BLACK - if self.lastMove.player is Player.EMPTY: + if self.lastMove.getLastPlayer() is Player.EMPTY: return Player.BLACK - return Player.otherPlayer(self.lastMove.player) + return Player.otherPlayer(self.lastMove.getLastPlayer()) def getPlayerCode(self): """Gets a string representation of the current player.""" @@ -40,38 +40,18 @@ class GameState: def playMoveForPlayer(self, row, col, player): """Execute a move on the board for the given player.""" - # Check valid move - if not self.prevalidateMove(row, col): - print("Invalid move!") - return False - - # Check suicide - if self.getBoard().isMoveSuicidal(row, col, player): - print("Invalid move! (Suicide)") - return False - - # Check ko prevBoards = self.lastMove.getThisAndPrevBoards() - if self.getBoard().isMoveKoIllegal(row, col, player, prevBoards): - print("Invalid move! (Ko)") - return False - - # Move is legal - self.__addMove(player, row, col) - return True + playable, message = self.lastMove.board.isPlayable(row, col, player, prevBoards) + if playable: + self.__addMove(player, row, col) + return True + print("Invalid Move! %s" % message) + return False def undo(self): """Sets the move before the last move as the new last move.""" self.lastMove = self.lastMove.previousMove - def prevalidateMove(self, row, col): - """Returns True if move is inside bounds and cell is empty, False if not.""" - if not self.getBoard().isMoveInBoardBounds(row, col): - return False - if not self.getBoard().isCellEmpty(row, col): - return False - return True - def __addMove(self, player, row, col): # Check a last move already exists -- cgit v1.2.1