def castle(self, color, side=Side.Kingside): """Perform a castle.""" grid = deepcopy(self) king = next( (x for x in grid._pieces[piece_types.King] if x.color is color)) rook_f = 7 if side is Side.Kingside else 0 rook = grid[Position(king.position.rank, rook_f)] if not rook or not isinstance(rook, piece_types.Rook): return False if king.moves or rook.moves: return False direction = int(math.copysign(1, rook_f - king.position.file)) move_pos = king.position + Position(0, direction) if grid[move_pos] or not self.move(rook.position, move_pos): return False grid._pieces[piece_types.Rook].remove(rook) for _ in range(2): moved = grid.move(king.position, king.position + Position(0, direction)) if not moved: return False grid._pieces[piece_types.Rook].append(rook) self._pieces = grid._pieces return True
def test_attack_range(pawn): # noqa: D103 dir_val = pawn.get_direction(pawn).value grid = pawn.grid assert pawn.attack_range == get_expected_attack_range(pawn, left=True, right=True) p2_pos = pawn.position + Position(dir_val, -1) grid.add_piece(pieces.Pawn(p2_pos, pawn.color)) assert pawn.attack_range == get_expected_attack_range(pawn, left=False, right=True) p3_pos = pawn.position + Position(dir_val, 1) grid.add_piece(pieces.Pawn(p3_pos, pawn.color)) assert pawn.attack_range == get_expected_attack_range(pawn, left=False, right=False) grid[p2_pos].color = grid[p2_pos].color.inverted() assert pawn.attack_range == get_expected_attack_range(pawn, left=True, right=False) grid[p3_pos].color = grid[p3_pos].color.inverted() assert pawn.attack_range == get_expected_attack_range(pawn, left=True, right=True)
def test_an_passant(pawn): # noqa: D103 grid = pawn.grid p2_pos = pawn.position + Position(0, -1) grid.add_piece(pieces.Pawn(p2_pos, pawn.color)) grid[p2_pos].previous_positions = [None] assert pawn.attack_range == get_expected_attack_range(pawn, left=True, right=True) grid[p2_pos].color = pawn.color.inverted() assert pawn.attack_range == get_expected_attack_range(pawn, left=True, right=True) grid[p2_pos].previous_positions = [] assert pawn.attack_range == get_expected_attack_range(pawn, left=True, right=True) p3_pos = pawn.position + Position(0, 1) attacked_pos = p3_pos + Position(pawn.get_direction(pawn).value, 0) grid.add_piece(pieces.Pawn(p3_pos, pawn.color.inverted())) grid[p3_pos].previous_positions = [None] other = grid[p3_pos] assert grid.move(pawn.position, attacked_pos) assert grid.captured == [other]
def test_knight_ranges(color, coords, knight): # noqa: D103 knight.position = Position(*coords) knight.color = color assert sorted(knight.move_range) == sorted(knight.attack_range) move_diffs = [(1, 2), (1, -2), (-1, 2), (-1, -2)] move_diffs += list(map(reversed, move_diffs)) move_diffs = map(lambda x: Position(*x), move_diffs) moves = map(lambda x: x + knight.position, move_diffs) moves = list(filter(lambda x: x.is_valid(), moves)) assert sorted(knight.move_range) == sorted(moves) pos = knight.position + Position(1, 2) pawn = pieces.Pawn(pos, color=color) knight.grid.add_piece(pawn) try: moves.remove(pawn.position) except ValueError: pass assert sorted(knight.move_range) == sorted(moves) pawn.color = pawn.color.inverted() if pawn.position.is_valid(): moves.append(pawn.position) assert sorted(knight.move_range) == sorted(moves)
def get_expected_move_range(pawn, two_positions=False): """Get expected move range.""" r, f = pawn.position dir_val = pieces.Pawn.get_direction(pawn).value expected = [Position(r + dir_val, f)] if two_positions: expected.append(Position(r + dir_val * 2, f)) return expected
def test_bishop_incorrect_move(wrong_pos_coords, bishop): # noqa: D103 assert not bishop.move(bishop.position) assert not bishop.move(bishop.position + Position(*wrong_pos_coords)) assert not bishop.move(Position(MAX_POS, bishop.position.file)) assert not bishop.move(Position(-1, bishop.position.file)) assert not bishop.move(Position(bishop.position.rank, MAX_POS)) assert not bishop.move(Position(bishop.position.rank, -1))
def test_king_incorrect_move(wrong_pos_coords, king): # noqa: D103 assert not king.move(king.position) assert not king.move(Position(*wrong_pos_coords) + king.position) assert not king.move(Position(MAX_POS, king.position.file)) assert not king.move(Position(-1, king.position.file)) assert not king.move(Position(king.position.rank, MAX_POS)) assert not king.move(Position(king.position.rank, -1))
def test_rook_incorrect_move(wrong_pos_coords, rook): # noqa: D103 assert not rook.move(rook.position) assert not rook.move(rook.position + Position(*wrong_pos_coords)) assert not rook.move(Position(MAX_POS, rook.position.file)) assert not rook.move(Position(-1, rook.position.file)) assert not rook.move(Position(rook.position.rank, MAX_POS)) assert not rook.move(Position(rook.position.rank, -1))
def test_knight_incorrect_move(wrong_pos_coords, knight): # noqa: D103 assert not knight.move(knight.position) assert not knight.move(knight.position + Position(*wrong_pos_coords)) assert not knight.move(Position(MAX_POS, knight.position.file)) assert not knight.move(Position(-1, knight.position.file)) assert not knight.move(Position(knight.position.rank, MAX_POS)) assert not knight.move(Position(knight.position.rank, -1))
def test_pawn_empty_range(pawn): # noqa: D103 pawn.position = Position(7, 5) pawn.color = Color.White assert pawn.move_range == [] pawn.position = Position(0, 5) pawn.color = Color.Black assert pawn.move_range == []
def test_queen_incorrect_move(wrong_pos_coords, queen): # noqa: D103 assert not queen.move(queen.position) assert not queen.move(Position(*wrong_pos_coords) + queen.position) assert not queen.move(Position(MAX_POS, queen.position.file)) assert not queen.move(Position(-1, queen.position.file)) assert not queen.move(Position(queen.position.rank, MAX_POS)) assert not queen.move(Position(queen.position.rank, -1))
def test_vertical_center(piece): # noqa: D103 start_pos = piece.position ver = vertical(piece) p1 = Position(*next(ver)) assert p1.file == start_pos.file p2 = Position(*next(ver)) assert p2.file == start_pos.file expected = [start_pos.rank - 1, start_pos.rank + 1] assert sorted((p1.rank, p2.rank)) == expected
def test_horizontal_center(piece): # noqa: D103 start_pos = piece.position hor = horizontal(piece) p1 = Position(*next(hor)) assert p1.rank == start_pos.rank p2 = Position(*next(hor)) assert p2.rank == start_pos.rank expected = [start_pos.file - 1, start_pos.file + 1] assert sorted((p1.file, p2.file)) == expected
def test_in_check(grid): # noqa: D103 king = piece_types.King(Position(0, 0)) rook = piece_types.Rook(Position(0, 1), Color.Black) assert not grid.own_king_in_check(king) grid.add_piece(king) grid.add_piece(rook) assert grid.own_king_in_check(king) grid.report_capture(rook) assert not grid.own_king_in_check(king)
def test_vertical_edge(p_rank, piece): # noqa: D103 piece.position = Position(p_rank, piece.position.file) start_pos = piece.position ver = vertical(piece) p1 = Position(*next(ver)) diff = 1 if p_rank == MIN_POS else -1 assert p1.file == start_pos.file assert p1.rank == start_pos.rank + diff p2 = Position(*next(ver)) assert p2.file == start_pos.file assert p2.rank == start_pos.rank + diff * 2
def attack_range(self): """Get an attack range for pawn.""" r, f = self.position n_rank = r + self.get_direction(self).value rng = [] for n_file in (f + 1, f - 1): pos = Position(n_rank, n_file) other = self.grid[pos] if pos.is_valid() and (not other or other.color != self.color): rng.append(pos) return rng
def _pos_iter(piece, transformation): """Iterate through positions based on the given transformation function.""" pos = piece.position while True: pos = Position(*transformation(*pos)) if not pos.is_valid(): break other_piece = piece.grid[pos] if other_piece: if other_piece.color != piece.color and piece.move_attacks: yield pos break yield pos
def test_horizontal_edge(p_file, piece): # noqa: D103 piece.position = Position(piece.position.rank, p_file) start_pos = piece.position hor = horizontal(piece) p1 = Position(*next(hor)) diff = 1 if p_file == MIN_POS else -1 assert p1.file == start_pos.file + diff assert p1.rank == start_pos.rank p2 = Position(*next(hor)) assert p2.file == start_pos.file + diff * 2 assert p2.rank == start_pos.rank
def move(self, position): # noqa: D102 new_pos = Position(*position) if not new_pos.is_valid(): return False if position == self.position: return False if new_pos not in self.move_range + self.attack_range: return False self.previous_positions.append(self.position) self.position = position return True
def test_queen_ranges(coords, queen): # noqa: D103 queen.position = Position(*coords) exp_ranges = chain(diagonal(queen), horizontal(queen), vertical(queen)) assert sorted(queen.move_range) == sorted(queen.attack_range) assert sorted(exp_ranges) == sorted(queen.move_range)
def __init__(self, position, color=Color.White): self.position = Position(*position) self.color = color self.grid = None self.move_attacks = True self._mv_range = [] self.previous_positions = []
def collision_setup(piece): # noqa: D103 grid = piece.grid p2 = Piece(piece.position + Position(0, 1), piece.color) grid.add_piece(piece) grid.add_piece(p2) r = right(piece) return piece, p2, r
def test_king_ranges(coords, king): # noqa: D103 king.position = Position(*coords) exp_ranges = chain(diagonal(king, 4), horizontal(king, 2), vertical(king, 2)) assert sorted(king.move_range) == sorted(king.attack_range) assert sorted(exp_ranges) == sorted(king.move_range)
def test_move_in_check(): # noqa: D103 grid = Grid() r, f = 1, 1 king = piece_types.King(Position(r, f - 1)) rook = piece_types.Rook(Position(r, f + 1), color=Color.Black) p1 = piece_types.Pawn(Position(r - 1, f)) p2 = piece_types.Pawn(Position(r - 1, f + 2)) for piece in (king, rook, p1, p2): grid.add_piece(piece) assert grid.own_king_in_check(king) assert not grid.move(p2.position, p2.position + Position(1, 0)) assert grid.move(p1.position, p1.position + Position(1, 0)) assert not grid.own_king_in_check(king) p1.position += Position(-1, 0) print(king, p2, rook) assert grid.own_king_in_check(king) assert grid.move(p2.position, rook.position) assert not grid.own_king_in_check(king)
def prepare_pieces(pieces_notations, color): """Prepare pieces for color.""" iters = [] for piece_cls, notations in pieces_notations.items(): pieces = list( map(lambda n: piece_cls(Position.get_pos(n), color), notations)) iters.append(pieces) return chain.from_iterable(iters)
def test_pawn_move_range(r, f, color, pawn): # noqa: D103 pawn.position = Position(r, f) pawn.color = color assert pawn.move_range == get_expected_move_range(pawn, True) start_pos = pawn.position pawn.move(next(iter(pawn.move_range))) pawn.position = start_pos assert pawn.move_range == get_expected_move_range(pawn, False)
def test_revert_move(piece): # noqa: D103 positions = [piece.position] for _ in range(3): piece.move(piece.position + Position(1, 0)) positions.append(piece.position) while positions: assert positions.pop() == piece.position piece.revert_move()
def test_piece_eq(piece): # noqa: D103 other = deepcopy(piece) assert other == piece other.color = other.color.inverted() assert other != piece other = deepcopy(piece) other.position = other.position + Position(1, 1) assert other != piece
def test_castle_attacking_bishop(color, side, grid_castle): # noqa: D103 kings = grid_castle._pieces[piece_types.King] king = next(filter(lambda p: p.color == color, kings)) direction = 1 if color is Color.White else -1 bishop = piece_types.Bishop(king.position + Position(direction, 0), color.inverted()) grid_castle.add_piece(bishop) assert not grid_castle.castle(color, side)
def test_castle_piece_between(color, file_letter, grid_castle): # noqa: D103 rank = 1 if color is Color.White else 8 side = Side.Queenside if file_letter in "bcd" else Side.Kingside pawn = piece_types.Pawn(Position.get_pos(f"{file_letter}{rank}"), color=color.White) grid_castle.add_piece(pawn) assert not grid_castle.castle(color, side)