def test_king_movement_adjusted_after_moving(self): """ Move a king of each color Expected result is king cannot move left or right two squares once it has moved. :return: """ board = ChessBoard() positions = {Color.WHITE: ('e1', 'e2'), Color.BLACK: ('e8', 'e7')} expected_directions = { MoveDirection.FORWARD: 1, MoveDirection.F_RIGHT_DIAG: 1, MoveDirection.RIGHT: 1, MoveDirection.B_RIGHT_DIAG: 1, MoveDirection.BACKWARD: 1, MoveDirection.B_LEFT_DIAG: 1, MoveDirection.LEFT: 1, MoveDirection.F_LEFT_DIAG: 1 } for color in [Color.BLACK, Color.WHITE]: with self.subTest(color=color, movement=positions[color]): start_pos, end_pos = positions[color] board[start_pos] = King(color) board.move_piece(start_pos, end_pos) self.assertDictEqual(expected_directions, board[end_pos].move_directions, 'Incorrect move_directions')
def test_king_movement_adjusted_after_right_rook_moves(self): """ Move the queen side rook for white and black player Expected result is king can no longer castle queen side. :return: """ board = ChessBoard() king_positions = {Color.WHITE: 'e1', Color.BLACK: 'e8'} rook_positions = {Color.WHITE: ('h1', 'h2'), Color.BLACK: ('a8', 'a7')} expected_directions = { MoveDirection.FORWARD: 1, MoveDirection.F_RIGHT_DIAG: 1, MoveDirection.RIGHT: 1, MoveDirection.B_RIGHT_DIAG: 1, MoveDirection.BACKWARD: 1, MoveDirection.B_LEFT_DIAG: 1, MoveDirection.LEFT: 2, MoveDirection.F_LEFT_DIAG: 1 } for color in [Color.BLACK, Color.WHITE]: with self.subTest(color=color, king_pos=king_positions[color], rook_pos=rook_positions[color]): king_pos = king_positions[color] rook_start, rook_end = rook_positions[color] board[king_pos] = King(color) board[rook_start] = Rook(color) board.move_piece(rook_start, rook_end) self.assertDictEqual(expected_directions, board[king_pos].move_directions, 'Incorrect move_directions')
def test_knight_capture(self): """ Move knight to a middle square with a piece on a capture square. Expected result is position with opponent piece is in legal move list and piece is captured when knight moves to that position. :return: """ board = ChessBoard() start_position = 'd4' capture_position = 'f5' board[start_position] = Knight(Color.WHITE) board[capture_position] = Bishop(Color.BLACK) expected_possible_moves = [ 'b3', 'b5', 'c2', 'c6', 'e2', 'e6', 'f3', 'f5' ] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected move list does not match actual move list' self.assertListEqual(expected_possible_moves, possible_moves, message) # Move knight to capture a piece move_result = board.move_piece(start_position, capture_position) message = 'Knight should have captured piece on ' + capture_position + ' square' self.assertIsInstance(board[capture_position], Knight, message) # Test move result expected_move_result = { start_position: None, capture_position: Knight(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
def test_move_l_shape(self): """ Move knight in every possible direction. Expected result is that the knight no longer exist on the starting square, but on the ending square. :return: """ start_position = 'd4' end_positions = ['b3', 'b5', 'c2', 'c6', 'e2', 'e6', 'f3', 'f5'] piece_colors = [Color.WHITE, Color.BLACK] for color in piece_colors: for end_position in end_positions: with self.subTest(color=color, end_position=end_position): board = ChessBoard() board[start_position] = Knight(color) move_result = board.move_piece(start_position, end_position) self.assertIsNone( board[start_position], 'There should no longer be a piece on start position') self.assertIsInstance( board[end_position], Knight, 'There should be a piece on end position') expected_result = { start_position: None, end_position: Knight(color) } self.assertDictEqual( expected_result, move_result, 'Expected move result does not match actual')
def test_queen_capture(self): """ Move queen to position where an opponents piece is in the capture path. Expected result is opponents piece is in legal move list and piece is captured when queen moves to that position. :return: """ board = ChessBoard() start_position = 'd4' capture_position = 'e4' board[start_position] = Queen(Color.WHITE) board['d5'] = Pawn(Color.BLACK) board[capture_position] = Pawn(Color.BLACK) expected_possible_moves = [ 'a1', 'a4', 'a7', 'b2', 'b4', 'b6', 'c3', 'c4', 'c5', 'd1', 'd2', 'd3', 'd5', 'e3', 'e4', 'e5', 'f2', 'f6', 'g1', 'g7', 'h8' ] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected move list does not match actual move list' self.assertListEqual(expected_possible_moves, possible_moves, message) # Move queen to capture a piece move_result = board.move_piece(start_position, capture_position) message = 'Queen should have captured piece on ' + capture_position + ' square' self.assertIsInstance(board[capture_position], Queen, message) # Test move result expected_move_result = { start_position: None, capture_position: Queen(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
def test_king_capture(self): """ Move king to square right next to piece of opposing color with nothing backing it up. Expected result is position with opponent piece is in legal move list and piece is captured when king moves to that position. :return: """ board = ChessBoard() start_position = 'd4' capture_position = 'e4' board[start_position] = King(Color.WHITE) board[capture_position] = Pawn(Color.BLACK) expected_legal_moves = ['c3', 'c4', 'c5', 'd5', 'e3', 'e4', 'e5'] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() self.assertListEqual( expected_legal_moves, possible_moves, 'Expected move list does not match actual move list') # Move king to capture a piece move_result = board.move_piece(start_position, capture_position) message = 'King should have captured piece on ' + capture_position + ' square' self.assertIsInstance(board[capture_position], King, message) # Test move result expected_move_result = { start_position: None, capture_position: King(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
def test_bishop_capture(self): """ Move a bishop to square where there is a piece of the opposite color on capture diagonal. Expected result is position with opponent piece is in legal move list and piece is captured when bishop moves to that position. :return: """ board = ChessBoard() start_position = 'd4' capture_position = 'h8' board[start_position] = Bishop(Color.WHITE) board['c5'] = Queen(Color.BLACK) board[capture_position] = Pawn(Color.BLACK) expected_possible_moves = [ 'a1', 'b2', 'c3', 'c5', 'e3', 'e5', 'f2', 'f6', 'g1', 'g7', 'h8' ] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected move list does not match actual move list' self.assertListEqual(expected_possible_moves, possible_moves, message) # Move bishop to capture a piece move_result = board.move_piece(start_position, capture_position) message = 'Bishop should have captured piece on ' + capture_position + ' square' self.assertIsInstance(board[capture_position], Bishop, message) # Test move result expected_move_result = { start_position: None, capture_position: Bishop(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
def test_move_right(self): """ Move a piece of every type one square to the right. Expected result is that the piece no longer exist on the starting square, but on the ending square. :return: """ start_positions = ['a1', 'h8'] end_positions = ['b1', 'g8'] piece_colors = [Color.WHITE, Color.BLACK] piece_types = {Type.KING: King, Type.QUEEN: Queen, Type.ROOK: Rook} for start, end, color in zip(start_positions, end_positions, piece_colors): for t, piece_class in piece_types.items(): with self.subTest(t=t, piece_class=piece_class, start=start, end=end, color=color): board = ChessBoard() board[start] = piece_class(color) move_result = board.move_piece(start, end) self.assertIsNone( board[start], 'There should no longer be a piece on square ' + start) self.assertIsInstance( board[end], piece_class, 'There should be a piece on square ' + end) expected_result = {start: None, end: piece_class(color)} self.assertDictEqual( expected_result, move_result, 'Expected move result does not match actual')
def test_pawn_legal_moves(self): """ Move a pawn to each corner and one middle square. Expected result is that all the possible moves match the expected list. :return: """ start_positions = { Color.WHITE: { 'a1': ['a2', 'a3'], 'a8': [], 'h1': ['h2', 'h3'], 'h8': [], 'd4': ['d5', 'd6'] }, Color.BLACK: { 'a1': [], 'a8': ['a6', 'a7'], 'h1': [], 'h8': ['h6', 'h7'], 'd4': ['d2', 'd3'] } } for color, positions in start_positions.items(): for start_position, expected_moves in positions.items(): with self.subTest(color=color, start_position=start_position, expected_moves=expected_moves): board = ChessBoard() board[start_position] = Pawn(color) possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected move list does not match actual move list' self.assertListEqual(expected_moves, possible_moves, message) # Confirm pawn can only move one square after it is moved board = ChessBoard() board['a1'] = Pawn(Color.WHITE) board.move_piece('a1', 'a3') possible_moves = board.get_legal_moves('a3') expected_possible_moves = ['a4'] self.assertListEqual(expected_possible_moves, possible_moves, 'Pawn should not be able to ')
def test_can_en_passant(self): """ Test that a pawn can perform en passant move. :return: """ board = ChessBoard() # En passant from white's perspective. board['b5'] = Pawn(Color.WHITE) board['a7'] = Pawn(Color.BLACK) board.move_piece('a7', 'a5') self.assertTrue(board.can_en_passant('b5', MoveDirection.F_LEFT_DIAG), 'Pawn should be able to perform en passant') # En passant from black's perspective. board['g4'] = Pawn(Color.BLACK) board['h2'] = Pawn(Color.WHITE) board.move_piece('h2', 'h4') self.assertTrue(board.can_en_passant('g4', MoveDirection.F_LEFT_DIAG), 'Pawn should be able to perform en passant')
def test_cannot_en_passant(self): """ Test that a pawn cannot perform en passant move. :return: """ board = ChessBoard() # Confirm pawn that just moved 2 spaces can't perform enpassant. board['b5'] = Pawn(Color.WHITE) board['a7'] = Pawn(Color.BLACK) board.move_piece('a7', 'a5') self.assertFalse(board.can_en_passant('a5', MoveDirection.F_LEFT_DIAG), 'Pawn should not be able to perform en passant') # Confirm when all condition have been met but push pawn moved one square twice, en passant can't happen. board['g4'] = Pawn(Color.BLACK) board['h2'] = Pawn(Color.WHITE) board.move_piece('h2', 'h3') board.move_piece('h3', 'h4') self.assertFalse(board.can_en_passant('g4', MoveDirection.F_LEFT_DIAG), 'Pawn should be able to perform en passant')
def test_pawn_en_passant_legal_move(self): """ Place pawn on 4th or 5th rank and move pawn of opposite color immediately to left or right of first pawn. Expected result is en passant move is present in legal move list for first pawn. :return: """ piece_movements = { Color.WHITE: [[('h2', 'h5'), ('g7', 'g5')], [('a2', 'a5'), ('b7', 'b5')]], Color.BLACK: [[('a7', 'a4'), ('b2', 'b4')], [('h7', 'h4'), ('g2', 'g4')]] } expected_moves = { Color.WHITE: { 'h5': ['g6', 'h6'], 'a5': ['a6', 'b6'] }, Color.BLACK: { 'a4': ['a3', 'b3'], 'h4': ['g3', 'h3'] } } fen = Fen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -') for (c1, moves_for_color), (c2, expected_for_color) in zip( piece_movements.items(), expected_moves.items()): for piece_moves, (check_position, expected_list) in zip( moves_for_color, expected_for_color.items()): with self.subTest(piece_moves=piece_moves, check_position=check_position, expected_list=expected_list): board = ChessBoard(fen) for movements in piece_moves: start, end = movements board.move_piece(start, end) legal_moves = board.get_legal_moves(check_position) legal_moves.sort() self.assertListEqual( expected_list, legal_moves, 'En passant move should be in legal moves list')
def test_pawn_movement_adjusted_after_moving(self): """ Move a pawn of each color Expected result is pawn cannot move forward two squares once it has moved. :return: """ board = ChessBoard() positions = {Color.WHITE: ('b2', 'b3'), Color.BLACK: ('b7', 'b6')} expected_directions = { MoveDirection.FORWARD: 1, MoveDirection.F_RIGHT_DIAG: 1, MoveDirection.F_LEFT_DIAG: 1 } for color in [Color.BLACK, Color.WHITE]: with self.subTest(color=color, movement=positions[color]): start_pos, end_pos = positions[color] board[start_pos] = Pawn(color) board.move_piece(start_pos, end_pos) self.assertDictEqual(expected_directions, board[end_pos].move_directions, 'Incorrect move_directions')
def test_king_perform_castle(self): """ Perform castle to left and right with black king and white king. Expected result is king is moved two places to the left or right and the rook in that direction is moved on the other side of the king. :return: """ castle_expected_result = { Color.WHITE: [{ 'king_move': ('e1', 'c1'), 'rook_move': ('a1', 'd1') }, { 'king_move': ('e1', 'g1'), 'rook_move': ('h1', 'f1') }], Color.BLACK: [{ 'king_move': ('e8', 'c8'), 'rook_move': ('a8', 'd8') }, { 'king_move': ('e8', 'g8'), 'rook_move': ('h8', 'f8') }] } for color, left_right_castle in castle_expected_result.items(): for castle_info in left_right_castle: with self.subTest(color=color, castle_info=castle_info): board = ChessBoard() king_start, king_end = castle_info['king_move'] rook_start, rook_end = castle_info['rook_move'] board[king_start] = King(color) board[rook_start] = Rook(color) move_result = board.move_piece(king_start, king_end) self.assertEqual(Type.KING, board[king_end].type, 'King should have moved two spaces') self.assertEqual(Type.ROOK, board[rook_end].type, 'Rook should be on other side of king') self.assertIsNone(board[rook_start], 'Rook should have been moved') expected_result = { king_start: None, king_end: King(color), rook_start: None, rook_end: Rook(color), } self.assertDictEqual( expected_result, move_result, 'Expected move result does not match actual')
def test_pawn_capture(self): """ Move a pawn to a square where there is a piece of the opposite color on one of the most immediate diagonal squares. Expected result is that the square that contains the piece of the opposite color is in the list of possible moves for the pawn. Opposing piece is also successfully captured by pawn. :return: """ # Test diagonal move when a piece of the opposite color is present board = ChessBoard() start_position = 'b1' capture_position = 'c2' board[start_position] = Pawn(Color.WHITE) board['c2'] = Bishop(Color.BLACK) expected_possible_moves = ['b2', 'b3', 'c2'] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected pawn to be able to move diagonally' self.assertListEqual(expected_possible_moves, possible_moves, message) # place a second piece and confirm both diagonals show as possible moves board['a2'] = Rook(Color.BLACK) expected_possible_moves = ['a2', 'b2', 'b3', 'c2'] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected pawn to be able to move diagonally in both directions' self.assertListEqual(expected_possible_moves, possible_moves, message) # Move pawn to capture a piece move_result = board.move_piece(start_position, capture_position) message = 'Pawn should have captured piece on ' + capture_position + ' square' self.assertIsInstance(board[capture_position], Pawn, message) # Test move result expected_move_result = { start_position: None, capture_position: Pawn(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
def test_rook_capture(self): """ Move a rook to square where there are pieces of the opposite color on capture file and rank. Expected result is that the squares that contain the pieces of the opposite color are in the list of possible moves for the rook. One opposing piece is also successfully captured by rook. :return: """ board = ChessBoard() start_position = 'b1' capture_position = 'e1' board[start_position] = Rook(Color.WHITE) board['c2'] = Bishop(Color.BLACK) board['c8'] = Pawn(Color.BLACK) board['e1'] = Bishop(Color.BLACK) # Test possible moves with several pieces on possible capture squares expected_possible_moves = [ 'a1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'c1', 'd1', 'e1' ] possible_moves = board.get_legal_moves(start_position) possible_moves.sort() message = 'Expected move list does not match actual move list' self.assertListEqual(expected_possible_moves, possible_moves, message) # Confirm piece is captured move_result = board.move_piece(start_position, capture_position) message = 'Rook should have captured piece on ' + capture_position + ' square' self.assertIsInstance(board[capture_position], Rook, message) # Test move result expected_move_result = { start_position: None, capture_position: Rook(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
def test_en_passant_capture(self): """ Test capturing an opponents pawn via en passant. Expected result is opponents piece is successfully captured when en passant move is performed. :return: """ board = ChessBoard() # Check from white perspective board['b5'] = Pawn(Color.WHITE) board['a7'] = Pawn(Color.BLACK) board.move_piece('a7', 'a5') move_result = board.move_piece('b5', 'a6') self.assertIsNone(board['a5'], 'Expected black pawn to be captured') # Test move result expected_move_result = { 'b5': None, 'a5': None, 'a6': Pawn(Color.WHITE) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual') # Check from black perspective board['e4'] = Pawn(Color.BLACK) board['d2'] = Pawn(Color.WHITE) board.move_piece('d2', 'd4') move_result = board.move_piece('e4', 'd3') self.assertIsNone(board['d4'], 'Expected black pawn to be captured') # Test move result expected_move_result = { 'e4': None, 'd4': None, 'd3': Pawn(Color.BLACK) } self.assertDictEqual(expected_move_result, move_result, 'Expected move result does not match actual')
class ChessGame(db.Model): """ Create new chess game """ __tablename__ = 'game' id = db.Column(db.Integer, primary_key=True) fen = db.Column(db.String(100)) white_player_id = db.Column(db.Integer, db.ForeignKey('players.id')) black_player_id = db.Column(db.Integer, db.ForeignKey('players.id')) is_over = db.Column(db.Boolean, default=False) white_player = db.relationship("Player", foreign_keys=white_player_id) black_player = db.relationship("Player", foreign_keys=black_player_id) score = db.relationship("GameScore", uselist=False, back_populates="game") def __init__(self, fen=None, **kwargs): """ Generate a ChessGame object. :param fen: string Fen notation string to initialize game. :param kwargs: """ super().__init__(**kwargs) self.fen = fen if fen else 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -' fen = Fen(self.fen, validate=False) self._board = ChessBoard(fen) @property def board(self): return self._board @board.setter def board(self, board): self._board = board def get_legal_moves(self, position): """ Retrieve possible legal moves for a piece on a position. :param position: string Algebraic notation position. :return: """ try: ChessHelper.validate_position(position) except InvalidPositionError as e: # Maybe log error here # Reraise exception either way raise else: return self._board.get_legal_moves(position) def move_piece(self, start_position, end_position): """ Move a piece from start_position to end_position. :param start_position: :param end_position: :return: """ try: ChessHelper.validate_position(start_position) ChessHelper.validate_position(end_position) except InvalidPositionError as e: # Maybe log error here # Reraise exception either way raise else: current_fen = Fen(self.fen) current_player = current_fen.current_player next_player = Color.WHITE if current_player == Color.BLACK else Color.BLACK move_result = MoveResult() # If moving a pawn to the end of the board, dont update anything on the board. # Instead, just return the pawn promote info. if self.can_promote_pawn(start_position, end_position): player = self._get_player_by_color(current_player) promote_info = { 'player': player, 'promote_types': self.get_pawn_promote_types() } move_result.pawn_promote_info = promote_info return move_result move_result.update_positions = self._board.move_piece( start_position, end_position) # Determine which directions castling is possible for. castle_info = {Color.BLACK: [], Color.WHITE: []} for color in [Color.WHITE, Color.BLACK]: for direction in [MoveDirection.LEFT, MoveDirection.RIGHT]: if self._board.can_castle(color, direction): castle_info[color].append(direction) # Generate and save fen string after move next_fen = Fen() next_fen_str = next_fen.generate_fen( self._board.get_board_pieces(), next_player, castle_info[Color.WHITE], castle_info[Color.BLACK], self._board.get_enpassant_position()) self.fen = next_fen_str # If checkmate or draw, set game over flag. Also create game_score object and fill # the move results object. is_checkmate = self._board.is_checkmate(next_player) is_stalemate = self._board.is_stalemate(next_player) if is_checkmate or is_stalemate: self.is_over = True if is_checkmate: if current_player == Color.WHITE: self.score = GameScore(game=self, white_score=1) player_in_checkmate = self.black_player player_in_checkmate.color = Color.BLACK else: self.score = GameScore(game=self, black_score=1) player_in_checkmate = self.white_player player_in_checkmate.color = Color.WHITE move_result.king_in_checkmate = player_in_checkmate else: self.score = GameScore(game=self, white_score=0.5, black_score=0.5) move_result.draw = True # If it is check, add info to move_result. is_check = self._board.is_check(next_player) if is_check: if current_player == Color.WHITE: player_in_check = self._get_player_by_color(Color.BLACK) else: player_in_check = self._get_player_by_color(Color.WHITE) move_result.king_in_check = player_in_check return move_result @property def current_player(self): """ Retrieve the current player :return Player: Player object with color set. """ fen = Fen(self.fen) current_player_color = fen.current_player current_player = self.white_player if current_player_color == Color.WHITE else self.black_player # Dynamically add color field so UI can know player info and color. if current_player: current_player.color = Color.WHITE if current_player_color == Color.WHITE else Color.BLACK return current_player def promote_pawn(self, start_position, end_position, piece_type): """ Promote a pawn to another piece type. :param start_position: string Algebraic notation for pawn position. :param end_position: string Algebraic notation for destination position. :param piece_type: Type Value from Type enum :return: """ try: ChessHelper.validate_position(start_position) ChessHelper.validate_position(end_position) except InvalidPositionError as e: # Maybe log error here # Reraise exception either way raise else: if piece_type not in self.get_pawn_promote_types(): raise PieceTypeError( piece_type, 'Cannot promote pawn to supplied piece type') if self._board[start_position] is None: raise EmptyPositionError(start_position) # TODO confirm pawn on second to last row piece = self._board[start_position] piece_class = { Type.ROOK: Rook, Type.KNIGHT: Knight, Type.BISHOP: Bishop, Type.QUEEN: Queen } self._board[start_position] = piece_class[piece_type](piece.color) return self.move_piece(start_position, end_position) def get_winner(self): """ Return winner of game. :return: """ # Check if score obj exist. If so, return winner pass @classmethod def get_pawn_promote_types(cls): """ Retrieve the piece types a pawn can promote to. :return: Type[] """ return [Type.ROOK, Type.KNIGHT, Type.BISHOP, Type.QUEEN] def can_promote_pawn(self, start_position, end_position): """ Test if pawn promotion is possible for the provided position. :param start_position: string Algebraic notation position. :param end_position: string Algebraic notation position. :return: """ try: ChessHelper.validate_position(start_position) ChessHelper.validate_position(end_position) except InvalidPositionError as e: # Maybe log error here # Reraise exception either way raise else: if self._board[start_position] is None: return False piece = self._board[start_position] if piece.type != Type.PAWN: return False _, start_row = self._board.position_to_row_and_column( start_position, piece.color) _, end_row = self._board.position_to_row_and_column( end_position, piece.color) if start_row == self._board.get_dimension( ) - 2 and end_row == self._board.get_dimension() - 1: return True return False def save_to_db(self): """ Save game to db. :return: """ db.session.add(self) db.session.commit() def delete_from_db(self): """ Delete this game from the db. :return: """ db.session.delete(self) db.session.commit() def to_dict(self): """ Return dictionary for game. :return: """ current_player = self.current_player white_player = self.white_player black_player = self.black_player return { 'game_id': self.id, 'current_player': current_player.to_dict() if current_player else None, 'white_player': white_player.to_dict() if white_player else None, 'black_player': black_player.to_dict() if black_player else None, 'game_over': self.is_over, 'board': { position: piece.to_dict() for position, piece in self.board.get_board_pieces().items() if piece } } @classmethod def load_by_id(cls, game_id): game = cls.query.get(game_id) if game: game.board = ChessBoard(Fen(game.fen)) return game def _get_player_by_color(self, color): """ Retrieve the player associated with the provided color. :param color: Color :return: Player """ if color == Color.WHITE: player = self.white_player player.color = Color.WHITE else: player = self.black_player player.color = Color.BLACK return player def __str__(self): return str(self._board)
def test_cannot_castle(self): """ Test cases where a king cannot castle. Expected result is king cannot castle through check, from check, into check, after moving, if the rook has moved. :return: """ # Check case where king would pass through check board = ChessBoard() board['a1'] = Rook(Color.WHITE) board['e1'] = King(Color.WHITE) board['d5'] = Rook(Color.BLACK) expected_moves = ['e2', 'f1', 'f2'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # King in check board.move_piece('d5', 'e5') expected_moves = ['d1', 'd2', 'f1', 'f2'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # King ends in check board.move_piece('e5', 'c5') expected_moves = ['d1', 'd2', 'e2', 'f1', 'f2'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # Check after king moves board = ChessBoard() board['a1'] = Rook(Color.WHITE) board['e1'] = King(Color.WHITE) board.move_piece('e1', 'd1') expected_moves = ['c1', 'c2', 'd2', 'e1', 'e2'] legal_moves = board.get_legal_moves('d1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # Check after left rook moves board = ChessBoard() board['a1'] = Rook(Color.WHITE) board['e1'] = King(Color.WHITE) board.move_piece('a1', 'b1') expected_moves = ['d1', 'd2', 'e2', 'f1', 'f2'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # Check after right rook moves board = ChessBoard() board['e1'] = King(Color.WHITE) board['h1'] = Rook(Color.WHITE) board.move_piece('h1', 'h2') expected_moves = ['d1', 'd2', 'e2', 'f1', 'f2'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # Check with both rooks after right rook moves board = ChessBoard() board['a1'] = Rook(Color.WHITE) board['e1'] = King(Color.WHITE) board['h1'] = Rook(Color.WHITE) board.move_piece('h1', 'h2') expected_moves = ['c1', 'd1', 'd2', 'e2', 'f1', 'f2'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # Check with both rooks after right rook moves board = ChessBoard() board['a1'] = Rook(Color.WHITE) board['e1'] = King(Color.WHITE) board['h1'] = Rook(Color.WHITE) board.move_piece('a1', 'b1') expected_moves = ['d1', 'd2', 'e2', 'f1', 'f2', 'g1'] legal_moves = board.get_legal_moves('e1') legal_moves.sort() self.assertListEqual(expected_moves, legal_moves, 'Expected moves does not match actual') # Confirm cannot castle even if king and rooks back in starting positions fen = Fen('r3k2r/8/8/8/8/8/8/8 b - -') board = ChessBoard(fen) can_castle_left = board.can_castle(Color.BLACK, MoveDirection.LEFT) can_castle_right = board.can_castle(Color.BLACK, MoveDirection.RIGHT) self.assertFalse(can_castle_left, 'King should not be able to castle left') self.assertFalse(can_castle_right, 'King should not be able to castle right')