-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboard.go
313 lines (271 loc) · 6.8 KB
/
board.go
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
package main
import (
"fmt"
"math/bits"
"strings"
)
// Square represents a square on the board in little-endian rank-file order (a1 = 0, a2 = 1, h8 = 63).
// Behavior is undefined for values outside of [0, 64).
type Square byte
// Rank returns the rank of s in the range [0, 7).
func (s Square) Rank() byte { return byte(s >> 3) }
// File returns the file of s in the range [0, 7).
func (s Square) File() byte { return byte(s & 7) }
// Diagonal returns the southwest-northeast diagonal of s, from 0 (h1) to 14 (a8).
func (s Square) Diagonal() byte { return 7 + s.Rank() - s.File() }
// AntiDiagonal returns the northwest-southeast anti-diagonal of s, from 0 (a1) to 14 (h8).
func (s Square) AntiDiagonal() byte { return s.Rank() + s.File() }
// Board returns a Board in which only the bit corresponding to s is set.
func (s Square) Board() Board { return 1 << s }
//go:generate stringer -type=Square
const (
a1 Square = iota
b1
c1
d1
e1
f1
g1
h1
a2
b2
c2
d2
e2
f2
g2
h2
a3
b3
c3
d3
e3
f3
g3
h3
a4
b4
c4
d4
e4
f4
g4
h4
a5
b5
c5
d5
e5
f5
g5
h5
a6
b6
c6
d6
e6
f6
g6
h6
a7
b7
c7
d7
e7
f7
g7
h7
a8
b8
c8
d8
e8
f8
g8
h8
)
// Board represents a bitboard that describes some aspect of a chess board or position.
// Every bit corresponds to one square on the board.
type Board uint64
const (
AFile Board = 0x0101010101010101 << iota
BFile
CFile
DFile
EFile
FFile
GFile
HFile
)
const (
Rank1 Board = 0xff << (8 * iota)
Rank2
Rank3
Rank4
Rank5
Rank6
Rank7
Rank8
)
const (
DarkSquares Board = 0xaa55aa55aa55aa55
LightSquares Board = ^DarkSquares
LongDiagonal Board = 0x8040201008040201 // a1 to h8
LongAntiDiagonal Board = 0x0102040810204080 // h1 to a8
)
var (
Files = []Board{AFile, BFile, CFile, DFile, EFile, FFile, GFile, HFile}
Ranks = []Board{Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8}
fileLetters = []string{"a", "b", "c", "d", "e", "f", "g", "h"}
rankNumbers = []string{"1", "2", "3", "4", "5", "6", "7", "8"}
// CastleEmptySquares describes the squares that must be empty when castling.
CastleEmptySquares = [][]Board{
{
(BFile | CFile | DFile) & Rank1,
(FFile | GFile) & Rank1,
},
{
(BFile | CFile | DFile) & Rank8,
(FFile | GFile) & Rank8,
},
}
// CastleKingSquares describes the squares that the king occupies during castling.
CastleKingSquares = [][]Board{
{
(CFile | DFile | EFile) & Rank1,
(EFile | FFile | GFile) & Rank1,
},
{
(CFile | DFile | EFile) & Rank8,
(EFile | FFile | GFile) & Rank8,
},
}
)
// Color represents the color of a chess piece. A piece's color determines when it can move and whether it can move to an occupied Square.
type Color byte
//go:generate stringer -type=Color
const (
White Color = iota
Black
)
// Piece is one of the six types of chess pieces, or an auxiliary value corresponding to none or all of them.
type Piece byte
//go:generate stringer -type=Piece
const (
// None is used in Move's CapturePiece and PromotePiece fields to denote that a move is not a capture or a promotion.
None Piece = iota
Pawn
Knight
Bishop
Rook
Queen
King
// All is updated in a Position's b elements to track each side's pieces regardless of type.
// This is an optimization for the move generation functions.
All
)
var pieceLetter = []string{"", "P", "N", "B", "R", "Q", "K"}
// Side represents the two sides of the board to which the king can castle.
type Side byte
const (
QS Side = iota
KS
)
// Position contains all information necessary to specify the current state of a game.
type Position struct {
// Castle describes castling rights.
// Castle[c][side] indicates whether the c retains the option of castling to side, if it is legal to do so.
Castle [2][2]bool
// ep is the unique square, if any, to which an en passant capture can be played by the side to move.
// Valid values are in the ranges [16, 24) (the 3rd rank, with Black to move following a White pawn push)
// and [40, 48) (the 6th rank, with White to move following a Black pawn push).
// A value of 0 indicates that there is no en passant opportunity. Behavior is undefined for any other value.
ep Square
// ToMove is the side to move.
ToMove Color
// HalfMove is the number of half-moves (plies) since the most recent capture or pawn move,
// for use in determining eligibility for a draw under the fifty-move rule.
HalfMove int
// FullMove is the number of the current move for the side to play.
// This begins at 1 and increments after each Black move.
FullMove int
// KingSquare is the Square of the indexed Color's king.
KingSquare [2]Square
// b contains Boards describing the positions of all White and Black pieces.
// The set bits of b[c][p] give the locations of all pieces of color c and type p.
// b[c][All] gives the union of all pieces of color c.
b [2][8]Board
// z is the position's Zobrist bitstring.
z Zobrist
}
// InitialPositionFEN is the FEN record of the initial position of the pieces.
var InitialPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
// InitialPosition is the initial Position.
var InitialPosition, _ = ParseFEN(InitialPositionFEN)
// Opp returns the Color of the player who does not have the move.
func (pos Position) Opp() Color { return pos.ToMove ^ 1 }
// PieceOn returns the Color and Piece type of the piece, if any, on s.
// It returns a Piece of None if the Square is empty.
func (pos Position) PieceOn(s Square) (c Color, p Piece) {
b := s.Board()
switch {
case pos.b[White][All]&b != 0:
c = White
case pos.b[Black][All]&b != 0:
c = Black
default:
return
}
for _, p = range []Piece{Pawn, Knight, Bishop, Rook, Queen, King} {
if pos.b[c][p]&b != 0 {
return
}
}
// Error if neither return statement above is utilized
panic(fmt.Sprintf("PieceOn: invalid piece on square %v in position %+v", s, pos))
return
}
// LS1B returns a Board consisting of only the least significant 1 bit of b.
func LS1B(b Board) Board {
if b == 0 {
panic("LS1B: Board is empty")
}
return b & -b
}
// LS1BIndex returns the position of the least significant 1 bit of b.
func LS1BIndex(b Board) Square {
return Square(bits.TrailingZeros64(uint64(b)))
}
// ResetLS1B returns a Board consisting of all set bits of b except for the least significant one.
func ResetLS1B(b Board) Board {
if b == 0 {
panic("ResetLS1B: Board is empty")
}
return b & (b - 1)
}
// PopCount returns the number of 1 bits in b.
func PopCount(b Board) int {
return bits.OnesCount64(uint64(b))
}
func pieceChar(c Color, p Piece) string {
s := pieceLetter[p]
if c == Black {
s = strings.ToLower(s)
}
return s
}
func (pos Position) String() string {
var s string
for r := 7; r >= 0; r-- {
for f := 0; f < 8; f++ {
if c, p := pos.PieceOn(Square(8*r + f)); p != None {
s += pieceChar(c, p)
} else {
s += "."
}
s += " "
}
s += "\n"
}
return s
}