Spaces:
Build error
Build error
File size: 4,080 Bytes
02f6666 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
from enum import IntEnum
PIECE_TYPES = 14
_COLUMN_TO_INT = {
c:num for c, num in zip("abcdefgh", range(0, 8))
}
class Player(IntEnum):
WHITE = 1
BLACK = 2
class Piece(IntEnum):
PAWN_W = 1
PAWN_B = 2
ROOK_W = 3
ROOK_B = 4
KNIGHT_W = 5
KNIGHT_B = 6
BISHOP_W = 7
BISHOP_B = 8
QUEEN_W = 9
QUEEN_B = 10
KING_W = 11
KING_B = 12
EN_PASSANT_SPACE = 13
@classmethod
def from_fen(cls, letter: chr, default = None):
return {
'p': Piece.PAWN_B,
'P': Piece.PAWN_W,
'r': Piece.ROOK_B,
'R': Piece.ROOK_W,
'n': Piece.KNIGHT_B,
'N': Piece.KNIGHT_W,
'b': Piece.BISHOP_B,
'B': Piece.BISHOP_W,
'q': Piece.QUEEN_B,
'Q': Piece.QUEEN_W,
'k': Piece.KING_B,
'K': Piece.KING_W,
}.get(letter, default)
class BitBoard:
def __init__(self):
self.boards = [0]*PIECE_TYPES
self.next_to_move = Player.WHITE
self.castling_status = set() # Just add pieces for QUEEN_B, etc.
self.halfstep_count = 0
self.fullstep_count = 0
def make_move(self, mv: str):
# NOTE: This does not handle castling yet and probably screws up clearing en-passant.
# Convert the pair into from/to.
from_x = _COLUMN_TO_INT[mv[0].lower()]
from_y = int(mv[1])-1
to_x = _COLUMN_TO_INT[mv[2].lower()]
to_y = int(mv[3])-1
promote_to = None
if len(mv) > 3:
promote_to = Piece.from_fen(mv[4])
# Clear piece at moving spot.
moving_piece = self.get_piece(from_x, from_y)
self.clear_piece(from_x, from_y)
if promote_to is None:
self.set_piece(to_x, to_y, moving_piece)
else:
self.set_piece(to_x, to_y, promote_to)
@classmethod
def from_fen(cls, fen_string: str):
# rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
# 4r3/1k6/pp3r2/1b2P2p/3R1p2/P1R2P2/1P4PP/6K1 w - - 0 35
new_board = cls()
board_layout, current_player, castle_status, en_passant, halfmove_clock, fullmove_clock = fen_string.split(" ")
board_rows = board_layout.split("/")
for y, row in enumerate(reversed(board_rows)):
x = 0
while row:
if row[0].isdigit():
x += int(row[0])
else:
piece = Piece.from_fen(row[0])
new_board.set_piece(x, y, piece)
x += 1
row = row[1:]
if current_player == 'w':
new_board.next_to_move = Player.WHITE
elif current_player == 'b':
new_board.next_to_move = Player.BLACK
else:
raise Exception(f"Bad parse. Starting player unrecognized: {current_player}")
# Parse castling here.
for character in castle_status:
new_board.castling_status.add(Piece.from_fen(character))
# Parse en_passant here
new_board.halfstep_count = int(halfmove_clock)
new_board.fullmove_count = int(fullmove_clock)
return new_board
def get_piece(self, x, y):
assert(0 <= x < 8 and 0 <= y < 8)
# X and Y should be in the range 0-7 inclusive.
# a1 is bottom-left, bit zero.
# h8 is the top-right, bit 63.
idx = (1 << x+y*8)
for i in range(1, PIECE_TYPES):
if self.boards[i] & idx:
return Piece(i)
return None
def set_piece(self, x, y, piece: Piece, clear_previous=True):
assert(x >= 0 and x < 8 and y >= 0 and y < 8)
idx = (1 << x+y*8)
if clear_previous:
self.clear_piece(x, y)
self.boards[int(piece)] |= idx
def clear_piece(self, x, y):
assert (x >= 0 and x < 8 and y >= 0 and y < 8)
idx = (1 << x + y * 8)
clear_mask = 0xFFFF_FFFF_FFFF_FFFF & ~idx
for i in range(0, PIECE_TYPES):
self.boards[i] &= clear_mask
|