def test_parse_moves_fools_mate(): parser = Parser.from_fools_mate() stats = get_parser_stats(parser) total = stats['total'] white_pieces = stats['white_pieces'] white_destinations = stats['white_destinations'] col_helpers = stats['col_helpers'] row_helpers = stats['row_helpers'] black_pieces = stats['black_pieces'] black_destinations = stats['black_destinations'] upgrades = stats['upgrades'] en_passants = stats['en_passants'] queensides = stats['queensides'] kingsides = stats['kingsides'] assert 2 == total assert (2 * total) - kingsides - queensides == sum( white_destinations.values()) + sum(black_destinations.values()) assert 0 == en_passants assert 0 == kingsides assert 0 == queensides assert {} == upgrades assert {} == col_helpers assert {} == row_helpers assert dict(Pawn=2) == white_pieces assert 1 == white_destinations[Point(3, 6)] assert 2 == len(white_destinations) assert dict(Pawn=1, Queen=1) == black_pieces assert 1 == black_destinations[Point(4, 4)] assert 2 == len(black_destinations)
def test_parse_mecking_h_donner_1971(): parser = Parser.from_pgn(get_input.get('mecking_h_donner_1971.pgn')) stats = get_parser_stats(parser) total = stats['total'] white_pieces = stats['white_pieces'] white_destinations = stats['white_destinations'] col_helpers = stats['col_helpers'] row_helpers = stats['row_helpers'] black_pieces = stats['black_pieces'] black_destinations = stats['black_destinations'] upgrades = stats['upgrades'] en_passants = stats['en_passants'] queensides = stats['queensides'] kingsides = stats['kingsides'] assert len(parser.moves) == total assert (2 * total) - kingsides - queensides - 1 == sum( white_destinations.values()) + sum(black_destinations.values()) assert 0 == en_passants assert 2 == kingsides assert 0 == queensides assert {} == upgrades assert dict(F=1) == col_helpers assert {} == row_helpers assert dict(Pawn=8, Rook=1, Knight=3, Bishop=2, Queen=2, King=1) == white_pieces assert 2 == white_destinations[Point(4, 2)] assert 15 == len(white_destinations) assert dict(Pawn=6, Knight=3, Bishop=5, Queen=1, King=1) == black_pieces assert 3 == black_destinations[Point(4, 2)] assert 11 == len(black_destinations)
def _get_all_valid_moves(self) -> tp.List[Point]: moves = [] x = self.pos.x y = self.pos.y for row, col in product(range(-1, 2), range(-1, 2)): move = Point(x + row, y + col) if self.is_valid_move(move): moves.append(move) # Check potential castle moves if self._is_first_move: row = 0 if self.color == Color.White else 7 move_left = Point(row, 3) move_right = Point(row, 6) if self.is_valid_move(move_left): moves.append(move_left) if self.is_valid_move(move_right): moves.append(move_right) return moves
def get_input(self, msg: str) -> Point: '''Prompts for input from the user. Parses input''' piece_location, move_to = '', '' while True: user_input = input(msg) if user_input in ('Quit', 'quit', 'q', 'Q'): raise UserQuitMidGameException if user_input == 'interact': breakpoint() for delim in self.INPUT_DELIMS: if delim in user_input: try: piece_location, move_to = user_input.split(delim) except ValueError: print(self.INVALID_INPUT_MSG) continue if not piece_location or not move_to: print(self.INVALID_INPUT_MSG) continue piece_location = Point.from_str(piece_location) move_to = Point.from_str(move_to) if not self.validate_input(piece_location, move_to): continue break return piece_location, move_to
def test_equals_point(): p = Point(1, 4) q = Point(2, 3) assert p != q q = Point(1, 4) assert p == q
def test_can_not_move_vertically_to_team_piece(): b = Board() p = PieceTester(Color.White, b, Point(4, 4)) assert not p._can_move_vertically_to(1) b.perform_move(b[0, 0], Point(5, 4)) assert not p._can_move_vertically_to(5)
def test_can_move_vertically_to_enemy_piece(): b = Board() p = PieceTester(Color.White, b, Point(4, 4)) assert p._can_move_vertically_to(6) b.perform_move(b[7, 7], Point(2, 4)) assert p._can_move_vertically_to(2)
def perform_move(): piece = PieceTester(Color.White, None, Point(1, 1)) en_passant = Ref(Point(1, 1)) piece.perform_move(Point(3, 4), en_passant) assert not piece.is_first_move assert p.pos == Point(3, 4) assert en_passant() == Point()
def test_parse_length8848_5(): parser = Parser.from_pgn(get_input.get('length8848.5.pgn')) stats = get_parser_stats(parser) total = stats['total'] white_pieces = stats['white_pieces'] white_destinations = stats['white_destinations'] col_helpers = stats['col_helpers'] row_helpers = stats['row_helpers'] black_pieces = stats['black_pieces'] black_destinations = stats['black_destinations'] upgrades = stats['upgrades'] en_passants = stats['en_passants'] queensides = stats['queensides'] kingsides = stats['kingsides'] white_sum = sum(white_destinations.values()) black_sum = sum(black_destinations.values()) assert 8849 == total assert 1 == white_sum - black_sum assert (2 * total) - kingsides - queensides - 1 == white_sum + black_sum assert 0 == en_passants assert 0 == kingsides assert 0 == queensides assert dict(Queen=16) == upgrades assert dict(A=593, B=762, C=885, D=914, E=750, F=803, G=781, H=536) == col_helpers assert { 1: 173, 2: 158, 3: 198, 4: 183, 5: 142, 6: 126, 7: 95, 8: 81 } == row_helpers assert dict(Pawn=48, Rook=962, Knight=1487, Bishop=716, Queen=4818, King=818) == white_pieces assert 144 == white_destinations[Point(3, 2)] assert 64 == len(white_destinations) assert dict(Pawn=48, Rook=1269, Knight=531, Bishop=701, Queen=5146, King=1153) == black_pieces assert 121 == black_destinations[Point(3, 2)] assert 64 == len(black_destinations)
def test_can_move_horizontally_to_enemy_piece(): b = Board() p = PieceTester(Color.White, b, Point(4, 4)) b.perform_move(b[6, 2], Point(4, 2)) assert p._can_move_horizontally_to(2) b.perform_move(b[7, 7], Point(4, 7)) assert p._can_move_horizontally_to(7)
def test_can_not_move_horizontally_to_team_piece(): b = Board() p = PieceTester(Color.White, b, Point(4, 4)) b.perform_move(b[1, 1], Point(4, 1)) assert not p._can_move_horizontally_to(1) b.perform_move(b[1, 7], Point(4, 7)) assert not p._can_move_horizontally_to(7)
def perform_move(self, to: Point, en_passant: Ref[Point]): piece_x = self.pos.x to_x = to.x to_y = to.y # If double-step, any existing en passant was effectively ignored and a new en passant is in effect new_en_passant = Point() if abs(piece_x - to_x) == 2: direction = -1 if piece_x > to_x else 1 new_en_passant = Point(piece_x + direction, to_y) super().perform_move(to, en_passant) en_passant.update(new_en_passant)
def from_board(cls, board: 'Board') -> 'Screenshot': moves: tp.List[TeamMovePair] = [] total_pieces = 0 pawn_locations: tp.List[Point] = [] for row, col in product(range(8), range(8)): piece: Piece = board[row, col] if piece is not None: valid_moves = piece.get_all_valid_moves() for move in valid_moves: moves.append( TeamMovePair(MovePair(Point(row, col), move), piece.color)) total_pieces += 1 if piece.piece_type == PieceType.Pawn: pawn_locations.append(piece.pos) return cls(moves, board.white_score, board.black_score, pawn_locations, total_pieces)
def _get_all_valid_moves(self) -> tp.List[Point]: moves = [] for row, col in product(range(8), range(8)): move = Point(row, col) if self.is_valid_move(move): moves.append(move) return moves
def _get_all_valid_moves(self) -> tp.List[Point]: moves = [] x = self.pos.x y = self.pos.y for i in range(8): v_move = Point(i, y) h_move = Point(x, i) if self.is_valid_move(v_move): moves.append(v_move) if self.is_valid_move(h_move): moves.append(h_move) return moves
def _get_all_valid_moves(self) -> tp.List[Point]: moves = [] x = self.pos.x y = self.pos.y direction = 1 if self.color == Color.White else -1 possible_moves = [ Point(x + direction, y), Point(x + 2 * direction, y), Point(x + direction, y - 1), Point(x + direction, y + 1) ] for move in possible_moves: if self.is_valid_move(move): moves.append(move) return moves
def test_can_move_horizontally_to_empty_space(): b = Board() p = PieceTester(Color.White, b, Point(4, 4)) assert p._can_move_horizontally_to(0) for i in range(1, 8): if i == 4: continue assert p._can_move_horizontally_to(i)
def test_is_valid(): assert Point(0, 0).is_valid() assert Point(4, 4).is_valid() assert Point(7, 7).is_valid() assert not Point(-1, 0).is_valid() assert not Point(0, -1).is_valid() assert not Point(0, 8).is_valid() assert not Point(8, 0).is_valid()
def perform_move(self, to: Point, en_passant: Ref[Point]): ''' Updates piece's internal state to reflect move. Do before changing actual board! Callers must remember to reset en_passant ''' if self._is_first_move: self._is_first_move = False self._pos = to en_passant.update(Point())
def _gen_board(self): _board = np.full((8, 8), fill_value=None) for row, col in product(range(8), range(8)): color = Color.White if row <= 1 else Color.Black piece = None if row == 0 or row == 7: if col == 0 or col == 7: piece = Rook(color, self, Point(row, col)) elif col == 1 or col == 6: piece = Knight(color, self, Point(row, col)) elif col == 2 or col == 5: piece = Bishop(color, self, Point(row, col)) elif col == 3: piece = Queen(color, self, Point(row, col)) else: piece = King(color, self, Point(row, col)) elif row == 1 or row == 6: piece = Pawn(color, self, Point(row, col)) if piece is not None: if piece.piece_type == PieceType.King: self._kings[color] = piece else: self._pieces[color][piece.piece_type].add(piece) _board[row, col] = piece return sf.Frame.from_records(_board, columns=tuple('ABCDEFGH'))
def can_move_vertically_to_empty_space(): b = Board() p = PieceTester(Color.White, b, Point(4, 4)) b.remove_piece((0, 4)) b.remove_piece((1, 4)) b.remove_piece((6, 4)) b.remove_piece((7, 4)) assert p._can_move_vertically_to(0) for i in range(1, 8): if i == 4: continue assert p._can_move_vertically_to(i)
def test_parse_fischer_spassky_1992(): parser = Parser.from_pgn(get_input.get('fischer_spassky_1992.pgn')) stats = get_parser_stats(parser) total = stats['total'] white_pieces = stats['white_pieces'] white_destinations = stats['white_destinations'] col_helpers = stats['col_helpers'] row_helpers = stats['row_helpers'] black_pieces = stats['black_pieces'] black_destinations = stats['black_destinations'] upgrades = stats['upgrades'] en_passants = stats['en_passants'] queensides = stats['queensides'] kingsides = stats['kingsides'] assert len(parser.moves) == total assert (2 * total) - kingsides - queensides - 1 == sum( white_destinations.values()) + sum(black_destinations.values()) assert 0 == en_passants assert 2 == kingsides assert 0 == queensides assert {} == upgrades assert dict(A=1, B=2) == col_helpers assert {} == row_helpers assert dict(Pawn=14, Rook=7, Knight=7, Bishop=8, Queen=3, King=4) == white_pieces assert 3 == white_destinations[Point(0, 4)] assert 28 == len(white_destinations) assert dict(Pawn=12, Rook=3, Knight=12, Bishop=5, Queen=3, King=7) == black_pieces assert 3 == black_destinations[Point(4, 1)] assert 29 == len(black_destinations)
def test_parse_raphael_hiaves_2006(): parser = Parser.from_pgn(get_input.get('raphael_hiaves_2006.pgn')) stats = get_parser_stats(parser) total = stats['total'] white_pieces = stats['white_pieces'] white_destinations = stats['white_destinations'] col_helpers = stats['col_helpers'] row_helpers = stats['row_helpers'] black_pieces = stats['black_pieces'] black_destinations = stats['black_destinations'] upgrades = stats['upgrades'] en_passants = stats['en_passants'] queensides = stats['queensides'] kingsides = stats['kingsides'] assert len(parser.moves) == total assert (2 * total) - kingsides - queensides == sum( white_destinations.values()) + sum(black_destinations.values()) assert 1 == en_passants assert 2 == kingsides assert 0 == queensides assert dict(Queen=1) == upgrades assert dict(A=2, C=1, F=2) == col_helpers assert {} == row_helpers assert dict(Pawn=6, Rook=3, Knight=5, Bishop=2, Queen=2, King=2) == white_pieces assert 3 == white_destinations[Point(4, 3)] assert 15 == len(white_destinations) assert dict(Pawn=10, Rook=2, Knight=3, Bishop=3, Queen=1, King=1) == black_pieces assert 4 == black_destinations[Point(4, 3)] assert 14 == len(black_destinations)
def test_parse_lucky_bardwick_1995(): parser = Parser.from_pgn(get_input.get('lucky_bardwick_1995.pgn')) stats = get_parser_stats(parser) total = stats['total'] white_pieces = stats['white_pieces'] white_destinations = stats['white_destinations'] col_helpers = stats['col_helpers'] row_helpers = stats['row_helpers'] black_pieces = stats['black_pieces'] black_destinations = stats['black_destinations'] upgrades = stats['upgrades'] en_passants = stats['en_passants'] queensides = stats['queensides'] kingsides = stats['kingsides'] assert len(parser.moves) == total assert (2 * total) - kingsides - queensides == sum( white_destinations.values()) + sum(black_destinations.values()) assert 0 == en_passants assert 0 == kingsides assert 1 == queensides assert {} == upgrades assert dict(D=1, F=1) == col_helpers assert {} == row_helpers assert dict(Pawn=20, Rook=55, Knight=30, Bishop=3, Queen=8, King=27) == white_pieces assert 10 == white_destinations[Point(4, 4)] assert 49 == len(white_destinations) assert dict(Pawn=12, Rook=60, Knight=15, Bishop=8, Queen=3, King=45) == black_pieces assert 8 == black_destinations[Point(6, 3)] assert 51 == len(black_destinations)
def __init__(self): self._enpassant_location: Ref[Point] = Ref(Point()) self._kings: tp.Dict[Color, King] = {} self._pieces: tp.Dict[Color, tp.Dict[PieceType, tp.Set[Piece]]] = {} for color in Color: piece_collection: tp.Dict[PieceType, tp.Set[Piece]] = {} for piece_type in (pt for pt in PieceType if pt != PieceType.King): piece_collection[piece_type] = set() self._pieces[color] = piece_collection self._board: sf.Frame = self._gen_board() self.white_score: float = 0.0 self.black_score: float = 0.0
def parse_points_from_move( self, move: Move) -> tp.Tuple[Piece, Point, tp.Optional[PieceType]]: if move.piece == PieceType.King: king = self.get_king(move.color) if move.castle in (Castle.Queenside, Castle.Kingside): direction = 2 if Castle.Kingside else -2 return king, Point(king.pos.x, king.pos.y + direction), None return king, move.destination, None else: target = self._find_target_piece(move) return target, move.destination, move.upgrade
def _can_move_horizontally_to(self, to: int) -> bool: if self._check_illegal_move(Point(self._pos.x, to)): return False me_x = self._pos.x me_y = self._pos.y direction = 1 if to > me_y else -1 # 1 = right, -1 = left col = me_y + direction # Move one space away as starting square # Keep advancing along empty spaces until end is hit while col != to: if self._board[me_x, col] is not None: return False col += direction return True
def _can_move_vertically_to(self, to: int) -> bool: if self._check_illegal_move(Point(to, self._pos.y)): return False me_x = self._pos.x me_y = self._pos.y direction = 1 if to > me_x else -1 # 1 = down, -1 = up row = me_x + direction # Move one space away as starting square # Keep advancing along empty spaces until end is hit while row != to: if self._board[row, me_y] is not None: return False row += direction return True
def _check_illegal_move(self, to: Point, *, ignore_color: bool = False) -> bool: # Cannot move outisde board or to same spot if not to.is_valid() or self._pos == to: return True if ignore_color: return False # Cannot move to teammate spot actual_piece = self._board[to] if actual_piece is not None and actual_piece.color == self.color: return True return False
def _get_all_valid_moves(self) -> tp.List[Point]: moves = [] x = self.pos.x y = self.pos.y possible_moves = [ Point(x + 1, y + 2), Point(x + 1, y - 2), Point(x - 1, y + 2), Point(x - 1, y - 2), Point(x + 2, y + 1), Point(x + 2, y - 1), Point(x - 2, y + 1), Point(x - 2, y - 1) ] for move in possible_moves: if self.is_valid_move(move): moves.append(move) return moves