Пример #1
0
    async def make_move(self, game: Game, move: str) -> Game:
        """
        Makes a move in given game, if valid

        :param game: Game to be played
        :type game: Game
        :param move: Chess move in SAN or UCI notation
        :type move: str
        :return: Updated game
        :rtype: Game
        """
        chess_move = self._parse_str_move(game, move)
        if not chess_move:
            raise InvalidMove()
        game.board.push(chess_move)

        game.current_player = game.player1 if game.current_player == game.player2 else game.player2

        if self.is_pve_game(game) and not self.is_game_over(game):
            stockfish_result = await self._play_move(game)
            game.board.push(stockfish_result.move)
            game.current_player = game.player1

        if self.is_game_over(game):
            game.result = game.board.result(claim_draw=True)
            self.games.remove(game)

        await game.save()
        return game
    def test_is_pve_game_pvp_game(self):
        game = Game()
        game.cpu_level = None

        chess_bot = Chess()
        chess_bot.stockfish_path = "valid"
        chess_bot.games.append(game)

        self.assertFalse(chess_bot.is_pve_game(game))
    def test_is_pve_game_stockfish_disabled(self):
        game = Game()
        game.cpu_level = 0

        chess_bot = Chess()
        chess_bot.stockfish_path = ""
        chess_bot.games.append(game)

        self.assertFalse(chess_bot.is_pve_game(game))
    def test_new_game_game_already_started(self):
        game = Game()
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)

        chess_bot = Chess()
        chess_bot.games.append(game)

        with self.assertRaises(GameAlreadyInProgress):
            chess_bot.new_game(game.player1, game.player2)

        self.assertEqual(len(chess_bot.games), 1)
 def _build_puzzle_bot_with_one_puzzle(self):
     puzzle_bot = Puzzle()
     game = Game()
     game.board = Board("8/7p/4p1p1/1p5k/8/PB4PK/2P2q1P/4R3 w - - 0 37")
     game.board.push_san("g4+")
     puzzle_id = "557ca53de13823b83a98100e"
     puzzle_bot.puzzles = {
         puzzle_id: {
             "game": game,
             "correct_sequence": ["Kg5", "Rf1", "Qxf1+", "Kg3", "Qf4+"]
         }
     }
     return puzzle_bot, puzzle_id
    def test_make_move_legal_move_pve(self):
        board = chess.Board(
            'rn2kb1r/pp1qpppp/2ppbn2/1B6/3PP3/2N2N2/PPP2PPP/R1BQK2R w KQkq - 0 6'
        )
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1
        game.cpu_level = 0

        chess_bot = Chess()
        chess_bot.games.append(game)
        chess_bot.stockfish_limit['time'] = 1
        result = asyncio.run(chess_bot.make_move(game, 'b5d3'))

        self.assertIsInstance(result, Game)
        if chess_bot.is_stockfish_enabled():
            self.assertEqual(len(result.board.move_stack), 2)
            self.assertEqual(result.current_player, game.player1)

        updated_chess_game = self.db_session.query(ChessGame).filter_by(
            player1_id=result.player1.id).first()
        updated_game_from_db = Game.from_chess_game_model(updated_chess_game)
        self.assertEqual(updated_game_from_db.board.move_stack,
                         result.board.move_stack)
    def test_get_all_boards_png_one_game(self):
        chess_bot = Chess()

        board1 = chess.Board()
        board1.push_san("e4")
        board1.push_san("e5")
        board1.push_san("Bc4")
        game1 = Game()
        game1.color_schema = "green"
        game1.board = board1
        chess_bot.games.append(game1)

        image_bytesio = asyncio.run(chess_bot.get_all_boards_png())

        with open(
                os.path.join('tests', 'support',
                             'get_all_boards_png_one_game.png'), 'rb') as f:
            self.assertEqual(image_bytesio.read(), f.read())
    def test_build_animated_sequence_gif_invalid_move_in_sequence(self):
        board = chess.Board()
        board.push_san('e4')
        board.push_san('c5')
        board.push_san('Nc3')
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)

        chess_bot = Chess()
        chess_bot.games.append(game)
        sequence = ['Nf3', 'd6', 'd4', 'Rxa8', 'Nxd4', 'Nf6', 'Nc3', 'a6']

        result = asyncio.run(
            chess_bot.build_animated_sequence_gif(game, 2, sequence))

        self.assertIsNone(result)
Пример #9
0
    def build_animated_sequence_gif(self, game: Game, game_move: int,
                                    sequence: list) -> BytesIO:
        """
        Builds an animated GIF for illustrating a given game's possible variation

        :param game: Game to be used for variation
        :type game: Game
        :param game_move: Number of game's move for the start of variation
        :type game_move: int
        :param sequence: Variation's sequence of move
        :type sequence: str[]
        :return: Animated gif's bytesIO
        :rtype: BytesIO
        """
        game_moves = game.board.move_stack
        new_board = chess.Board()
        new_game = Game()
        new_game.board = new_board
        new_game.color_schema = game.color_schema
        for move in game_moves[:game_move]:
            new_board.push(move)

        first_gif_frame = Image.open(self.build_png_board(new_game))
        gif_frames = []
        for variant_move in sequence:
            variant_chess_move = self._parse_str_move(new_game, variant_move)
            if not variant_chess_move:
                return
            new_board.push(variant_chess_move)
            gif_frame = Image.open(self.build_png_board(new_game))
            gif_frames.append(gif_frame)

        bytesio = BytesIO()
        first_gif_frame.save(bytesio,
                             format='gif',
                             save_all=True,
                             append_images=gif_frames,
                             duration=1000,
                             loop=0)
        bytesio.seek(0)
        return bytesio
    def test_build_animated_sequence_gif_valid_params(self):
        board = chess.Board()
        board.push_san('e4')
        board.push_san('c5')
        board.push_san('Nc3')
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)

        chess_bot = Chess()
        chess_bot.games.append(game)
        sequence = ['Nf3', 'd6', 'd4', 'cxd4', 'Nxd4', 'Nf6', 'Nc3', 'a6']

        result = asyncio.run(
            chess_bot.build_animated_sequence_gif(game, 2, sequence))

        with open(
                os.path.join('tests', 'support',
                             'build_animated_sequence_gif.gif'), 'rb') as f:
            self.assertEqual(result.getvalue(), f.read())
    def test_make_move_draw_by_threefold_repetition(self):
        board = chess.Board()
        board.push_san("Nf3")
        board.push_san("Nf6")
        board.push_san("Ng1")
        board.push_san("Ng8")
        board.push_san("Nf3")
        board.push_san("Nf6")
        board.push_san("Ng1")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player2

        chess_bot = Chess()
        chess_bot.games.append(game)

        self.assertFalse(chess_bot.is_game_over(game))

        result = asyncio.run(chess_bot.make_move(game, 'Ng8'))

        self.assertIsInstance(result, Game)
        self.assertEqual(len(chess_bot.games), 0)
        self.assertEqual(
            self.db_session.query(ChessGame).filter_by(result=0).count(), 1)
        self.assertTrue(chess_bot.is_game_over(result))
        self.assertEqual(result.result, '1/2-1/2')
Пример #12
0
    def _is_last_move_blunder(self, game: Game, analysis: dict):
        mate_score = 100000
        last_eval = game.last_eval
        game.last_eval = analysis["score"].white()

        if last_eval.__class__ != game.last_eval.__class__:
            return True

        last_eval_score = last_eval.score(mate_score=mate_score)
        current_eval_score = game.last_eval.score(mate_score=mate_score)
        return abs(
            self._evaluation_normalizer(current_eval_score) -
            self._evaluation_normalizer(last_eval_score)) > 2
Пример #13
0
    def build_puzzle(self, puzzle_dict):
        try:
            puzzle_id = puzzle_dict["data"]["id"]
            first_move = puzzle_dict["data"]["blunderMove"]
            fen = puzzle_dict["data"]["fenBefore"]
            correct_sequence = puzzle_dict["data"]["forcedLine"]

            board = Board(fen)
            board.push_san(first_move)
            game = Game()
            game.board = board
            game.color_schema = "default"
            self.puzzles[puzzle_id] = {
                "id": puzzle_id,
                "game": game,
                "correct_sequence": correct_sequence
            }
            return self.puzzles[puzzle_id]
        except KeyError:
            return {"error": "Missing required keys"}
        except:
            return {"error": "Invalid FEN or move"}
Пример #14
0
    async def resign(self, game: Game) -> Game:
        """
        Resigns the given game. Only the next player to move can resign their game.

        :param game: Game to be resigned
        :type game: Game
        :return: Updated game
        :rtype: Game
        """
        board_png_bytes = self.build_png_board(game)
        game.result = '0-1' if game.board.turn == chess.WHITE else '1-0'
        await game.save()
        self.games.remove(game)
        return game
Пример #15
0
    def new_game(self, user1, user2, color_schema=None, cpu_level=None):
        player1, player2 = convert_users_to_players(user1, user2)
        current_players_pairs = map(lambda x: [x.player1, x.player2],
                                    self.games)
        given_players_pairs = [player1, player2]

        if given_players_pairs in current_players_pairs:
            raise GameAlreadyInProgress()

        game = Game()
        game.board = chess.Board()
        game.player1 = player1
        game.player2 = player2
        game.current_player = player1
        game.result = game.board.result()
        game.color_schema = color_schema
        game.cpu_level = cpu_level

        self.games.append(game)
        return game
Пример #16
0
    async def load_games(self):
        """
        Load all ongoing games from database

        :return: List of ongoing games
        :rtype: List[Games]
        """
        try:
            chess_games_models = await ChessGameModel.get_all_ongoing_games()
            self.games = [
                Game.from_chess_game_model(x) for x in chess_games_models
            ]
        except Exception as e:
            logging.warning(e, exc_info=True)
        finally:
            return self.games
Пример #17
0
    async def get_game_by_id(self, chess_game_id: str) -> Game:
        """
        Fetches from database chess game by given chess game id

        :param chess_game_id: Chess game's UUID
        :type chess_game_id: str
        :return: Chess game
        :rtype: Game
        """
        try:
            chess_game_model = await ChessGameModel.get(chess_game_id,
                                                        preload_players=True)
            if not chess_game_model:
                return None
        except:
            return None
        return Game.from_chess_game_model(chess_game_model)
    def test_get_game_by_id_game_invalid_uuid(self):
        warnings.simplefilter('ignore')
        board1 = chess.Board()
        board1.push_san("e4")
        board1.push_san("e5")
        game1 = Game()
        game1.board = board1
        game1.player1 = FakeDiscordUser(id=1)
        game1.player2 = FakeDiscordUser(id=2)
        asyncio.run(game1.save())

        result = asyncio.run(Chess().get_game_by_id("invalid_id"))

        self.assertIsNone(result)
    def test_get_game_by_id_game_exists(self):
        warnings.simplefilter('ignore')
        board1 = chess.Board()
        board1.push_san("e4")
        board1.push_san("e5")
        game1 = Game()
        game1.board = board1
        game1.player1 = FakeDiscordUser(id=1)
        game1.player2 = FakeDiscordUser(id=2)
        asyncio.run(game1.save())

        result = asyncio.run(Chess().get_game_by_id(game1.id))

        self.assertEqual(result, game1)
    def test_eval_last_move_no_blunder_no_mate(self):
        board = chess.Board()
        board.push_san("g4")
        board.push_san("e5")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1
        game.last_eval = Cp(0)

        chess_bot = Chess()
        chess_bot.games.append(game)
        chess_bot.stockfish_limit["time"] = 2

        result = asyncio.run(chess_bot.eval_last_move(game))

        self.assertFalse(result["blunder"])
        self.assertIsNone(result["mate_in"])
    def test_generate_pgn(self):
        board = chess.Board()
        board.push_san("g4")
        board.push_san("e5")
        board.push_san("f4")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1, name='Player1')
        game.player2 = FakeDiscordUser(id=2, name='Player2')
        game.current_player = game.player1
        game.result = game.board.result()

        chess_bot = Chess()
        chess_bot.games.append(game)
        result = chess_bot.generate_pgn(game)

        self.assertIn('[White "Player1"]', result)
        self.assertIn('[Black "Player2"]', result)
        self.assertIn('1. g4 e5 2. f4 *', result)
    def test_find_current_game_in_players_turn_no_ambiguity(self):
        board = chess.Board()
        board.push_san("e4")
        board.push_san("e5")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1

        chess_bot = Chess()
        chess_bot.games.append(game)

        result = chess_bot.find_current_game(user=game.player1)

        self.assertEqual(result, game)
    def test_eval_last_move_no_blunder_mate_in_two(self):
        board = chess.Board(
            "Q2r4/1p1k4/1P3ppp/1Kp1r3/4p2b/1B3P2/2P2q2/8 w - - 6 44")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1
        game.last_eval = Mate(2)

        chess_bot = Chess()
        chess_bot.games.append(game)

        result = asyncio.run(chess_bot.eval_last_move(game))

        self.assertFalse(result["blunder"])
        if chess_bot.is_stockfish_enabled():
            self.assertEqual(result["mate_in"], 2)
        else:
            self.assertIsNone(result["mate_in"])
    def test_eval_last_move_lost_position_blunders_mate(self):
        board = chess.Board(
            "Q1kr4/1p6/1P3ppp/1Kp1r3/4p2b/1B3P2/2P2q2/8 b - - 5 43")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1
        game.last_eval = Cp(1000)

        chess_bot = Chess()
        chess_bot.games.append(game)

        result = asyncio.run(chess_bot.eval_last_move(game))

        if chess_bot.is_stockfish_enabled():
            self.assertTrue(result["blunder"])
            self.assertEqual(result["mate_in"], -2)
        else:
            self.assertFalse(result["blunder"])
            self.assertIsNone(result["mate_in"])
    def test_make_move_illegal_move_in_players_turn(self):
        board = chess.Board()
        board.push_san("e4")
        board.push_san("e5")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1

        chess_bot = Chess()
        chess_bot.games.append(game)

        with self.assertRaises(InvalidMove) as e:
            asyncio.run(chess_bot.make_move(game, 'invalid'))

        self.assertEqual(len(game.board.move_stack), 2)
        self.assertEqual(game.current_player, game.player1)
    def test_resign_game_found(self):
        board = chess.Board()
        board.push_san("e4")
        board.push_san("e5")
        board.push_san("Nf3")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1

        chess_bot = Chess()
        chess_bot.games.append(game)
        result = asyncio.run(chess_bot.resign(game))

        self.assertEqual(len(chess_bot.games), 0)
        self.assertEqual(
            self.db_session.query(ChessGame).filter_by(result=1).count(), 1)
    def test_eval_last_move_last_move_blunder_mate_in_one(self):
        board = chess.Board()
        board.push_san("g4")
        board.push_san("e5")
        board.push_san("f4")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1
        game.last_eval = Cp(0)

        chess_bot = Chess()
        chess_bot.games.append(game)
        result = asyncio.run(chess_bot.eval_last_move(game))

        if chess_bot.is_stockfish_enabled():
            self.assertTrue(result["blunder"])
            self.assertEqual(result["mate_in"], 1)
        else:
            self.assertFalse(result["blunder"])
            self.assertIsNone(result["mate_in"])
    def test_make_move_legal_san_move_in_players_turn(self):
        board = chess.Board()
        board.push_san("e4")
        board.push_san("e5")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1

        chess_bot = Chess()
        chess_bot.games.append(game)
        result = asyncio.run(chess_bot.make_move(game, 'Nf3'))

        self.assertIsInstance(result, Game)
        self.assertEqual(len(result.board.move_stack), 3)
        self.assertEqual(result.current_player, game.player2)

        updated_chess_game = self.db_session.query(ChessGame).filter_by(
            player1_id=result.player1.id).first()
        updated_game_from_db = Game.from_chess_game_model(updated_chess_game)
        self.assertEqual(updated_game_from_db.board.move_stack,
                         result.board.move_stack)
    def test_make_move_finish_game_pve_player_wins(self):
        board = chess.Board()
        board.push_san("e4")
        board.push_san("g5")
        board.push_san("d4")
        board.push_san("f5")
        game = Game()
        game.board = board
        game.player1 = FakeDiscordUser(id=1)
        game.player2 = FakeDiscordUser(id=2)
        game.current_player = game.player1
        game.cpu_level = 20

        chess_bot = Chess()
        chess_bot.games.append(game)
        result = asyncio.run(chess_bot.make_move(game, 'Qh5'))

        self.assertIsInstance(result, Game)
        self.assertEqual(len(chess_bot.games), 0)
        self.assertEqual(
            self.db_session.query(ChessGame).filter_by(result=1).count(), 1)
        self.assertTrue(chess_bot.is_game_over(result))
        self.assertEqual(result.result, '1-0')
    def test_get_all_boards_png_no_twelve_games_second_page(self):
        chess_bot = Chess()

        board1 = chess.Board()
        board1.push_san("e4")
        board1.push_san("e5")
        game1 = Game()
        game1.color_schema = "blue"
        game1.board = board1
        chess_bot.games.append(game1)

        board2 = chess.Board()
        board2.push_san("Nf3")
        board2.push_san("d6")
        game2 = Game()
        game2.color_schema = "wood"
        game2.board = board2
        chess_bot.games.append(game2)

        board3 = chess.Board()
        board3.push_san("Nf3")
        game3 = Game()
        game3.color_schema = "green"
        game3.board = board3
        chess_bot.games.append(game3)

        for i in range(3):
            chess_bot.games.append(game1)
            chess_bot.games.append(game2)
            chess_bot.games.append(game3)

        image_bytesio = asyncio.run(chess_bot.get_all_boards_png(page=2),
                                    debug=True)

        with open(
                os.path.join(
                    'tests', 'support',
                    'get_all_boards_png_twelve_games_second_page.png'),
                'rb') as f:
            self.assertEqual(image_bytesio.read(), f.read())