Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 pmbaty 1
/*
2
    Texel - A UCI chess engine.
3
    Copyright (C) 2012-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
4
 
5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9
 
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
 
15
    You should have received a copy of the GNU General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
 
19
/*
20
 * position.hpp
21
 *
22
 *  Created on: Feb 25, 2012
23
 *      Author: petero
24
 */
25
 
26
#ifndef POSITION_HPP_
27
#define POSITION_HPP_
28
 
29
#include <iosfwd>
30
 
31
#include "move.hpp"
32
#include "undoInfo.hpp"
33
#include "bitBoard.hpp"
34
#include "piece.hpp"
35
#include "material.hpp"
36
#include <algorithm>
37
#include <iostream>
38
 
39
/**
40
 * Stores the state of a chess position.
41
 * All required state is stored, except for all previous positions
42
 * since the last capture or pawn move. That state is only needed
43
 * for three-fold repetition draw detection, and is better stored
44
 * in a separate hash table.
45
 */
46
class Position {
47
public:
48
    /** Bit definitions for the castleMask bit mask. */
49
    static const int A1_CASTLE = 0; /** White long castle. */
50
    static const int H1_CASTLE = 1; /** White short castle. */
51
    static const int A8_CASTLE = 2; /** Black long castle. */
52
    static const int H8_CASTLE = 3; /** Black short castle. */
53
 
54
    /** Initialize board to empty position. */
55
    Position();
56
 
57
    bool equals(const Position& other) const;
58
 
59
    /**
60
     * Return Zobrist hash value for the current position.
61
     * Everything except the move counters are included in the hash value.
62
     */
63
    U64 zobristHash() const;
64
    U64 pawnZobristHash() const;
65
    U64 kingZobristHash() const;
66
 
67
    /** Zobrist hash including the halfMove clock.
68
     *  Only large halfMove clock values affect the hash. */
69
    U64 historyHash() const;
70
    /** Hash including halfMoveClock, to avoid opening book cycles. */
71
    U64 bookHash() const;
72
 
73
    /** Compute zobrist hash for position after "move" has been made.
74
     * May be incorrect in some cases, intended for prefetch. */
75
    U64 hashAfterMove(const Move& move) const;
76
 
77
    /** Return the material identifier. */
78
    int materialId() const;
79
 
80
    /**
81
     * Decide if two positions are equal in the sense of the draw by repetition rule.
82
     * @return True if positions are equal, false otherwise.
83
     */
84
    bool drawRuleEquals(Position other) const;
85
 
86
    bool isWhiteMove() const;
87
 
88
    void setWhiteMove(bool whiteMove);
89
 
90
    /** Return piece occupying a square. */
91
    int getPiece(int square) const;
92
 
93
    /** Set a square to a piece value. */
94
    void setPiece(int square, int piece);
95
 
96
    /**
97
     * Set a square to a piece value.
98
     * Special version that only updates enough of the state for the SEE function to be happy.
99
     */
100
    void setSEEPiece(int square, int piece);
101
 
102
    /** Return true if white long castling right has not been lost. */
103
    bool a1Castle() const;
104
 
105
    /** Return true if white short castling right has not been lost. */
106
    bool h1Castle() const;
107
 
108
    /** Return true if black long castling right has not been lost. */
109
    bool a8Castle() const;
110
 
111
    /** Return true if black short castling right has not been lost. */
112
    bool h8Castle() const;
113
 
114
    /** Bitmask describing castling rights. */
115
    int getCastleMask() const;
116
    void setCastleMask(int castleMask);
117
 
118
    /** En passant square, or -1 if no en passant possible. */
119
    int getEpSquare() const;
120
 
121
    void setEpSquare(int epSquare);
122
 
123
    int getKingSq(bool white) const;
124
 
125
    /** Apply a move to the current position. */
126
    void makeMove(const Move& move, UndoInfo& ui);
127
 
128
    void unMakeMove(const Move& move, const UndoInfo& ui);
129
 
130
    /** Special make move functions used by MoveGen::isLegal(). Does not update all data members. */
131
    void makeMoveB(const Move& move, UndoInfo& ui);
132
    void unMakeMoveB(const Move& move, const UndoInfo& ui);
133
    void setPieceB(int square, int piece);
134
 
135
    /**
136
     * Apply a move to the current position.
137
     * Special version that only updates enough of the state for the SEE function to be happy.
138
     */
139
    void makeSEEMove(const Move& move, UndoInfo& ui);
140
 
141
    void unMakeSEEMove(const Move& move, const UndoInfo& ui);
142
 
143
    int getFullMoveCounter() const;
144
    void setFullMoveCounter(int fm);
145
    int getHalfMoveClock() const;
146
    void setHalfMoveClock(int hm);
147
 
148
    /** Return incrementally updated piece square table score for middle game and endgame. */
149
    int psScore1(int piece) const;
150
    int psScore2(int piece) const;
151
 
152
    /** BitBoard for all squares occupied by a piece type. */
153
    U64 pieceTypeBB(Piece::Type piece) const;
154
    /** BitBoard for all squares occupied by several piece types. */
155
    template <typename Piece0, typename... Pieces> U64 pieceTypeBB(Piece0 piece0, Pieces... pieces) const;
156
 
157
    /** BitBoard for all squares occupied by white pieces. */
158
    U64 whiteBB() const;
159
    /** BitBoard for all squares occupied by black pieces. */
160
    U64 blackBB() const;
161
    /** BitBoard for all squares occupied by white or black pieces. */
162
    U64 colorBB(int wtm) const;
163
 
164
    /** BitBoard for all squares occupied by white and black pieces. */
165
    U64 occupiedBB() const;
166
 
167
    int wKingSq() const;
168
    int bKingSq() const;
169
 
170
    /** Total white/black material value. */
171
    int wMtrl() const;
172
    int bMtrl() const;
173
    /** White/black material value for all pawns. */
174
    int wMtrlPawns() const;
175
    int bMtrlPawns() const;
176
 
177
 
178
    /** Return index in squares[] vector corresponding to (x,y). */
179
    static int getSquare(int x, int y);
180
 
181
    /** Return x position (file) corresponding to a square. */
182
    static int getX(int square);
183
 
184
    /** Return y position (rank) corresponding to a square. */
185
    static int getY(int square);
186
 
187
    /** Return getSquare(getX(square),7-getY(square)). */
188
    static int mirrorY(int square);
189
 
190
    /** Return true if (x,y) is a dark square. */
191
    static bool darkSquare(int x, int y);
192
 
193
    /** Compute the Zobrist hash value non-incrementally. Only useful for testing. */
194
    U64 computeZobristHash();
195
 
196
    /** Initialize static data. */
197
    static void staticInitialize();
198
 
199
    /** Get hash key for a piece at a square. */
200
    static U64 getHashKey(int piece, int square);
201
 
202
 
203
    /** Serialization. Used by tree logging code. */
204
    struct SerializeData {
205
        U64 v[5];
206
    };
207
    void serialize(SerializeData& data) const;
208
    void deSerialize(const SerializeData& data);
209
 
210
private:
211
    /** Move a non-pawn piece to an empty square. */
212
    void movePieceNotPawn(int from, int to);
213
    void movePieceNotPawnB(int from, int to);
214
 
215
    void removeCastleRights(int square);
216
 
217
    static U64 getRandomHashVal(int rndNo);
218
 
219
 
220
    int wKingSq_, bKingSq_;  // Cached king positions
221
    int wMtrl_;              // Total value of all white pieces and pawns
222
    int bMtrl_;              // Total value of all black pieces and pawns
223
    int wMtrlPawns_;         // Total value of all white pawns
224
    int bMtrlPawns_;         // Total value of all black pawns
225
 
226
    int squares[64];
227
 
228
    // Piece square table scores
229
    short psScore1_[Piece::nPieceTypes];
230
    short psScore2_[Piece::nPieceTypes];
231
 
232
    // Bitboards
233
    U64 pieceTypeBB_[Piece::nPieceTypes];
234
    U64 whiteBB_, blackBB_;
235
 
236
    bool whiteMove;
237
 
238
    /** Number of half-moves since last 50-move reset. */
239
    int halfMoveClock;
240
 
241
    /** Game move number, starting from 1. */
242
    int fullMoveCounter;
243
 
244
    int castleMask;
245
    int epSquare;
246
 
247
    U64 hashKey;           // Cached Zobrist hash key
248
    U64 pHashKey;          // Cached Zobrist pawn hash key
249
    MatId matId;           // Cached material identifier
250
 
251
    static U64 psHashKeys[Piece::nPieceTypes][64];    // [piece][square]
252
 
253
    static U64 whiteHashKey;
254
    static U64 castleHashKeys[16];   // [castleMask]
255
    static U64 epHashKeys[9];        // [epFile + 1] (epFile==-1 for no ep)
256
    static U64 moveCntKeys[101];     // [min(halfMoveClock, 100)]
257
 
258
    static const U64 zobristRndKeys[];
259
};
260
 
261
/** For debugging. */
262
std::ostream& operator<<(std::ostream& os, const Position& pos);
263
 
264
inline bool
265
Position::equals(const Position& other) const {
266
    if (!drawRuleEquals(other))
267
        return false;
268
    if (halfMoveClock != other.halfMoveClock)
269
        return false;
270
    if (fullMoveCounter != other.fullMoveCounter)
271
        return false;
272
    if (hashKey != other.hashKey)
273
        return false;
274
    if (pHashKey != other.pHashKey)
275
        return false;
276
    if (matId() != other.matId())
277
        return false;
278
    return true;
279
}
280
 
281
inline U64
282
Position::zobristHash() const {
283
    return hashKey;
284
}
285
 
286
inline U64
287
Position::pawnZobristHash() const {
288
    return pHashKey;
289
}
290
 
291
inline U64
292
Position::kingZobristHash() const {
293
    return psHashKeys[Piece::WKING][wKingSq()] ^
294
           psHashKeys[Piece::BKING][bKingSq()];
295
}
296
 
297
inline U64
298
Position::historyHash() const {
299
    U64 ret = hashKey;
300
    if (halfMoveClock >= 40) {
301
        if (halfMoveClock < 80)
302
            ret ^= moveCntKeys[halfMoveClock / 10];
303
        else
304
            ret ^= moveCntKeys[std::min(halfMoveClock, 100)];
305
    }
306
    return ret;
307
}
308
 
309
inline U64
310
Position::bookHash() const {
311
    U64 ret = hashKey;
312
    ret ^= moveCntKeys[std::min(halfMoveClock, 100)];
313
    return ret;
314
}
315
 
316
inline int
317
Position::materialId() const {
318
    return matId();
319
}
320
 
321
inline bool
322
Position::drawRuleEquals(Position other) const {
323
    for (int i = 0; i < 64; i++)
324
        if (squares[i] != other.squares[i])
325
            return false;
326
    if (whiteMove != other.whiteMove)
327
        return false;
328
    if (castleMask != other.castleMask)
329
        return false;
330
    if (epSquare != other.epSquare)
331
        return false;
332
    return true;
333
}
334
 
335
inline bool
336
Position::isWhiteMove() const {
337
    return whiteMove;
338
}
339
 
340
inline void
341
Position::setWhiteMove(bool whiteMove) {
342
    if (whiteMove != this->whiteMove) {
343
        hashKey ^= whiteHashKey;
344
        this->whiteMove = whiteMove;
345
    }
346
}
347
 
348
inline int
349
Position::getPiece(int square) const {
350
    return squares[square];
351
}
352
 
353
inline void
354
Position::setSEEPiece(int square, int piece) {
355
    int removedPiece = squares[square];
356
 
357
    // Update board
358
    squares[square] = piece;
359
 
360
    // Update bitboards
361
    U64 sqMask = 1ULL << square;
362
    pieceTypeBB_[removedPiece] &= ~sqMask;
363
    pieceTypeBB_[piece] |= sqMask;
364
    if (removedPiece != Piece::EMPTY) {
365
        if (Piece::isWhite(removedPiece))
366
            whiteBB_ &= ~sqMask;
367
        else
368
            blackBB_ &= ~sqMask;
369
    }
370
    if (piece != Piece::EMPTY) {
371
        if (Piece::isWhite(piece))
372
            whiteBB_ |= sqMask;
373
        else
374
            blackBB_ |= sqMask;
375
    }
376
}
377
 
378
inline bool
379
Position::a1Castle() const {
380
    return (castleMask & (1 << A1_CASTLE)) != 0;
381
}
382
 
383
inline bool
384
Position::h1Castle() const {
385
    return (castleMask & (1 << H1_CASTLE)) != 0;
386
}
387
 
388
inline bool
389
Position::a8Castle() const {
390
    return (castleMask & (1 << A8_CASTLE)) != 0;
391
}
392
 
393
inline bool
394
Position::h8Castle() const {
395
    return (castleMask & (1 << H8_CASTLE)) != 0;
396
}
397
 
398
inline int
399
Position::getCastleMask() const {
400
    return castleMask;
401
}
402
 
403
inline void
404
Position::setCastleMask(int castleMask) {
405
    hashKey ^= castleHashKeys[this->castleMask];
406
    hashKey ^= castleHashKeys[castleMask];
407
    this->castleMask = castleMask;
408
}
409
 
410
inline int
411
Position::getEpSquare() const {
412
    return epSquare;
413
}
414
 
415
inline void
416
Position::setEpSquare(int epSquare) {
417
    if (this->epSquare != epSquare) {
418
        hashKey ^= epHashKeys[(this->epSquare >= 0) ? getX(this->epSquare) + 1 : 0];
419
        hashKey ^= epHashKeys[(epSquare >= 0) ? getX(epSquare) + 1 : 0];
420
        this->epSquare = epSquare;
421
    }
422
}
423
 
424
inline int
425
Position::getKingSq(bool white) const {
426
    return white ? wKingSq() : bKingSq();
427
}
428
 
429
inline void
430
Position::unMakeMove(const Move& move, const UndoInfo& ui) {
431
    hashKey ^= whiteHashKey;
432
    whiteMove = !whiteMove;
433
    int p = squares[move.to()];
434
    setPiece(move.from(), p);
435
    setPiece(move.to(), ui.capturedPiece);
436
    setCastleMask(ui.castleMask);
437
    setEpSquare(ui.epSquare);
438
    halfMoveClock = ui.halfMoveClock;
439
    bool wtm = whiteMove;
440
    if (move.promoteTo() != Piece::EMPTY) {
441
        p = wtm ? Piece::WPAWN : Piece::BPAWN;
442
        setPiece(move.from(), p);
443
    }
444
    if (!wtm)
445
        fullMoveCounter--;
446
 
447
    // Handle castling
448
    int king = wtm ? Piece::WKING : Piece::BKING;
449
    if (p == king) {
450
        int k0 = move.from();
451
        if (move.to() == k0 + 2) { // O-O
452
            movePieceNotPawn(k0 + 1, k0 + 3);
453
        } else if (move.to() == k0 - 2) { // O-O-O
454
            movePieceNotPawn(k0 - 1, k0 - 4);
455
        }
456
    }
457
 
458
    // Handle en passant
459
    if (move.to() == epSquare) {
460
        if (p == Piece::WPAWN) {
461
            setPiece(move.to() - 8, Piece::BPAWN);
462
        } else if (p == Piece::BPAWN) {
463
            setPiece(move.to() + 8, Piece::WPAWN);
464
        }
465
    }
466
}
467
 
468
inline void
469
Position::unMakeMoveB(const Move& move, const UndoInfo& ui) {
470
    int p = squares[move.to()];
471
    setPieceB(move.from(), p);
472
    setPieceB(move.to(), ui.capturedPiece);
473
    bool wtm = whiteMove;
474
    if (move.promoteTo() != Piece::EMPTY) {
475
        p = wtm ? Piece::WPAWN : Piece::BPAWN;
476
        setPieceB(move.from(), p);
477
    }
478
 
479
    // Handle castling
480
    int king = wtm ? Piece::WKING : Piece::BKING;
481
    if (p == king) {
482
        int k0 = move.from();
483
        if (move.to() == k0 + 2) { // O-O
484
            movePieceNotPawnB(k0 + 1, k0 + 3);
485
        } else if (move.to() == k0 - 2) { // O-O-O
486
            movePieceNotPawnB(k0 - 1, k0 - 4);
487
        }
488
    }
489
 
490
    // Handle en passant
491
    if (move.to() == epSquare) {
492
        if (p == Piece::WPAWN) {
493
            setPieceB(move.to() - 8, Piece::BPAWN);
494
        } else if (p == Piece::BPAWN) {
495
            setPieceB(move.to() + 8, Piece::WPAWN);
496
        }
497
    }
498
}
499
 
500
inline void
501
Position::setPieceB(int square, int piece) {
502
    int removedPiece = squares[square];
503
    squares[square] = piece;
504
 
505
    // Update bitboards
506
    const U64 sqMask = 1ULL << square;
507
    pieceTypeBB_[removedPiece] &= ~sqMask;
508
    pieceTypeBB_[piece] |= sqMask;
509
 
510
    if (removedPiece != Piece::EMPTY) {
511
        if (Piece::isWhite(removedPiece))
512
            whiteBB_ &= ~sqMask;
513
        else
514
            blackBB_ &= ~sqMask;
515
    }
516
 
517
    if (piece != Piece::EMPTY) {
518
        if (Piece::isWhite(piece)) {
519
            whiteBB_ |= sqMask;
520
            if (piece == Piece::WKING)
521
                wKingSq_ = square;
522
        } else {
523
            blackBB_ |= sqMask;
524
            if (piece == Piece::BKING)
525
                bKingSq_ = square;
526
        }
527
    }
528
}
529
 
530
inline void
531
Position::movePieceNotPawnB(int from, int to) {
532
    const int piece = squares[from];
533
 
534
    squares[from] = Piece::EMPTY;
535
    squares[to] = piece;
536
 
537
    const U64 sqMaskF = 1ULL << from;
538
    const U64 sqMaskT = 1ULL << to;
539
    pieceTypeBB_[piece] &= ~sqMaskF;
540
    pieceTypeBB_[piece] |= sqMaskT;
541
    if (Piece::isWhite(piece)) {
542
        whiteBB_ &= ~sqMaskF;
543
        whiteBB_ |= sqMaskT;
544
        if (piece == Piece::WKING)
545
            wKingSq_ = to;
546
    } else {
547
        blackBB_ &= ~sqMaskF;
548
        blackBB_ |= sqMaskT;
549
        if (piece == Piece::BKING)
550
            bKingSq_ = to;
551
    }
552
}
553
 
554
inline void
555
Position::makeSEEMove(const Move& move, UndoInfo& ui) {
556
    ui.capturedPiece = squares[move.to()];
557
    int p = squares[move.from()];
558
 
559
    // Handle en passant
560
    if (move.to() == epSquare) {
561
        if (p == Piece::WPAWN) {
562
            setSEEPiece(move.to() - 8, Piece::EMPTY);
563
        } else if (p == Piece::BPAWN) {
564
            setSEEPiece(move.to() + 8, Piece::EMPTY);
565
        }
566
    }
567
 
568
    // Perform move
569
    setSEEPiece(move.from(), Piece::EMPTY);
570
    setSEEPiece(move.to(), p);
571
    whiteMove = !whiteMove;
572
}
573
 
574
inline void
575
Position::unMakeSEEMove(const Move& move, const UndoInfo& ui) {
576
    whiteMove = !whiteMove;
577
    int p = squares[move.to()];
578
    setSEEPiece(move.from(), p);
579
    setSEEPiece(move.to(), ui.capturedPiece);
580
 
581
    // Handle en passant
582
    if (move.to() == epSquare) {
583
        if (p == Piece::WPAWN) {
584
            setSEEPiece(move.to() - 8, Piece::BPAWN);
585
        } else if (p == Piece::BPAWN) {
586
            setSEEPiece(move.to() + 8, Piece::WPAWN);
587
        }
588
    }
589
}
590
 
591
inline int
592
Position::getSquare(int x, int y) {
593
    return y * 8 + x;
594
}
595
 
596
/** Return x position (file) corresponding to a square. */
597
inline int
598
Position::getX(int square) {
599
    return square & 7;
600
}
601
 
602
/** Return y position (rank) corresponding to a square. */
603
inline int
604
Position::getY(int square) {
605
    return square >> 3;
606
}
607
 
608
inline int
609
Position::mirrorY(int square) {
610
    return square ^ 56;
611
}
612
 
613
/** Return true if (x,y) is a dark square. */
614
inline bool
615
Position::darkSquare(int x, int y) {
616
    return (x & 1) == (y & 1);
617
}
618
 
619
inline void
620
Position::removeCastleRights(int square) {
621
    if (square == getSquare(0, 0)) {
622
        setCastleMask(castleMask & ~(1 << A1_CASTLE));
623
    } else if (square == getSquare(7, 0)) {
624
        setCastleMask(castleMask & ~(1 << H1_CASTLE));
625
    } else if (square == getSquare(0, 7)) {
626
        setCastleMask(castleMask & ~(1 << A8_CASTLE));
627
    } else if (square == getSquare(7, 7)) {
628
        setCastleMask(castleMask & ~(1 << H8_CASTLE));
629
    }
630
}
631
 
632
inline int Position::getFullMoveCounter() const {
633
    return fullMoveCounter;
634
}
635
 
636
inline void Position::setFullMoveCounter(int fm) {
637
    fullMoveCounter = fm;
638
}
639
 
640
inline int Position::getHalfMoveClock() const {
641
    return halfMoveClock;
642
}
643
 
644
inline void Position::setHalfMoveClock(int hm) {
645
    halfMoveClock = hm;
646
}
647
 
648
inline int Position::psScore1(int piece) const {
649
    return psScore1_[piece];
650
}
651
 
652
inline int Position::psScore2(int piece) const {
653
    return psScore2_[piece];
654
}
655
 
656
inline U64 Position::pieceTypeBB(Piece::Type piece) const {
657
    return pieceTypeBB_[piece];
658
}
659
 
660
template <typename Piece0, typename... Pieces>
661
inline U64 Position::pieceTypeBB(Piece0 piece0, Pieces... pieces) const {
662
    return pieceTypeBB(piece0) | pieceTypeBB(pieces...);
663
}
664
 
665
inline U64 Position::whiteBB() const {
666
    return whiteBB_;
667
}
668
 
669
inline U64 Position::blackBB() const {
670
    return blackBB_;
671
};
672
 
673
inline U64 Position::colorBB(int wtm) const {
674
    return wtm ? whiteBB_ : blackBB_;
675
}
676
 
677
inline U64 Position::occupiedBB() const {
678
    return whiteBB() | blackBB();
679
}
680
 
681
inline int Position::wKingSq() const {
682
    return wKingSq_;
683
}
684
 
685
inline int Position::bKingSq() const {
686
    return bKingSq_;
687
}
688
 
689
inline int Position::wMtrl() const {
690
    return wMtrl_;
691
}
692
 
693
inline int Position::bMtrl() const {
694
    return bMtrl_;
695
}
696
 
697
inline int Position::wMtrlPawns() const {
698
    return wMtrlPawns_;
699
}
700
 
701
inline int Position::bMtrlPawns() const {
702
    return bMtrlPawns_;
703
}
704
 
705
inline U64 Position::getHashKey(int piece, int square) {
706
    return psHashKeys[piece][square];
707
}
708
 
709
#endif /* POSITION_HPP_ */