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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
| require "./piece"
require "./const"
require "./zobrist"
module Fork
CASTLE_UPDATE = [
0b1101, 0b1111, 0b1111, 0b1111, 0b1100, 0b1111, 0b1111, 0b1110,
0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111,
0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111,
0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111,
0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111,
0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111,
0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111,
0b0111, 0b1111, 0b1111, 0b1111, 0b0011, 0b1111, 0b1111, 0b1011,
]
WHITE_KING_CASTLE = 0b0001
WHITE_QUEEN_CASTLE = 0b0010
BLACK_KING_CASTLE = 0b0100
BLACK_QUEEN_CASTLE = 0b1000
STARTING_BOARD = Board.new("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
EMPTY_BOARD = Board.new("8/8/8/8/8/8/8/8 w KQkq - 0 1")
def self.occupy(player)
player[0] | player[1] | player[2] | player[3] | player[4] | player[5]
end
struct Board
getter white : StaticArray(UInt64, 6) = StaticArray(UInt64, 6).new(0)
getter black : StaticArray(UInt64, 6) = StaticArray(UInt64, 6).new(0)
getter side = WHITE
getter castle : UInt32 = 0
getter en_passant : UInt64 = 0
getter hash : UInt64 = 0
def initialize(fen : String)
split = fen.split
pieces = split.first
row = 7
col = 0
pieces.each_char do |c|
if c.ascii_number?
col += c.to_i
next
elsif c == '/'
row -= 1
col = 0
next
end
bit = 1u64 << (row * 8 + col)
case c
when 'P'
white[PAWN] = white[PAWN] ^ bit
when 'N'
white[KNIGHT] = white[KNIGHT] ^ bit
when 'B'
white[BISHOP] = white[BISHOP] ^ bit
when 'R'
white[ROOK] = white[ROOK] ^ bit
when 'Q'
white[QUEEN] = white[QUEEN] ^ bit
when 'K'
white[KING] = white[KING] ^ bit
when 'p'
black[PAWN] = black[PAWN] ^ bit
when 'n'
black[KNIGHT] = black[KNIGHT] ^ bit
when 'b'
black[BISHOP] = black[BISHOP] ^ bit
when 'r'
black[ROOK] = black[ROOK] ^ bit
when 'q'
black[QUEEN] = black[QUEEN] ^ bit
when 'k'
black[KING] = black[KING] ^ bit
end
col += 1
end
if split[1] == "w"
@side = WHITE
else
@side = BLACK
end
split[2].each_char do |c|
case c
when 'K'
@castle |= WHITE_KING_CASTLE
when 'Q'
@castle |= WHITE_QUEEN_CASTLE
when 'k'
@castle |= BLACK_KING_CASTLE
when 'q'
@castle |= BLACK_QUEEN_CASTLE
else
end
end
double_push = split[3][0]
unless double_push == '-'
@en_passant = (double_push - 'a').to_u64
end
init_hash
end
def init_hash
@hash = @hash ^ Zobrist::INSTANCE.castle[@castle]
SIDES.each do |side|
TYPES.each do |type|
Fork.iterate_bitboard(player(side)[type]) do |index|
@hash = @hash ^ Zobrist::INSTANCE.pieces[side][type][index]
end
end
end
end
def player(side)
side == WHITE ? white : black
end
def piece_at(row, col)
bit = 1u64 << (row * 8 + col)
SIDES.each do |side|
TYPES.each do |type|
if player(side)[type] & bit != 0
return Piece.new(type, side, row * 8 + col)
end
end
end
nil
end
def piece_at(index)
return nil if index.nil?
row, col = index.divmod(8)
piece_at(row, col)
end
def print
str = ""
7.downto(0) do |row|
0.upto(7) do |col|
piece = piece_at(row, col)
c = piece.try &.to_c || '.'
str += c
end
str += "\n"
end
puts str
end
def attacked?(index, side)
player = side == WHITE ? white : black
enemy = side == WHITE ? black : white
player_occ = Fork.occupy(player)
enemy_occ = Fork.occupy(enemy)
p = side == WHITE ? Moves::WHITE_PAWN_ATTACKS[index] : Moves::BLACK_PAWN_ATTACKS[index]
return true if enemy[PAWN] & p != 0
return true if enemy[KNIGHT] & Moves::KNIGHT_MOVES[index] != 0
b = Moves.bishop_moves(index, player_occ, enemy_occ)
return true if enemy[BISHOP] & b != 0
r = Moves.rook_moves(index, player_occ, enemy_occ)
return true if enemy[ROOK] & r != 0
return true if enemy[QUEEN] & (r | b) != 0
return true if enemy[KING] & Moves::KING_MOVES[index] != 0
false
end
def checked?(side)
king_pos = Fork.lsb(player(side)[KING])
attacked?(king_pos, side)
end
def make_move(move : Fork::Move)
# start_bitboard = (1u64 << start_index)
end_bitboard = (1u64 << move.to)
neg_end_bitboard = ~end_bitboard
king_pos = nil
if @side == WHITE
white[move.type] = white[move.type] ^ (1u64 << move.from)
@hash = @hash ^ Zobrist::INSTANCE.pieces[WHITE][move.type][move.from]
if move.promote_to == 0
white[move.type] = white[move.type] | end_bitboard
@hash = @hash ^ Zobrist::INSTANCE.pieces[WHITE][move.type][move.to]
else
white[move.promote_to] = white[move.promote_to] | end_bitboard
@hash = @hash ^ Zobrist::INSTANCE.pieces[WHITE][move.promote_to][move.to]
end
king_pos = Fork.lsb(white[KING])
else
black[move.type] = black[move.type] ^ (1u64 << move.from)
@hash = @hash ^ Zobrist::INSTANCE.pieces[BLACK][move.type][move.from]
if move.promote_to == 0
black[move.type] = black[move.type] | end_bitboard
@hash = @hash ^ Zobrist::INSTANCE.pieces[BLACK][move.type][move.to]
else
black[move.promote_to] = black[move.promote_to] | end_bitboard
@hash = @hash ^ Zobrist::INSTANCE.pieces[BLACK][move.promote_to][move.to]
end
king_pos = Fork.lsb(black[KING])
end
@hash = @hash ^ Zobrist::INSTANCE.castle[@castle]
if move.castle != 0
if move.castle == WHITE_KING_CASTLE
white[ROOK] = white[ROOK] ^ (H1_BIT | F1_BIT)
elsif move.castle == WHITE_QUEEN_CASTLE
white[ROOK] = white[ROOK] ^ (A1_BIT | D1_BIT)
elsif move.castle == BLACK_KING_CASTLE
black[ROOK] = black[ROOK] ^ (H8_BIT | F8_BIT)
elsif move.castle == BLACK_QUEEN_CASTLE
black[ROOK] = black[ROOK] ^ (A8_BIT | D8_BIT)
end
end
@castle = @castle & CASTLE_UPDATE[move.from] & CASTLE_UPDATE[move.to]
@hash = @hash ^ Zobrist::INSTANCE.castle[@castle]
if move.en_passant
if @side == WHITE
black[PAWN] = black[PAWN] ^ (1u64 << move.to - 8)
else
white[PAWN] = white[PAWN] ^ (1u64 << move.to + 8)
end
end
if @en_passant != 0
col = Fork.lsb(@en_passant) % 8
@hash = @hash ^ Zobrist::INSTANCE.en_passant[col]
end
if move.double_push
@en_passant = Moves::EN_PASSANT_PHANTOM[move.from]
col = move.from % 8
@hash = @hash ^ Zobrist::INSTANCE.en_passant[col]
else
@en_passant = 0
end
TYPES.each do |t|
if @side == WHITE
black[t] = black[t] & neg_end_bitboard
else
white[t] = white[t] & neg_end_bitboard
end
end
legal = king_pos < 64 && !attacked?(king_pos, @side)
@side = Fork.enemy(side)
@hash = @hash ^ Zobrist::INSTANCE.side
legal
end
end
end
|