def update_game(self, request: dict) -> dict: """ Update a certain game from source to target, and promotion role if occurred. :param request: A dict including session id, source coordinate, target coordinate,current logged in user and promotion role. :return: A dict including info about update validation, whether being checked, game status and turn color. """ session_id = request["session_id"] username = request["user"] if self.sessions[session_id][1] == username: src = request["src"] tar = request["tar"] role = request["role"] src_coord = Utility.decode(src) tar_coord = Utility.decode(tar) game = self.sessions[session_id][0] valid = game.update(src_coord, tar_coord, role) if valid: self.update_history(session_id) self.update_current(session_id) is_being_checked = game.is_being_checked() status = game.check_game_status() turn = game.get_turn() # AI's turns if status == "Continue" and valid and self.modes[ session_id] != "pvp": if self.modes[session_id] == 'easy': ai = SimpleAI(game) elif self.modes[session_id] == 'advanced': ai = AdvancedAI(game) # noinspection PyUnboundLocalVariable src_row, src_col, tar_row, tar_col = ai.get_next_move() src = (src_row, src_col) tar = (tar_row, tar_col) valid = game.update(src, tar, "Queen") if valid: self.update_history(session_id) self.update_current(session_id) is_being_checked = game.is_being_checked() status = game.check_game_status() turn = game.get_turn() if status in ["WhiteLoss", "BlackLoss" ] and valid and self.modes[session_id] != "pvp": self.update_score(session_id, username, status) return { "valid": valid, "is_being_checked": is_being_checked, "game_status": status, "turn": turn, "validSession": True } return {"valid": False, "validSession": False}
def update(self, src: tuple, tar: tuple, role: str, is_ai=False) -> bool: """ Game parameter information will be updated after a valid movement and return True. If the movement is not valid, nothing will be updated, and return False. :param src: the position of the piece move from :param tar: the position of the piece move to :param role: promotion role while the pawn reaches the eighth rank to be replaced by. :param is_ai: promotion role while the pawn reaches the eighth rank to be replaced by. :return: A boolean value which show the movement is valid or not. If the movement is valid, all game parameters are updated, otherwise no change. """ # If the game status is not 'Continue", then return False and nothing updated. if not is_ai and self.check_game_status() != "Continue": return False # If not in for the source piece turn, or the target movement position is not in the source piece movement list, # then then return False and nothing updated. src_row, src_col = src tar_row, tar_col = tar src_piece = self.board[src_row][src_col] if not is_ai and self.board[src_row][src_col].color != self.turn: return False if not is_ai and tar not in src_piece.get_checked_moves()["moves"]: return False # Implement en_passant self.update_en_passant(src, tar) # Reset en_passant notation self.en_passant_target_notation = '-' # Update Rook and King firstMove. if type(src_piece) in [King, Rook]: src_piece.firstMove = False # Update castling notation self.castling_notation = self.get_castling_notation() # Update en_passant target square notation self.update_en_passant_notation(src, tar) # Implement Castle self.update_castle(src, tar) # Update movement clock counts self.update_movement_clock(src, tar) # If the Pawn reached the button, then it would be promoted as promotion piece which passed through argument. self.update_promotion(src, tar, role) self.movement = { "src": Utility.encode(src_row, src_col), "tar": Utility.encode(tar_row, tar_col) } self.fen = self.get_fen() self.history.append({"fen": self.fen, "movement": self.movement}) return True
def get_next_move(self) -> tuple: """ AI will brute force all valid moves and choose the one with the biggest value from get_value() :return: A four tuple indicates AI's move """ board = copy.deepcopy(self.game.board) src_row, src_col, tar_row, tar_col = 0, 0, 0, 0 max_score = -10000 for row in range(8): for col in range(8): moves = self.game.get_checked_moves((row, col))["moves"] for coordinate in moves: r1, c1 = Utility.decode(coordinate) src_piece = board[row][col] tar_piece = board[r1][c1] board[r1][c1] = src_piece board[row][col] = self.game.empty_cell ret = self.get_value(board) if ret > max_score: src_row, src_col, tar_row, tar_col = row, col, r1, c1 max_score = ret board[row][col] = src_piece board[r1][c1] = tar_piece return src_row, src_col, tar_row, tar_col
def test_replay_a_session(self): db_url = os.path.join('sqlite:///' + os.getcwd(), 'game.db') api = ChessAPI(db_url=db_url) session_id = api.create_game("hu4396", "pvp")["session_id"] api.update_game({"session_id": session_id, "src": Utility.encode(1, 0), "tar": Utility.encode(3, 0), "role": "Pawn", "user": "******"}) ret = api.replay("hu4396", session_id, 1) self.assertEqual(ret["history"], {'src': 'a2', 'tar': 'a4'})
def test_get_checked_moves(self): coord = Utility.encode(1, 1) db_url = os.path.join('sqlite:///' + os.getcwd(), 'game.db') api = ChessAPI(db_url=db_url) session_id = api.create_game("hu4396", "pvp")["session_id"] checked = api.get_checked_moves({"session_id": session_id, "coordinate": coord}, "hu4396") self.assertEqual(checked, {'moves': ['b3', 'b4'], 'valid': True}) ChessAPITestCase.remove_db_file()
def update_en_passant_notation(self, src: tuple, tar: tuple) -> None: """ Update en_passant target square notation :param: src: Coordinate, tar: Coordinate :return: None """ src_row, src_col = src src_piece = self.board[src_row][src_col] tar_row, tar_col = tar if type(src_piece) == Pawn: step = tar_row - src_row if abs(step) == 2 and src_col == tar_col: if step > 0: self.en_passant_target_notation = Utility.encode( tar_row - 1, tar_col) else: self.en_passant_target_notation = Utility.encode( tar_row + 1, tar_col) return None
def test_update_game(self): db_url = os.path.join('sqlite:///' + os.getcwd(), 'game.db') api = ChessAPI(db_url=db_url) conn = api.engine.connect() session_id = api.create_game("hu4396", "pvp")["session_id"] api.update_game({"session_id": session_id, "src": Utility.encode(1, 0), "tar": Utility.encode(3, 0), "role": "Pawn", "user": "******"}) ret = conn.execute(api.history.select().where(api.history.c.session_id == session_id)) updated_src = ret.fetchall()[0]["src"] conn.close() self.assertEqual("a2", updated_src) ChessAPITestCase.remove_db_file()
def get_checked_moves(self, coordinate: tuple) -> dict: """ While checkmate, each current turn piece available movements which can release the checkmate. :param coordinate: piece position :return: A dictionary with key "moves", and value is a array list which includes the available movements while checkmate. """ row, col = coordinate piece = self.board[row][col] if piece.color != self.turn or self.check_game_status() != "Continue": return {"moves": []} ret = piece.get_checked_moves()["moves"] ret = Utility.encode_list(ret) return {"moves": ret}
def get_checked_moves(self, request: dict, username: str) -> dict: """ Get all valid moves which won't let you be checked in certain game for a coordinate. :param request: A dict including session id and piece coordinate :param username: logged in user :return: A dict which including all valid moves which won't let you be checked in certain game for a coordinate. """ session_id = request["session_id"] if session_id in self.sessions and self.sessions[session_id][ 1] == username: coordinate = request["coordinate"] coordinate = Utility.decode(coordinate) game = self.sessions[session_id][0] ret = game.get_checked_moves(coordinate) ret["valid"] = True return ret return {"valid": False}
def update_en_passant(self, src: tuple, tar: tuple) -> None: """ Implement en_passant :param: src: Coordinate, tar: Coordinate :return: None """ src_row, src_col = src src_piece = self.board[src_row][src_col] tar_row, tar_col = tar tar_piece = self.board[tar_row][tar_col] if type( src_piece ) == Pawn and src_col != tar_col and tar_piece == self.empty_cell: last_pawn_row, last_pawn_col = Utility.decode( self.history[-1]["movement"]["tar"]) self.board[last_pawn_row][last_pawn_col] = self.empty_cell return None
def test_encode(self): self.assertEqual(Utility.encode(1, 2), "c2") self.assertEqual(Utility.encode(2, 2), "c3") self.assertEqual(Utility.encode(5, 3), "d6") self.assertEqual(Utility.encode(0, 0), "a1") self.assertEqual(Utility.encode(7, 7), "h8")
def test_encode_list(self): input_list = [(1, 3), (3, 7), (6, 6)] actual = Utility.encode_list(input_list) expected = ["d2", "h4", "g7"] self.assertEqual(actual, expected)
def test_decode(self): self.assertEqual(Utility.decode("c2"), (1, 2)) self.assertEqual(Utility.decode("c3"), (2, 2)) self.assertEqual(Utility.decode("d6"), (5, 3)) self.assertEqual(Utility.decode("a1"), (0, 0)) self.assertEqual(Utility.decode("h8"), (7, 7))