@@ -37,6 +37,8 @@ type Board struct {
3737 whiteKingPosition Square
3838 blackKingPosition Square
3939 status int
40+ zobristTable * ZobristTable
41+ currentHash int64
4042}
4143
4244// NewBoard creates a new chessboard from given fen
@@ -47,6 +49,9 @@ func NewBoard(fen string) *Board {
4749 fmt .Printf ("invalid FEN: \" %s\" \n " , fen )
4850 }
4951
52+ b .zobristTable = NewZobristTable ()
53+ b .currentHash = b .generateHash ()
54+
5055 return b
5156}
5257
@@ -163,10 +168,12 @@ func (b *Board) MakeMove(m Move) {
163168 }
164169
165170 b .sideToMove = opponent (b .sideToMove )
171+ b .ply ++
166172
173+ historyItem .hash = b .currentHash
167174 b .history = append (b .history , historyItem )
168- b .ply ++
169175
176+ b .updateHash (m )
170177}
171178
172179// UndoMove undoes the last move on the board
@@ -187,6 +194,9 @@ func (b *Board) UndoMove() {
187194
188195 m := historyItem .move
189196
197+ // XXX: might to be done at the end?
198+ b .updateHash (m )
199+
190200 switch {
191201 case m .Special == moveOrdinary || m .Special == movePromotion :
192202 b .data [m .To ] = m .Content
@@ -245,10 +255,75 @@ func (b *Board) isEmpty(squares ...Square) bool {
245255
246256func (b * Board ) repetitions () int {
247257 r := 0
248-
258+ first := len (b .history ) - b .halfMoveClock
259+ if first >= 0 {
260+ for i := first ; i < len (b .history )- 1 ; i ++ {
261+ if b .history [i ].hash == b .currentHash {
262+ r ++
263+ }
264+ }
265+ }
249266 return r
250267}
251268
269+ func (b * Board ) updateHash (m Move ) {
270+ key := b .currentHash
271+ color := 0
272+ if b .sideToMove == Black {
273+ color = 1
274+ }
275+ piece := abs (m .MovedPiece ) - 1
276+
277+ key ^= b .zobristTable .hashPieces [piece ][color ][int8 (m .From )]
278+ key ^= b .zobristTable .hashPieces [piece ][color ][int8 (m .To )]
279+
280+ if m .Content != Empty {
281+ key ^= b .zobristTable .hashPieces [abs (m .Content )- 1 ][color ][int8 (m .To )]
282+ }
283+
284+ switch m .Special {
285+ case moveCastelingLong :
286+ if color == 0 {
287+ key ^= b .zobristTable .hashCastelingWhite [castleLong ]
288+ } else {
289+ key ^= b .zobristTable .hashCastelingBlack [castleLong ]
290+ }
291+ case moveCastelingShort :
292+ if color == 0 {
293+ key ^= b .zobristTable .hashCastelingWhite [castleShort ]
294+ } else {
295+ key ^= b .zobristTable .hashCastelingBlack [castleLong ]
296+ }
297+ case moveEnPassant :
298+ key ^= b .zobristTable .hashEnPassant [int8 (m .To )- int8 (m .MovedPiece )* nextRank ]
299+ case movePromotion :
300+ key ^= b .zobristTable .hashPromotion [abs (m .Promoted )- 1 ]
301+ }
302+
303+ b .currentHash = key
304+ }
305+
306+ func (b * Board ) generateHash () int64 {
307+ key := int64 (0 )
308+
309+ for square := int8 (0 ); square < boardSize ; square ++ {
310+ piece := b .data [square ]
311+ if piece > Empty {
312+ key ^= b .zobristTable .hashPieces [piece - 1 ][0 ][square ]
313+ } else if piece < Empty {
314+ key ^= b .zobristTable .hashPieces [- piece - 1 ][1 ][square ]
315+ }
316+ }
317+
318+ key ^= b .zobristTable .hashCastelingWhite [b .whiteCastle ]
319+ key ^= b .zobristTable .hashCastelingBlack [b .blackCastle ]
320+ if b .sideToMove == Black {
321+ key ^= b .zobristTable .hashSide
322+ }
323+
324+ return key
325+ }
326+
252327func (b * Board ) String () string {
253328 files := " a b c d e f g h"
254329 str := fmt .Sprintf ("%s\n " , files )
0 commit comments