def test_pawn_moves(self): """Test of pawn_moves() method.""" assert sorted(PieceMoves.pawn_moves(Position(4, 4), self.game)) == [ Move(Position(4, 4), Position(3, 5)), Move(Position(4, 4), Position(4, 5)), ]
def pawn_moves(pos: Position, game: Game) -> List[Move]: """Return list of <game.turn> pawn moves. pawn moves = positions_under_threat + move forward + en_passant """ # Init move list moves = [] # Check if position under threat occupied with opponent pieces or en_passant possible. for pos_under_threat in PositionsUnderThreat.positions_under_pawn_threat( pos, game.turn, game.board): move = Move(pos, pos_under_threat) if PositionsUnderThreat.is_position_enemy( pos_under_threat, game.turn, game.board) or move in PieceMoves.en_passant_moves( pos, game): moves.append(move) # Check forward move. shift_forward_y = 1 if game.turn == Colour.WHITE else -1 pos_forward = Position(pos.x, pos.y + shift_forward_y) if game.board.is_position_empty(pos_forward): move = Move(pos, pos_forward) moves.append(move) # Check double move forward shift_forward_y = 2 if game.turn == Colour.WHITE else -2 pos_d_forward = Position(pos.x, pos.y + shift_forward_y) if (game.board.is_position_empty(pos_forward) and game.board.is_position_empty(pos_d_forward) and not PieceMoves.is_piece_touched(pos, game)): move = Move(pos, pos_d_forward) moves.append(move) return moves
def test_remove_piece(self): board = Board() board.set_piece(Position(1, 2), Pieces.BLACK_BISHOP) assert board.get_piece(Position(1, 2)) == Pieces.BLACK_BISHOP board.remove_piece(Position(1, 2)) assert board.get_piece(Position(1, 2)) is None
def test_positions_under_knight_threat(self): """Test of positions_under_knight_threat() method.""" assert sorted( PositionsUnderThreat.positions_under_knight_threat( Position(7, 6), Colour.WHITE, self.board)) == [Position(5, 7), Position(6, 4)]
def test_set_piece(self): board = Board() board.set_piece(Position(4, 4), Pieces.WHITE_ROOK) # TODO(amirov-m): What is the right order for actual and expected? assert board.get_piece(Position(4, 4)) == Pieces.WHITE_ROOK board.set_piece(Position(7, 7), Pieces.BLACK_PAWN) assert board.get_piece(Position(7, 7)) == Pieces.BLACK_PAWN
def test_positions_under_threat(self): """Test of positions_under_threat() method.""" assert sorted( PositionsUnderThreat.positions_under_threat( Position(4, 4), Pieces.WHITE_KING, self.board)) == [ Position(3, 3), Position(3, 5), Position(4, 5), Position(5, 3) ]
def test_positions_under_king_threat(self): """Test of positions_under_king_threat() method.""" assert sorted( PositionsUnderThreat.positions_under_king_threat( Position(4, 4), Colour.WHITE, self.board)) == [ Position(3, 3), Position(3, 5), Position(4, 5), Position(5, 3) ]
def check_positions(pos: Position, colour: Colour, board: Board, shifts: list) -> List[Position]: """Take input list of shifts and verify which of them are possible.""" # Init position list. positions_under_threat = [] # Iterate through shifts. for shift_x, shift_y in shifts: # Check if shift is possible, otherwise break the loop. if PositionsUnderThreat.is_position_enemy_or_empty( Position(pos.x + shift_x, pos.y + shift_y), colour, board): # Add shift positions_under_threat.append( Position(pos.x + shift_x, pos.y + shift_y)) return positions_under_threat
def test_positions_under_queen_threat(self): """Test of positions_under_queen_threat() method.""" assert sorted( PositionsUnderThreat.positions_under_queen_threat( Position(5, 5), Colour.WHITE, self.board)) == [ Position(4, 5), Position(4, 6), Position(5, 6), Position(5, 7), Position(6, 4), Position(6, 5), Position(7, 3), Position(7, 5), ]
def test_get_positions_for_piece(self): board = Board() board.set_piece(Position(1, 0), Pieces.WHITE_KNIGHT) board.set_piece(Position(4, 0), Pieces.WHITE_KNIGHT) board.set_piece(Position(0, 7), Pieces.WHITE_KNIGHT) board.set_piece(Position(7, 7), Pieces.WHITE_KNIGHT) piece_positions = board.get_positions_for_piece(Pieces.WHITE_KNIGHT) assert set(piece_positions) == { Position(1, 0), Position(4, 0), Position(0, 7), Position(7, 7), }
def test_positions_under_rook_threat(self): """Test of positions_under_rook_threat() method.""" assert sorted( PositionsUnderThreat.positions_under_rook_threat( Position(3, 7), Colour.WHITE, self.board)) == [ Position(0, 7), Position(1, 7), Position(2, 7), Position(3, 5), Position(3, 6), Position(4, 7), Position(5, 7), ]
def test_all_positions_under_threat_for_side(self): """Test of all_positions_under_threat_for_side() method.""" assert sorted( PositionsUnderThreat.all_positions_under_threat_for_side( Colour.WHITE, self.board)) == [ Position(3, 4), Position(3, 7), Position(4, 7), Position(5, 4), Position(5, 5), Position(5, 6), Position(6, 7), Position(7, 7), ]
def test_is_mate(self): """Test of is_check() method.""" assert not GameLogic.is_mate(self.game) # Create dummy board board = Board() board.set_piece(Position(0, 1), Piece(PieceType.PAWN, Colour.WHITE)) board.set_piece(Position(1, 1), Piece(PieceType.PAWN, Colour.WHITE)) board.set_piece(Position(0, 0), Piece(PieceType.KING, Colour.WHITE)) board.set_piece(Position(2, 0), Piece(PieceType.ROOK, Colour.BLACK)) history_moves = [ Move(Position(4, 3), Position(4, 4)), Move(Position(3, 6), Position(3, 4)), ] game = Game(board, Colour.WHITE, history_moves) assert GameLogic.is_mate(game)
def check_positions_with_obstacles(pos: Position, colour: Colour, board: Board, shifts: list) -> List[Position]: """Take input list of shifts and verify which of them are possible. Iterate through shifts till the first obstacle. Obstacle means own or enemy piece. """ # Init position list. positions_under_threat = [] # Iterates though shifts. for shift_x, shift_y in shifts: # Check if shift is possible, otherwise break the loop. if not PositionsUnderThreat.is_position_enemy_or_empty( Position(pos.x + shift_x, pos.y + shift_y), colour, board): break # Add shift positions_under_threat.append( Position(pos.x + shift_x, pos.y + shift_y)) # Check if position is occupied with enemy piece, otherwise break the loop. if PositionsUnderThreat.is_position_enemy( Position(pos.x + shift_x, pos.y + shift_y), colour, board): break return positions_under_threat
def make_move(move: Move, game: Game) -> Game: """Make move. Attention: no checking of check after move. Technically move can be not valid!!! """ # Copy game (pass by value) game = copy.deepcopy(game) # Retrieve piece at start position piece = game.board.get_piece(move.start) # Get possible moves possible_moves = PieceMoves.moves(piece.type, move.start, game) # Check if move satisfies if move in possible_moves: # Check if castling occurs if move in PieceMoves.castling_moves(move.start, game): game.board.set_piece( Position(int((move.finish.x + move.start.x) / 2), move.start.y), Piece(PieceType.ROOK, game.turn), ) # Short castling if move.finish.x - move.start.x > 0: game.board.remove_piece(Position(7, move.start.y)) # Long castling else: game.board.remove_piece(Position(0, move.start.y)) # Check if en passant occurs if move in PieceMoves.en_passant_moves(move.start, game): game.board.remove_piece(Position(move.finish.x, move.start.y)) # Update board game.board.set_piece(move.finish, piece) game.board.remove_piece(move.start) # Update history game.history_moves.append(move) return Game(game.board, Colour.change_colour(game.turn), game.history_moves)
def en_passant_moves(pos: Position, game: Game) -> List[Move]: """Return list of <game.turn> en passant moves. en passant: - opponent pawn makes the move at previous turn - opponent pawn jumps over 2 positions """ # Init list of moves. en_passant = [] # Retrieve start piece. piece_start = game.board.get_piece(pos) # Check if piece_start not empty. if piece_start is not None and piece_start.type == PieceType.PAWN: # 2 directions. shifts = [(-1, 0), (1, 0)] for shift_x, shift_y in shifts: # Retrieve not start piece. piece = game.board.get_piece( Position(pos.x + shift_x, pos.y + shift_y)) if piece is not None and piece.type == PieceType.PAWN: # Retrieve last_move. last_move = game.history_moves[-1] # Check if pawn makes the last move and if it jumps over 2 positions. if (Position(pos.x + shift_x, pos.y + shift_y) == last_move.finish and abs(last_move.start.y - last_move.finish.y) == 2): # Add move in dependence of colour. if game.turn == Colour.WHITE: move = Move(pos, Position(pos.x + shift_x, pos.y + 1)) en_passant.append(move) else: move = Move(pos, Position(pos.x + shift_x, pos.y - 1)) en_passant.append(move) return en_passant
def setUp(self) -> None: """Create dummy game.""" # Create dummy board self.board = Board() self.board.set_piece(Position(4, 3), Pieces.WHITE_PAWN) self.board.set_piece(Position(3, 4), Pieces.WHITE_PAWN) self.board.set_piece(Position(5, 4), Pieces.WHITE_PAWN) self.board.set_piece(Position(0, 6), Pieces.WHITE_PAWN) self.board.set_piece(Position(7, 7), Pieces.WHITE_PAWN) self.board.set_piece(Position(4, 4), Pieces.WHITE_KING) self.board.set_piece(Position(6, 6), Pieces.WHITE_BISHOP) self.board.set_piece(Position(3, 7), Pieces.WHITE_ROOK) self.board.set_piece(Position(5, 5), Pieces.WHITE_QUEEN) self.board.set_piece(Position(7, 6), Pieces.WHITE_KNIGHT) self.board.set_piece(Position(4, 5), Pieces.BLACK_PAWN) self.board.set_piece(Position(5, 7), Pieces.BLACK_ROOK)
def test_make_move(self): """Test of make_move() method.""" # Check short castling game = GameLogic.make_move(Move(Position(4, 0), Position(6, 0)), self.game) assert game.turn != self.game.turn assert game.history_moves[-1] == Move(Position(4, 0), Position(6, 0)) assert game.board.get_piece(Position(4, 0)) is None assert game.board.get_piece(Position(5, 0)).type == PieceType.ROOK assert game.board.get_piece(Position(6, 0)).type == PieceType.KING assert game.board.get_piece(Position(7, 0)) is None # Check long castling game = GameLogic.make_move(Move(Position(4, 0), Position(2, 0)), self.game) assert game.turn != self.game.turn assert game.history_moves[-1] == Move(Position(4, 0), Position(2, 0)) assert game.board.get_piece(Position(4, 0)) is None assert game.board.get_piece(Position(3, 0)).type == PieceType.ROOK assert game.board.get_piece(Position(2, 0)).type == PieceType.KING assert game.board.get_piece(Position(1, 0)) is None assert game.board.get_piece(Position(0, 0)) is None # Check en_passant game = GameLogic.make_move(Move(Position(4, 4), Position(3, 5)), self.game) assert game.turn != self.game.turn assert game.history_moves[-1] == Move(Position(4, 4), Position(3, 5)) assert game.board.get_piece(Position(4, 4)) is None assert game.board.get_piece(Position(3, 5)).type == PieceType.PAWN assert game.board.get_piece(Position(3, 5)).colour == Colour.WHITE assert game.board.get_piece(Position(3, 4)) is None
def test_remove_piece_empty_cell(self): board = Board() board.remove_piece(Position(3, 4)) assert board.get_piece(Position(3, 4)) is None
def test_king_moves(self): """Test of king_moves() method.""" assert sorted(PieceMoves.king_moves(Position(4, 0), self.game)) == [ Move(Position(4, 0), Position(3, 0)), Move(Position(4, 0), Position(3, 1)), Move(Position(4, 0), Position(4, 1)), Move(Position(4, 0), Position(5, 0)), Move(Position(4, 0), Position(5, 1)), Move(Position(4, 0), Position(6, 0)), ]
def setUp(self) -> None: """Create dummy game.""" # Create dummy board self.board = Board() self.board.set_piece(Position(4, 0), Pieces.WHITE_KING) self.board.set_piece(Position(0, 0), Pieces.WHITE_ROOK) self.board.set_piece(Position(7, 0), Pieces.WHITE_ROOK) self.board.set_piece(Position(4, 4), Pieces.WHITE_PAWN) self.board.set_piece(Position(4, 1), Pieces.WHITE_BISHOP) self.board.set_piece(Position(3, 4), Pieces.BLACK_PAWN) self.board.set_piece(Position(5, 4), Pieces.BLACK_PAWN) self.board.set_piece(Position(4, 3), Pieces.BLACK_ROOK) self.board.set_piece(Position(2, 2), Pieces.BLACK_PAWN) # Create game self.game = Game(self.board, Colour.WHITE, [Move(Position(3, 6), Position(3, 4))])
def test_is_move_possible(self): """Test of is_move_possible() method.""" assert not GameLogic.is_move_possible( self.game, Move(Position(4, 1), Position(3, 2))) assert not GameLogic.is_move_possible( self.game, Move(Position(4, 0), Position(3, 1))) assert not GameLogic.is_move_possible( self.game, Move(Position(4, 0), Position(4, -1))) assert not GameLogic.is_move_possible( self.game, Move(Position(2, 2), Position(2, 1))) assert GameLogic.is_move_possible(self.game, Move(Position(4, 0), Position(5, 1))) assert GameLogic.is_move_possible(self.game, Move(Position(4, 0), Position(2, 0))) assert GameLogic.is_move_possible(self.game, Move(Position(4, 0), Position(6, 0)))
def create_start_board() -> Board: board = Board() # Set white pieces. board.set_piece(Position(0, 0), Pieces.WHITE_ROOK) board.set_piece(Position(1, 0), Pieces.WHITE_KNIGHT) board.set_piece(Position(2, 0), Pieces.WHITE_BISHOP) board.set_piece(Position(3, 0), Pieces.WHITE_QUEEN) board.set_piece(Position(4, 0), Pieces.WHITE_KING) board.set_piece(Position(5, 0), Pieces.WHITE_BISHOP) board.set_piece(Position(6, 0), Pieces.WHITE_KNIGHT) board.set_piece(Position(7, 0), Pieces.WHITE_ROOK) board.set_piece(Position(0, 1), Pieces.WHITE_PAWN) board.set_piece(Position(1, 1), Pieces.WHITE_PAWN) board.set_piece(Position(2, 1), Pieces.WHITE_PAWN) board.set_piece(Position(3, 1), Pieces.WHITE_PAWN) board.set_piece(Position(4, 1), Pieces.WHITE_PAWN) board.set_piece(Position(5, 1), Pieces.WHITE_PAWN) board.set_piece(Position(6, 1), Pieces.WHITE_PAWN) board.set_piece(Position(7, 1), Pieces.WHITE_PAWN) # Set black pieces. board.set_piece(Position(0, 7), Pieces.BLACK_ROOK) board.set_piece(Position(1, 7), Pieces.BLACK_KNIGHT) board.set_piece(Position(2, 7), Pieces.BLACK_BISHOP) board.set_piece(Position(3, 7), Pieces.BLACK_QUEEN) board.set_piece(Position(4, 7), Pieces.BLACK_KING) board.set_piece(Position(5, 7), Pieces.BLACK_BISHOP) board.set_piece(Position(6, 7), Pieces.BLACK_KNIGHT) board.set_piece(Position(7, 7), Pieces.BLACK_ROOK) board.set_piece(Position(0, 6), Pieces.BLACK_PAWN) board.set_piece(Position(1, 6), Pieces.BLACK_PAWN) board.set_piece(Position(2, 6), Pieces.BLACK_PAWN) board.set_piece(Position(3, 6), Pieces.BLACK_PAWN) board.set_piece(Position(4, 6), Pieces.BLACK_PAWN) board.set_piece(Position(5, 6), Pieces.BLACK_PAWN) board.set_piece(Position(6, 6), Pieces.BLACK_PAWN) board.set_piece(Position(7, 6), Pieces.BLACK_PAWN) return board
def test_all_moves(self): """Test of all_moves() method.""" assert sorted(PieceMoves.all_moves(self.game)) == [ Move(Position(0, 0), Position(1, 0)), Move(Position(0, 0), Position(2, 0)), Move(Position(0, 0), Position(3, 0)), Move(Position(0, 1), Position(0, 2)), Move(Position(0, 1), Position(0, 3)), *sorted(PieceMoves.king_moves(Position(4, 0), self.game)), Move(Position(4, 4), Position(3, 5)), Move(Position(4, 4), Position(4, 5)), Move(Position(7, 0), Position(5, 0)), Move(Position(7, 0), Position(6, 0)), Move(Position(7, 1), Position(7, 2)), Move(Position(7, 1), Position(7, 3)), ]
def test_positions_under_pawn_threat(self): """Test of positions_under_pawn_threat() method.""" assert not PositionsUnderThreat.positions_under_pawn_threat( Position(4, 3), Colour.WHITE, self.board) assert sorted( PositionsUnderThreat.positions_under_pawn_threat( Position(3, 4), Colour.WHITE, self.board)) == [Position(2, 5), Position(4, 5)] assert sorted( PositionsUnderThreat.positions_under_pawn_threat( Position(5, 4), Colour.WHITE, self.board)) == [Position(4, 5), Position(6, 5)] assert sorted( PositionsUnderThreat.positions_under_pawn_threat( Position(0, 6), Colour.WHITE, self.board)) == [Position(1, 7)] assert not PositionsUnderThreat.positions_under_pawn_threat( Position(7, 7), Colour.WHITE, self.board) assert PositionsUnderThreat.positions_under_pawn_threat( Position(4, 5), Colour.BLACK, self.board) == [Position(3, 4), Position(5, 4)]
def test_en_passant_moves(self): """Test of en_passant_moves() method.""" assert sorted(PieceMoves.en_passant_moves(Position( 4, 4), self.game)) == [Move(Position(4, 4), Position(3, 5))]
def test_set_piece_rewrite_piece(self): board = Board() board.set_piece(Position(1, 3), Pieces.WHITE_QUEEN) board.set_piece(Position(1, 3), Pieces.BLACK_KING) assert board.get_piece(Position(1, 3)) == Pieces.BLACK_KING
def test_castling_moves(self): """Test of castling_moves() method.""" assert sorted(PieceMoves.castling_moves(Position( 4, 0), self.game)) == [Move(Position(4, 0), Position(6, 0))]
def castling_moves(pos: Position, game: Game) -> List[Move]: """Return list of <game.turn> castling moves. Check is taking into account!!! """ # Init catling list. castling = [] # Retrieve piece at start position. piece_start = game.board.get_piece(pos) # Retrieve positions under threat (important info for castling). pos_under_threat = PositionsUnderThreat.all_positions_under_threat_for_side( game.turn, game.board) # Check if piece piece at start position King with no threat/check. if (piece_start is not None and piece_start.type == PieceType.KING and not PieceMoves.is_piece_touched(pos, game) and pos not in pos_under_threat): # Short castling. _1r_ means 1 pos to the right from white side. is_1r_pos_avail = ( game.board.is_position_empty(Position(pos.x + 1, pos.y)) and Position(pos.x + 1, pos.y) not in pos_under_threat) is_2r_pos_avail = ( game.board.is_position_empty(Position(pos.x + 2, pos.y)) and Position(pos.x + 2, pos.y) not in pos_under_threat) is_3r_pos_rook = ( not game.board.is_position_empty(Position(pos.x + 3, pos.y)) and game.board.get_piece(Position( pos.x + 3, pos.y)).type == PieceType.ROOK) if (is_1r_pos_avail and is_2r_pos_avail and is_3r_pos_rook and not PieceMoves.is_piece_touched( Position(pos.x + 3, pos.y), game)): move = Move(pos, Position(pos.x + 2, pos.y)) castling.append(move) # Long castling. _1l_ means 1 pos to the left from white side. is_1l_pos_avail = ( game.board.is_position_empty(Position(pos.x - 1, pos.y)) and Position(pos.x - 1, pos.y) not in pos_under_threat) is_2l_pos_avail = ( game.board.is_position_empty(Position(pos.x - 2, pos.y)) and Position(pos.x - 2, pos.y) not in pos_under_threat) is_4l_pos_rook = ( not game.board.is_position_empty(Position(pos.x - 4, pos.y)) and game.board.get_piece(Position( pos.x - 4, pos.y)).type == PieceType.ROOK) if (is_1l_pos_avail and is_2l_pos_avail and is_4l_pos_rook and game.board.is_position_empty(Position( pos.x - 3, pos.y)) and not PieceMoves.is_piece_touched( Position(pos.x - 4, pos.y), game)): move = Move(pos, Position(pos.x - 2, pos.y)) castling.append(move) return castling
def test_is_piece_touched(self): """Test of is_piece_touched() method.""" assert PieceMoves.is_piece_touched(Position(4, 4), self.game) assert PieceMoves.is_piece_touched(Position(3, 4), self.game)