今回は盤面構造体であるpositionクラスについて解説します。このpositionクラス、本当によく出来ていて、将棋ソフトに応用する場合でも、よくよく考えていくとこれと同じ構造に辿り着くと思います。
そういう意味では、将棋ソフトを作る場合でも独自に実装するより先に、このpositionクラスのソースコードを読んだほうがいいと思います。
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 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 |
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #ifndef POSITION_H_INCLUDED #define POSITION_H_INCLUDED #include #include #include "bitboard.h" #include "types.h" /// The checkInfo struct is initialized at c'tor time and keeps info used /// to detect if a move gives check. // checkInfo構造体は、コンストラクター実行時に初期化され、移動が王手になるかを // 検出するために使われる情報を保持する。 class Position; struct Thread; // 王手について調べるための構造体 // 指し手が王手になるかなどの判定に使う。 struct CheckInfo { // コンストラクタとして盤面を与えると、この構造体のメンバー変数を適切な値にセットする。 // 局面探索前などにこのクラスのコンストラクタを呼び出して初期化して使う。(使われる) explicit CheckInfo(const Position&); // 動かすと敵玉に対して空き王手になるかも知れない自駒の候補 // チェスの場合、駒がほとんどが大駒なのでこれらを動かすと必ず開き王手となる。 // 将棋の場合、そうとも限らないので移動方向について考えなければならない。 // ※ dcはdouble check(両王手)の意味か。 Bitboard dcCandidates; // 自分側(手番側)の(敵駒によって)pinされている駒 Bitboard pinned; // 自駒の駒種Xによって敵玉が王手となる升のbitboard // ※ 将棋では駒種が多いのでこれあまりよくない実装。 Bitboard checkSq[PIECE_TYPE_NB]; // 敵王の位置 Square ksq; }; /// The StateInfo struct stores information needed to restore a Position /// object to its previous state when we retract a move. Whenever a move /// is made on the board (by calling Position::do_move), a StateInfo /// object must be passed as a parameter. // StateInfo構造体は、指し手を戻すときに、前の状態に対する局面オブジェクトを // 復元するのに必要な情報を格納している。(Position::do_moveが呼び出されることによって) // 局面の指し手が指されたときに、StateInfoオブジェクトはパラメーターとして渡されなければならない。 // undo_move()で局面を戻すときに情報を元の状態に戻すのが面倒なものを詰め込んでおくための構造体。 // do_move()のときは、ブロックコピーで済むのでそこそこ高速。 struct StateInfo { // pawnとmaterial(それ以外)のhash key Key pawnKey, materialKey; // この局面の、non pawn(pawnでない駒)の価値を計算したもの // PAWN以外 = KNIGHTからQUEENまで // チェスには手駒はないので、単に盤上の駒の枚数×駒の価値 Value npMaterial[COLOR_NB]; // キャスリング可能かのフラグ // castleRights : キャスリング権に関して16通りの数(CASTLE_RIGHT_NB)のようだ。 // rule50 : 50手ルールのときの手数。これが100以上になったときに王手がかかっていなくて合法な指し手があれば引き分けが成立する。 // 50手の間、駒の取り合いがなく、成りもないことが条件?駒をとると0にリセットしているようなのだが。 // pliesFromNull : 最後のdo_null_move()からの手数 // ※ このカウンターはdo_null_move()が呼び出されたときに0にリセットされる。 // min(rule50,pliesFromNull)が、StateInfoをpreviousで遡れる手数。 int castleRights, rule50, pliesFromNull; // この局面の駒の位置によるスコア。 // 次の局面ではこのスコアから差分で計算する。 // この変数名のPSQというのは"P"iece "SQ"uareの略と思われる。 Score psq; // アンパサンの升 Square epSquare; // その他、何か持たせたほうがいい局面構造体があればここに持たせる。 // ※ undo_move()で一気に戻すため。 // この局面のハッシュキー // ※ 次の局面にdo_move()で進むときに最終的な値が設定される Key key; // 現局面で手番側に対して王手をしている駒のbitboard // ※ 次の局面にdo_move()で進むときに更新している Bitboard checkersBB; // この局面で捕獲された駒 // ※ 次の局面にdo_move()で進むときにこの値が設定される PieceType capturedType; // 1手前の局面へのポインタ StateInfo* previous; }; /// When making a move the current StateInfo up to 'key' excluded is copied to /// the new one. Here we calculate the quad words (64bits) needed to be copied. // 指し手で局面を進めているときに、現在のStateInfoを'key'(局面のhash key)を除く // ところまで新しいStateInfo objectにコピーする。これは、コピーされるために必要とされる // quad word(64bit)が何個であるかを計算するものである。 const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t)+1; /// The Position class stores the information regarding the board representation /// like pieces, side to move, hash keys, castling info, etc. The most important /// methods are do_move() and undo_move(), used by the search to update node info /// when traversing the search tree. // Positionクラスは駒、手番、hash key、キャスリング情報などの盤面表現に関する情報を // 格納する。もっとも重要なメソッドはdo_move()とundo_move()であり、探索木を巡回するときに // node情報を更新するための探索に用いられる。 class Position { public: Position() {} // p = 他の局面をコピーして初期化するときに使うコンストラクタ // t = この局面の探索を担当するスレッド Position(const Position& p, Thread* t) { *this = p; thisThread = t; } // f = fen表記で局面を指定する場合に使う。 // c960 = Chess960というチェスのルールの場合 // t = この局面の探索を担当するスレッド Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } // Position::operator=()は、'pos'のコピーを作る。我々はいかなる外部データにも依存していない // 新しく生まれた局面オブジェクトが欲しいので、元の局面オブジェクトからstate pointerは // デタッチしてやる。 Position& operator=(const Position&); // Zobrist Hashの初期化および、駒の升ごとの評価値テーブルの初期化 static void init(); // Text input/output // テキストの入出力(これはFEN表記で行われる) // fen = FEN表記の局面文字列 // isChess960 = Chess960というチェスのルールの場合 // th = this->thisThreadにコピーされる。この局面探索用のメインスレッド void set(const std::string& fen, bool isChess960, Thread* th); // fen形式で局面を出力する const std::string fen() const; // Position::pretty()は、標準出力へ出力する局面のASCII表現を指し手のsan表記とともに返す。 const std::string pretty(Move m = MOVE_NONE) const; // Position representation // 局面の表現 // 盤面に対して、駒のある升すべてが1のBitboardが返る Bitboard pieces() const; // 駒種別ptのbitboardが返る。先後の区別はないので自駒だけであればpieces(Color)と&をとる必要がある。 Bitboard pieces(PieceType pt) const; // 駒種pt1とpt2のBitboardをORしたものを返す Bitboard pieces(PieceType pt1, PieceType pt2) const; // 手番c側の駒がある升が1になっているBitboardを返す Bitboard pieces(Color c) const; // 手番c側の駒種別ptのbitboard Bitboard pieces(Color c, PieceType pt) const; // 手番c側で駒種別pt1とpt2のORをとったbitboard Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const; // 盤上の升sにある駒の種類を返す Piece piece_on(Square s) const; // c側の王の位置 Square king_square(Color c) const; // En passantの升(チェス特有) Square ep_square() const; // 盤上の升に駒がおかれていないならtrueが返る bool empty(Square s) const; // c側の駒種Ptの駒の数(チェスは手駒がないので盤面の駒の数の合計)を返す // → 将棋では使わないな…。 template // 駒種Ptの手番c側の盤上の升が入っている配列の先頭アドレスが返る。 // 終端はSQ_NONEが格納されていると仮定できる。 // 特にKINGの位置を取得したいときに使う。 template // Castling // キャスリング // キャスリング可能か int can_castle(CastleRight f) const; // c側がキャスリング可能か int can_castle(Color c) const; // キャスリングを妨害している駒があるかを返す bool castle_impeded(Color c, CastlingSide s) const; // キャスリングするRookの升 Square castle_rook_square(Color c, CastlingSide s) const; // Checking // --- 王手がらみ // 現局面で手番側に王手している駒 Bitboard checkers() const; // 移動させると敵玉に対して空き王手となる手番側の駒のbitboardを得る Bitboard discovered_check_candidates() const; // 現局面でtoMove側のpinされている駒 // (この駒を移動させるとtoMove側の玉が素抜かれる可能性がある) Bitboard pinned_pieces(Color toMove) const; // Attacks to/from a given square // --- 与えられた升からの利きをbitboardで返すヘルパメソッド // 盤上の升sに利く駒を列挙する。(bitboardで返す) // ※ 全駒分調べるのでそこそこ遅いのだが、チェスだと駒種が少なくて、かつBitboardが64bitで、かつ駒の動きがPAWN以外は上下対称なので先後同時に // チェックできるのでそこまで問題とならない。将棋だと8倍ぐらい重いので気安く使うべきではないと思う。将棋ではこの設計は良くない。 Bitboard attackers_to(Square s) const; // 盤上の升sに利く駒を列挙する。(bitboardで返す) // occ = 駒の存在する場所を表現するbitboard // ※ 全駒分調べるのでそこそこ遅いのだが、チェスだと駒種が少なくて、かつBitboardが64bitで、かつ駒の動きがPAWN以外は上下対称なので先後同時に // チェックできるのでそこまで問題とならない。将棋だと8倍ぐらい重いので気安く使うべきではないと思う。将棋ではこの設計は良くない。 Bitboard attackers_to(Square s, Bitboard occ) const; // 盤上の升sにおかれた駒種pによる利きをbitboardで返す // ※ Pieceは先手の駒のみ。後手の駒は渡しては駄目。 // だもんで、このメソッドではPAWNに関しては正しく生成できない。 Bitboard attacks_from(Piece p, Square s) const; // ↑のヘルパー関数。occ = 盤面上の駒。(遠方駒の利きはこのoccを考慮して行われる) static Bitboard attacks_from(Piece p, Square s, Bitboard occ); // 駒種PieceTypeの升sからの利きのBitboardを返す template // 駒種PieceTypeの手番c側の駒の升sからの利きのBitboardを返す // ※ チェスではPAWNが上下非対称なので手番としてcを指定できる。 // ※ なぜ、SquareとColorをこの順番に書くのか…。他のメソッドに倣うべき。 template // Properties of moves // 指し手の特性について // mが合法手であるかをテスト。pinnedはpinされている駒のbitboard。 // 自殺手なども完全にチェックする。 bool legal(Move m, Bitboard pinned) const; // mが仮の合法手(自殺手が含まれていて良い。do_move()したときにアクセス違反にならなければそれで良い) // であるかのテスト // 置換表の指し手でdo_move()して良いのかの判定のために使われる。 bool pseudo_legal(const Move m) const; // 指し手mが捕獲する指し手なのかをチェックする。 // チェスではENPASSANTも含まれるようだ。よくわからんが。 // ※ Move自体は移動元と移動先の情報しかないので、実際に盤面に照らし合わせないと判断がつかない。 // この関数は、Killerの指し手が捕獲する指し手であるかをチェックするために呼び出される。 // ゆえに、Moveに捕獲する駒の情報を持たせる場合も、この関数は正しく動作しなければならない。 bool capture(Move m) const; // 捕獲する手もしくは成る手であるか // ※ Move自体は移動元と移動先の情報しかないので、実際に盤面に照らし合わせないと判断がつかない。 // この関数は、Killerの指し手が捕獲する指し手であるかをチェックするために呼び出される。 // ゆえに、Moveに捕獲する駒の情報を持たせる場合も、この関数は正しく動作しなければならない。 bool capture_or_promotion(Move m) const; // 仮の合法(擬似合法)の指し手が、(敵玉に)王手になるかをテストする。 bool gives_check(Move m, const CheckInfo& ci) const; bool passed_pawn_push(Move m) const; // do_move()する前の局面で、指し手に対して、その移動元にある駒を返す。(移動させる駒を返す) // ※ 将棋の場合、駒打ちがあるので別途、駒打ちの駒を返す関数が必要だろう。 Piece moved_piece(Move m) const; // 直前の指し手で捕獲された駒の種類を返す。 // ※ 捕獲された駒がなければPIECE_NONE PieceType captured_piece_type() const; // Piece specific // ※ 特別な駒のpawn pushなど判定用(チェス固有のもの) bool pawn_passed(Color c, Square s) const; bool pawn_on_7th(Color c) const; bool bishop_pair(Color c) const; bool opposite_bishops() const; // Doing and undoing moves // 局面を指し手で進める/戻す // Position::do_move()は指し手で局面を進め、StateInfo objectに対する必要なすべての情報を保存する。 // この移動は合法であると仮定している。仮の(擬似の)合法な指し手は、この関数が呼び出される前に // フィルターされるべきである。 // ※ 玉移動による自殺手とか二歩とかこの関数呼び出したら駄目みたいやな…。 // ※ あと、CheckInfoやらgives_checkで王手になるかを事前に調べていたりするので結構コストがかかる。 // 通常探索から呼び出すなら後者。 void do_move(Move m, StateInfo& st); // 通常探索から呼び出すためのdo_move() // moveIsCheck : 今回の指し手で王手になるかどうか // newSt : 次の局面のStateInfo // ci : 王手情報 // moveIsCheck : 今回の指し手で敵玉に王手になるのか(事前に確定していないときはgives_check(m,ci)を使う) void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void undo_move(Move m); void do_null_move(StateInfo& st); void undo_null_move(); // Static exchange evaluation // SEE(静的取り合い評価) // Position::see()は静的交換評価器(SEE)である。これは、指し手による駒による得失の結果 // を見積ろうと試みる。'asymmThreshold'パラメーターはtempi(tempoの複数形。緩急?)として考慮される。 // もし、取り合いを開始したほうの手番が最後の捕獲する指し手を行なうなら、 // この手番側はtempoを失い、もし'asymmThreshold'以下の結果になるなら、捕獲する一連の動作は // 悪いものだと考えられる。 // ※ SEEの解説についてはググれ。いわゆる静止評価。 // また、asymmThresholdを下回るような取り合いはよろしくないという意味。 // seeを求める。 int see(Move m, int asymmThreshold = 0) const; // seeの符号だけわかればいいときに使う。 // 正か、0か負かが返る。 int see_sign(Move m) const; // Accessing hash keys // hash keyへのアクセッサ // 局面のhash key // ※ このkeyはキャスリング・アンパサン・手番が入っている。 Key key() const; // 現在のnodeで探索するときに除外する指し手があれば異なるハッシュキーを用いたい。 // ※ これは1手でも除外したかどうかを示すために異なるhash keyになればよく、 // 除外した指し手によってhash keyの値が変わるという性質のものではない。 Key exclusion_key() const; // この局面のPAWNだけ考慮したhash keyを得る。 Key pawn_key() const; // この局面のmaterial_keyを返す。 // ※ このhash keyは、key()と比べるとキャスリング・アンパサン・手番が入っていないkeyとなっている。 Key material_key() const; // Incremental piece-square evaluation Score psq_score() const; Value non_pawn_material(Color c) const; // Other properties of the position // --- 局面に関するその他のプロパティ // 現局面の手番側を返す Color side_to_move() const; // 何手目か。(初期局面から現在の局面までの手数。) int game_ply() const; // Chess960のルールなのか bool is_chess960() const; // この局面を探索しているメインスレッドのThreadを返す。 Thread* this_thread() const; // 探索したノード数合計を返す。 int64_t nodes_searched() const; // 探索したノード数合計を設定する。 void set_nodes_searched(int64_t n); // 50手ルール、千日手等で引き分けが成立するかの判定 bool is_draw() const; // Position consistency check, for debugging // 局面の一貫性のチェック、デバッグ用 // failedStepが指定されていれば失敗した検証が何番目の検証であるかを返す。 bool pos_is_ok(int* failedStep = nullptr) const; // 盤面を上下反転させる。(180度回転ではない) デバッグ用。 // UCIコマンドでflipと入力したときに呼び出される。 void flip(); private: // Initialization helpers (used while setting up a position) // 初期化ヘルパー (局面のセットアップのときに使われる) void clear(); void set_castle_right(Color c, Square rfrom); // Helper functions void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); // 敵の大駒の影の利きにより、pinされている駒(自駒)および移動させると開き王手になる駒(敵駒)を列挙する。 // ksq = 玉の盤上の位置(自玉) // c = 手番c側の大駒によるpin(攻撃側) // toMove = この手番のほうのpinされている駒(or 開き王手となる駒)を列挙 Bitboard hidden_checkers(Square ksq, Color c, Color toMove) const; // 盤上に駒を置く。 // ※ 局面をset()でfenを読み込むなどして設定するときに盤上に駒を置くのに使う。 void put_piece(Square s, Color c, PieceType pt); // 駒を盤面から取り除き、pieceListなどを更新する。 // sの升からc側の駒がなくなり、その駒種(PIECE_TYPE)はptである // ※ sの地点は、この駒が捕獲されたからこそremoveされるわけであり、直後に次の駒がこの升に来ることを想定している。 void remove_piece(Square s, Color c, PieceType pt); // 手番c側の駒種ptの駒をfromからtoに移動させ、盤面情報等を更新する。 void move_piece(Square from, Square to, Color c, PieceType pt); // Computing hash keys from scratch (for initialization and debugging) // hash keyをスクラッチ(1から)で計算 (初期化時とデバッグ用) // 局面のhash keyの計算 Key compute_key() const; Key compute_pawn_key() const; Key compute_material_key() const; // Computing incremental evaluation scores and material counts Score compute_psq_score() const; Value compute_non_pawn_material(Color c) const; // Board and pieces // 盤面と駒 // 盤面上のそれぞれの升にある駒 Piece board[SQUARE_NB]; // 駒種別のBitboard Bitboard byTypeBB[PIECE_TYPE_NB]; // 先後別の駒のある場所を示すBitboard Bitboard byColorBB[COLOR_NB]; // c側の駒種Ptの駒の数(チェスは手駒がないので盤面の駒の数の合計) // ※ pieceCount[c][ALL_PIECES]はc側の駒の総数 // pieceCount[c][pt]はc側の駒種Ptの駒の総数 int pieceCount[COLOR_NB][PIECE_TYPE_NB]; // 駒の盤面上の位置を持っている。 // pieceList[color][piece_type][最大16個] // 王のように1枚しかない駒は、 // pieceList[WHITE][KING][0]がその位置である。 // また、pieceList[c][X]の終端要素はSQ_NONEが格納されていると仮定できる。 Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; // 盤上の升sqにある駒の駒種をptとして、何枚目のptであるか。 // pieceList[c][pt][X]のXの部分を指し示すのに使う。 // つまり、pieceList[c][pt][index[sq]] == sq int index[SQUARE_NB]; // Other info // その他の情報 // キャスリング権 int castleRightsMask[SQUARE_NB]; // キャスリングするRookの升 Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB]; // キャスリングする経路 Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB]; // 開始局面を表現するStateInfo。 // ※ これはthis->ssから最初にポイントされる。 StateInfo startState; // この局面のobjectによって探索したノード数 // operator = などではゼロに初期化される int64_t nodes; // 初期局面から現在の局面までの手数(初期局面であれば0) int gamePly; // この局面の手番を表す Color sideToMove; // この局面を担当するスレッド Thread* thisThread; // 局面の情報へのポインタ // ※ 最初this->startStateを指している。 StateInfo* st; // Chess960というチェスのルール // rookのキャスリング権(?) int chess960; }; // 探索したノード数合計を返す。 inline int64_t Position::nodes_searched() const { return nodes; } // 探索したノード数合計を設定する。 inline void Position::set_nodes_searched(int64_t n) { nodes = n; } // 盤面上のsの位置にある駒を返す inline Piece Position::piece_on(Square s) const { return board[s]; } // 指し手に対して、その移動元にある駒を返す。(移動させた駒を返す) inline Piece Position::moved_piece(Move m) const { return board[from_sq(m)]; } // 盤上の升に駒がおかれていないならtrueが返る inline bool Position::empty(Square s) const { return board[s] == NO_PIECE; } // 手番側を返す inline Color Position::side_to_move() const { return sideToMove; } // 盤面に対して、駒のある升すべてが1のBitboardが返る inline Bitboard Position::pieces() const { return byTypeBB[ALL_PIECES]; } // 駒種別ptのbitboard inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } // 駒種pt1とpt2のBitboardをORしたものを返す inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { return byTypeBB[pt1] | byTypeBB[pt2]; } // 手番c側の駒がある升が1になっているBitboardを返す inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; } // 手番c側の駒種別ptのbitboard inline Bitboard Position::pieces(Color c, PieceType pt) const { return byColorBB[c] & byTypeBB[pt]; } // 手番c側で駒種別pt1とpt2のORをとったbitboard inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); } // c側の駒種Ptの駒の数(チェスは手駒がないので盤面の駒の数の合計)を返す template return pieceCount[c][Pt]; } // 駒種Ptの手番c側の盤上の升が入っている配列の先頭アドレスが返る。 // 終端はSQ_NONEが格納されていると仮定できる。 template return pieceList[c][Pt]; } // En passantの升(チェス特有) inline Square Position::ep_square() const { return st->epSquare; } // c側の王の位置 inline Square Position::king_square(Color c) const { return pieceList[c][KING][0]; } // キャスリング可能か inline int Position::can_castle(CastleRight f) const { return st->castleRights & f; } // c側がキャスリング可能か inline int Position::can_castle(Color c) const { return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); } // キャスリングを妨害している駒があるかを返す inline bool Position::castle_impeded(Color c, CastlingSide s) const { return byTypeBB[ALL_PIECES] & castlePath[c][s]; } // キャスリングするRookの升 inline Square Position::castle_rook_square(Color c, CastlingSide s) const { return castleRookSquare[c][s]; } // 升sに利きをつけている駒種PtのあるBitboardを返す template inline Bitboard Position::attacks_from(Square s) const { // 大駒ならattacks_from<>を用いて、小駒ならばStepAttacksBBで一発。 return Pt == BISHOP || Pt == ROOK ? attacks_bb : Pt == QUEEN ? attacks_from : StepAttacksBB[Pt][s]; } // PAWNに関してだけ先後で利きが違うのでtemplateで特殊化してある。 // ※ attacks_from_pawn()と書けばいいような気がする。 template<> inline Bitboard Position::attacks_from return StepAttacksBB[make_piece(c, PAWN)][s]; } // 盤上の升sにおかれた駒種pの利きを列挙する // こちらは大駒でもちゃんと合法手のみを生成する。 inline Bitboard Position::attacks_from(Piece p, Square s) const { return attacks_from(p, s, byTypeBB[ALL_PIECES]); } // sの地点に利いている駒を列挙する inline Bitboard Position::attackers_to(Square s) const { // ※ magic bitboardを用いない場合、この実装だと、occ = byTypeBB[ALL_PIECES]となって、 // これに対する大駒の利きを求める必要が出てきてまずいのだが… return attackers_to(s, byTypeBB[ALL_PIECES]); } // 現局面で手番側に王手している駒 inline Bitboard Position::checkers() const { return st->checkersBB; } // 移動させると敵玉に対して空き王手となる手番側の駒のbitboardを得る inline Bitboard Position::discovered_check_candidates() const { // 相手玉に対して、手番側がpinしている手番側の駒を列挙。 return hidden_checkers(king_square(~sideToMove), sideToMove, sideToMove); } // pinされている駒のbitboardを得る inline Bitboard Position::pinned_pieces(Color toMove) const { // 王の位置に対して影の利きのある駒を調べ、それを遮っている駒を探す return hidden_checkers(king_square(toMove), ~toMove, toMove); } // pawnがpawn pushの条件を満たす升を通過しているか判定する。 inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); } // pawn pushの条件を満たしているか // ※ よくわからない。チェスのルール? inline bool Position::passed_pawn_push(Move m) const { return type_of(moved_piece(m)) == PAWN && pawn_passed(sideToMove, to_sq(m)); } // 局面のhash key // ※ このkeyはキャスリング・アンパサン・手番が入っている。 inline Key Position::key() const { return st->key; } inline Key Position::pawn_key() const { return st->pawnKey; } inline Key Position::material_key() const { return st->materialKey; } inline Score Position::psq_score() const { return st->psq; } inline Value Position::non_pawn_material(Color c) const { return st->npMaterial[c]; } // 何手目か。(初期局面から現在の局面までの手数。) inline int Position::game_ply() const { return gamePly; } inline bool Position::opposite_bishops() const { return pieceCount[WHITE][BISHOP] == 1 && pieceCount[BLACK][BISHOP] == 1 && opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); } inline bool Position::bishop_pair(Color c) const { return pieceCount[c][BISHOP] >= 2 && opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]); } inline bool Position::pawn_on_7th(Color c) const { return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7)); } inline bool Position::is_chess960() const { return chess960; } inline bool Position::capture_or_promotion(Move m) const { assert(is_ok(m)); return type_of(m) ? type_of(m) != CASTLE : !empty(to_sq(m)); } inline bool Position::capture(Move m) const { // Note that castle is coded as "king captures the rook" // castleは、"王が飛車に捕獲される"としてコード化されていることに注意。 // 「仮の合法」でなければならない。 assert(is_ok(m)); // 普通の指し手でかつ、捕獲する指し手でなければならない。 // チェスではENPASSANTも含まれるようだ。よくわからんが。 return (!empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT; } // 直前の指し手で捕獲された駒の種類を返す。 // ※ 捕獲された駒がなければPIECE_NONE inline PieceType Position::captured_piece_type() const { return st->capturedType; } // この局面を探索しているスレッドのThreadを返す。 inline Thread* Position::this_thread() const { return thisThread; } // 盤上に駒を置く。 // ※ 局面をset()でfenを読み込むなどして設定するときに盤上に駒を置くのに使う。 inline void Position::put_piece(Square s, Color c, PieceType pt) { // 盤上の升にその駒を置く。 board[s] = make_piece(c, pt); // Bitboardのうち、全駒を表すもの byTypeBB[ALL_PIECES] |= s; // 駒種別。 // ※ 将棋の場合、これは無駄なのでptの種類を減らす変換が必要だと思う。 byTypeBB[pt] |= s; // 先後別に駒がある升が1のbitboard byColorBB[c] |= s; // c側の駒の数 pieceCount[c][ALL_PIECES]++; // indexは盤上の升に対して、それがc側のptの駒の何枚目の駒であるか // このindexを使ってpieceListにアクセスするので.. index[s] = pieceCount[c][pt]++; // pieceListにはその駒が存在する盤上の升が入っている。 pieceList[c][pt][index[s]] = s; } // 手番c側の駒種ptの駒をfromからtoに移動させ、盤面情報等を更新する。 inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { // index[from] is not updated and becomes stale. This works as long // as index[] is accessed just by known occupied squares. // index[from]は更新されず、(情報が)古臭くなる。これはindex[]が既知の駒のある升からだけ // アクセスされる限りにおいてうまく機能する。 // ※ index[from]の地点はfromから駒を移動させるので空の升になる。しかしindex[]は駒のある升 // からしか使わないのでこれを更新しなくとも問題はないのだという意味。 // fromとtoの地点だけ1であるbitboardをまず作る。 Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; // 駒関係のbitboardを上で作ったfrom_to_bbでxor byTypeBB[ALL_PIECES] ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[c] ^= from_to_bb; // fromの地点には駒がなくなる board[from] = NO_PIECE; // toの地点には駒が復元される board[to] = make_piece(c, pt); // indexは変わらず。(この関数の冒頭の説明を読むこと) index[to] = index[from]; // pieceListも更新 pieceList[c][pt][index[to]] = to; } // 駒を盤面から取り除き、pieceListなどを更新する。 // sの升からc側の駒がなくなり、その駒種(PIECE_TYPE)はptである inline void Position::remove_piece(Square s, Color c, PieceType pt) { // WARNING: This is not a reversible operation. If we remove a piece in // do_move() and then replace it in undo_move() we will put it at the end of // the list and not in its original place, it means index[] and pieceList[] // are not guaranteed to be invariant to a do_move() + undo_move() sequence. // 警告 : これは不可逆操作である。もし、do_move()のなかで駒を取り除きたくて、 // それをundo_move()のなかで復元したいのであれば、我々は元あった駒の場所ではなく // listの終わりにそれを置いておくべきである。それはつまり、index[]とpieceList[]は // do_move() + undo_move()の一連の動作によって不変であることを保証されないということだ。 // sが盤面から取り除かれるので各bitboardから該当部分をリセット // 任意駒bitboard byTypeBB[ALL_PIECES] ^= s; // → これ直後にここに駒が来るのだが、しかしmove_piece()で移動させてしまうので // そっちでxorで更新するため、ここでリセットされている必要がある。 // 駒種bitboard byTypeBB[pt] ^= s; // 手番別bitboard byColorBB[c] ^= s; // 盤面上の升をNO_PIECEを入れるのは不要である。(捕獲する駒でこのあと上書きされるから) // ※ → その理屈でいけば、上のbyTypeBB[ALL_PIECES] ^= sだって不要なのだが、 // このあと移動させる駒でいろいろヘルパー関数を呼び出すので、その処理だけ除外しにくい。 /* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing // ※ removeは、この駒が捕獲されたからこそremoveされるわけであり、直後に次の駒がこの升に来ることを想定している。 // 捕獲されたのでc側の駒の枚数が1枚減る pieceCount[c][ALL_PIECES]--; // pieceList[c][pt][X]のXが一番大きい要素を削除(SQ_NONEを書き込む)したいので、 // ここの指し示す盤上の駒を、いま空番になった要素に移動させる Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; index[lastSquare] = index[s]; // この升で死亡 // ※ ややこしい書き方がしてあるが pieceList[c][pt][index[s]] = lastSquare pieceList[c][pt][index[lastSquare]] = lastSquare; pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; } #endif // #ifndef POSITION_H_INCLUDED |
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 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 |
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #include #include #include #include #include #include #include "bitcount.h" #include "movegen.h" #include "notation.h" #include "position.h" #include "psqtab.h" #include "rkiss.h" #include "thread.h" #include "tt.h" using std::string; using std::cout; using std::endl; // UCIで、盤上の駒を表現する文字列 static const string PieceToChar(" PNBRQK pnbrqk"); CACHE_LINE_ALIGNMENT // 評価関数の計算のために使う。 // 盤上の駒の先後・種類・升という盤上の位置に対する評価値。 // このファイルのなかにあるcompute_psq_score()も見ること。 // この変数名のPSQというのは"P"iece "SQ"uareの略と思われる。 Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; // 駒の価値。先後含むはずなのだが、先手分しかない。 // 後手の分はPosition::init()で先手の値をコピーして初期化している。 // SEEで用いる。 Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; // 局面のhash keyのために使う乱数テーブル namespace Zobrist { // hash keyを求めるとき、盤上のすべての駒に対してpsq[手番][駒種][升]でxorしていく Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; // キャスリング権 Key castle[CASTLE_RIGHT_NB]; // ※ 将棋なら持ち駒用のZobrist keyも要るかもな // 手番(後手のときにはこの値でXorする) Key side; // 現在の探索nodeにおいて除外する指し手があるときにハッシュキーを生成するためのもの Key exclusion; } // 現在のnodeで探索するときに除外する指し手があれば異なるハッシュキーを用いたい。 // ※ これは1手でも除外したかどうかを示すために異なるhash keyになればよく、 // 除外した指し手によってhash keyの値が変わるという性質のものではない。 Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion; } namespace { // min_attacker() is an helper function used by see() to locate the least // valuable attacker for the side to move, remove the attacker we just found // from the bitboards and scan for new X-ray attacks behind it. // min_attacker()はsee()で使われるヘルパー関数であり、(目的升toに利く) // 手番側の最も価値の低い攻撃駒の場所を特定し、その見つけた駒をビットボードから取り除き // その背後にあった遠方駒をスキャンする。 // (あればstmAttackersに追加する) // またこの関数はmin_attacker // KNIGHTの..というように徐々に攻撃駒をアップグレードしていく。 // bb = byTypeBB // byTypeBBは駒の価値の低い順に並んでいるものとする。 // occupied = 駒のある場所のbitboard。今回発見された駒は取り除かれる。 // stmAttackers = 手番側の攻撃駒 // attackers = toに利く駒(先後両方)。min_attacker(toに利く最小の攻撃駒)を見つけたら、その駒を除去して // その影にいたtoに利く攻撃駒をattackersに追加する。 // 返し値は今回発見されたtoに利く最小の攻撃駒。これがtoの地点において成れるなら成ったあとの駒を返すべき。 template PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers, Bitboard& occupied, Bitboard& attackers) { // 駒種ごとのbitboardのうち、攻撃駒の候補を調べる Bitboard b = stmAttackers & bb[Pt]; if (!b) return min_attacker // なければ、もうひとつ価値の高い攻撃駒について再帰的に調べる // bにあった駒を取り除く occupied ^= b & ~(b - 1); // この攻撃駒の種類によって場合分け // ※ PAWN,BISHOP,QUEENであるなら、その背後にあるかも知れないBISHOPとQUEENを追加。 // ※ ToDO:あれ?PAWNで捕獲したときにその背後にBISHOPがtoに利いていることなんてあるのか? // → チェスのPAWN、変な動きするのであるのか…。 // 将棋だとtoに対して取った方向によってその背後の駒を足すような処理になるが…。 if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) attackers |= attacks_bb if (Pt == ROOK || Pt == QUEEN) attackers |= attacks_bb // X-rayすでに処理された駒(上のb)も追加されたかも知れないX-rayのあと処理として、 // 再度、occupiedでmaskしておく。 attackers &= occupied; // After X-ray that may add already processed pieces return (PieceType)Pt; } template<> FORCE_INLINE PieceType min_attacker return KING; // No need to update bitboards, it is the last cycle // bitboardを更新する必要はない。これが最後のサイクルである。 // KINGの背後から利いていた駒を足す必要はないわけで… // (次に敵駒があればKINGを取られるし、自駒がこれ以上toに利いていても意味がない) // min_attacker<>()は、stmAttackers(手番側の攻撃駒)がnon zeroであるときに呼び出されることが // 呼び出し側で保証されているから、ここに来たということはtoにKINGが利いていたということを意味するので // KINGでtoの駒が取れる。 } } // namespace /// CheckInfo c'tor // CheckInfoのコンストラクタ CheckInfo::CheckInfo(const Position& pos) { // このクラスのメンバー変数は、このコンストラクタで適切な値をセットしてやる必要がある。 // 相手の手番 Color them = ~pos.side_to_move(); // 敵玉の位置 ksq = pos.king_square(them); // 手番側のpinされている駒 pinned = pos.pinned_pieces(pos.side_to_move()); // 動かすと開き王手になる自駒の候補 dcCandidates = pos.discovered_check_candidates(); // 駒種Xによって敵玉に王手となる升のbitboard // 歩であれば、自玉に敵の歩を置いたときの利きにある場所に自分の歩があればそれは敵玉に対して王手になるので、 // そういう意味で(ksq,them)となっている。 checkSq[PAWN] = pos.attacks_from // チェスでは歩以外は上下に関して利きの対称性があるので上記のような処理は不要となっている。 checkSq[KNIGHT] = pos.attacks_from checkSq[BISHOP] = pos.attacks_from checkSq[ROOK] = pos.attacks_from checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; // 王をもって王手することはできないのでこれはナシで良い。 checkSq[KING] = 0; } /// Position::init() initializes at startup the various arrays used to compute /// hash keys and the piece square tables. The latter is a two-step operation: /// First, the white halves of the tables are copied from PSQT[] tables. Second, /// the black halves of the tables are initialized by flipping and changing the /// sign of the white scores. // Position::init()は、hash keyを計算するために使う種々の配列や駒の升テーブルを // 起動時(main関数)にて初期化する。後者は、二段階の操作である。 // 1つ目、先手に関するテーブルの半分は、PSQT[]テーブルからのコピーである。 // 2つ目、後手のテーブルの半分は先手のスコアの符号を反転/変更するこにより初期化される。 void Position::init() { // -- Zobrist hashのテーブルの初期化 // 擬似乱数発生用 // ※ 乱数に再現性がないといけないので外部ライブラリを用いるわけにはいかない。 RKISS rk; // 3重ループにしてZobrist::psqを乱数で初期化する for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) for (Square s = SQ_A1; s <= SQ_H8; ++s) Zobrist::psq[c][pt][s] = rk.rand // hash keyのアンパサン用の乱数 for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rk.rand // hash keyのキャスリング権 for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; ++cr) { Bitboard b = cr; while (b) { Key k = Zobrist::castle[1ULL << pop_lsb(&b)]; Zobrist::castle[cr] ^= k ? k : rk.rand } } // hash keyの手番 Zobrist::side = rk.rand // hash keyの除外する指し手があるとき用 Zobrist::exclusion = rk.rand // -- 以下、評価値の初期化 // 駒の升ごとの価値の初期化 for (PieceType pt = PAWN; pt <= KING; ++pt) { // 後手の分は先手の値をコピー // これ、後手のNO_PIECEに相当するところ値がコピーされないが、ここは使わないからいいのか…。 PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { psq[WHITE][pt][s] = (v + PSQT[pt][s]); psq[BLACK][pt][~s] = -(v + PSQT[pt][s]); } } } /// Position::operator=() creates a copy of 'pos'. We want the new born Position /// object do not depend on any external data so we detach state pointer from /// the source one. // Position::operator=()は、'pos'のコピーを作る。我々はいかなる外部データにも依存していない // 新しく生まれた局面オブジェクトが欲しいので、元の局面オブジェクトからstate pointerは // デタッチしてやる。 Position& Position::operator=(const Position& pos) { // メモリコピーしたあと、 std::memcpy(this, &pos, sizeof(Position)); // 探索開始局面として、pos->stの局面をthis->startStateにコピー // して、this->stがこのコピーしたobjectを指すようにしておいてやる startState = *st; st = &startState; // この局面オブジェクトによって探索したノード数なのでゼロ初期化 nodes = 0; assert(pos_is_ok()); return *this; } /// Position::set() initializes the position object with the given FEN string. /// This function is not very robust - make sure that input FENs are correct, /// this is assumed to be the responsibility of the GUI. // Position::set()は、FEN文字列で与えられたものでposition object(このクラスのオブジェクト)を初期化する。 // この関数は非常に堅牢なようには作っていないので、入力のFENs文字列は正しいものにすべし。 // これ(正しいものにするのは)はGUI側の責任であると想定している。 void Position::set(const string& fenStr, bool isChess960, Thread* th) { /* A FEN string defines a particular position using only the ASCII character set. ひとつのFEN文字列は、ASCII文字だけを用いてあり、特定の局面を定義する。 ※ 将棋ではUCIではなくUSI cf. http://www.geocities.jp/shogidokoro/usi.html A FEN string contains six fields separated by a space. The fields are: ひとつのFENは、空白文字で区切られた6個のフィールドを持つ。そのフィールドは、 以下の6個である。 1) Piece placement (from white's perspective). Each rank is described, starting with rank 8 and ending with rank 1; within each rank, the contents of each square are described from file A through file H. Following the Standard Algebraic Notation (SAN), each piece is identified by a single letter taken from the standard English names. White pieces are designated using upper-case letters ("PNBRQK") while Black take lowercase ("pnbrqk"). Blank squares are noted using digits 1 through 8 (the number of blank squares), and "/" separates ranks. 2) Active color. "w" means white moves next, "b" means black. 3) Castling availability. If neither side can castle, this is "-". Otherwise, this has one or more letters: "K" (White can castle kingside), "Q" (White can castle queenside), "k" (Black can castle kingside), and/or "q" (Black can castle queenside). 4) En passant target square (in algebraic notation). If there's no en passant target square, this is "-". If a pawn has just made a 2-square move, this is the position "behind" the pawn. This is recorded regardless of whether there is a pawn in position to make an en passant capture. 5) Halfmove clock. This is the number of halfmoves since the last pawn advance or capture. This is used to determine if a draw can be claimed under the fifty-move rule. 6) Fullmove number. The number of the full move. It starts at 1, and is incremented after Black's move. */ char col, row, token; size_t p; // 右下から。 Square sq = SQ_A8; std::istringstream ss(fenStr); clear(); ss >> std::noskipws; // 1. Piece placement // 駒の配置 while ((ss >> token) && !isspace(token)) { if (isdigit(token)) sq += Square(token - '0'); // Advance the given number of files // '/'があると次の段。下から順番に埋めて行っているので、sqはその行の右端(次行の左端)まできているので、1段上の左上は16引く。 else if (token == '/') sq -= Square(16); else if ((p = PieceToChar.find(token)) != string::npos) { put_piece(sq, color_of(Piece(p)), type_of(Piece(p))); // 1升進める ++sq; } } // 2. Active color ss >> token; sideToMove = (token == 'w' ? WHITE : BLACK); ss >> token; // 3. Castling availability. Compatible with 3 standards: Normal FEN standard, // Shredder-FEN that uses the letters of the columns on which the rooks began // the game instead of KQkq and also X-FEN standard that, in case of Chess960, // if an inner rook is associated with the castling right, the castling tag is // replaced by the file letter of the involved rook, as for the Shredder-FEN. // 3. キャスリングが利用できるか。3つの標準と互換性がある。普通のFEN標準、 // KQKqの代わりにrookがゲームを開始する筋の文字を使うShredder-FENと、 // X-FEN標準、Chess960の場合、もし内部rookがキャスリング権と関連付けられているなら、 // Shredder-FENにするためにキャスリングタグは関係あるrookの筋の文字で置き換えられる。 while ((ss >> token) && !isspace(token)) { Square rsq; Color c = islower(token) ? BLACK : WHITE; token = char(toupper(token)); if (token == 'K') for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; --rsq) {} else if (token == 'Q') for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {} else if (token >= 'A' && token <= 'H') rsq = File(token - 'A') | relative_rank(c, RANK_1); else continue; set_castle_right(c, rsq); } // 4. En passant square. Ignore if no pawn capture is possible // 4. アンパサンの升。もしpawnの捕獲が可能でないなら無視する。 if (((ss >> col) && (col >= 'a' && col <= 'h')) && ((ss >> row) && (row == '3' || row == '6'))) { st->epSquare = File(col - 'a') | Rank(row - '1'); if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) st->epSquare = SQ_NONE; } // 5-6. Halfmove clock and fullmove number // 5-6. 半手数を全手数にする。 // ※ 先後両方が指して1手と数えるがhalfmove。将棋の普通の数え方がfullmove。 // UCIではhalf moveで与えられようだ。 ss >> std::skipws >> st->rule50 >> gamePly; // Convert from fullmove starting from 1 to ply starting from 0, // handle also common incorrect FEN with fullmove = 0. // 1から始まるfullmove(先・後両者が指すごとに1手とカウントする)から、 // 0から始まる普通の手数に変換する。fullmove == 0であるよくある不正確なFENも処理する。 // ※ そのため、max()で0に底上げしている。 // 初期局面から現在の局面までの手数(0から始まる) gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK); // 局面のハッシュキーの計算 st->key = compute_key(); // pawnに関するハッシュキーもある st->pawnKey = compute_pawn_key(); st->materialKey = compute_material_key(); st->psq = compute_psq_score(); st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); // 王手している駒の検出 st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); chess960 = isChess960; thisThread = th; assert(pos_is_ok()); } /// Position::set_castle_right() is an helper function used to set castling /// rights given the corresponding color and the rook starting square. // Position::set_castle_right()は対応する手番とrookの開始升によって与えられた // キャスリングの権利を設定するために使われるヘルパー関数である。 void Position::set_castle_right(Color c, Square rfrom) { Square kfrom = king_square(c); CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; CastleRight cr = make_castle_right(c, cs); st->castleRights |= cr; castleRightsMask[kfrom] |= cr; castleRightsMask[rfrom] |= cr; castleRookSquare[c][cs] = rfrom; Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s) if (s != kfrom && s != rfrom) castlePath[c][cs] |= s; for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s) if (s != kfrom && s != rfrom) castlePath[c][cs] |= s; } /// Position::fen() returns a FEN representation of the position. In case /// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function. // Position::fen()は局面のFEN表現を返す。Chess960の場合、Shredder(シュレッダー?)-FENが使われる。主にデバッグ用関数。 const string Position::fen() const { std::ostringstream ss; for (Rank rank = RANK_8; rank >= RANK_1; --rank) { for (File file = FILE_A; file <= FILE_H; ++file) { // それぞれの升に対して Square sq = file | rank; // 駒がないなら if (empty(sq)) { int emptyCnt = 1; // その段の、そのあとの駒のない升をカウントする for (; file < FILE_H && empty(++sq); ++file) ++emptyCnt; // 駒のなかった升の数を出力 ss << emptyCnt; } else // 駒があったなら、それに対応する駒文字列を出力 ss << PieceToChar[piece_on(sq)]; } // 最終段以外では次の行があるのでセパレーターである'/'を出力する。 // 最終段の末尾にはセパレーターは存在しない。 if (rank > RANK_1) ss << '/'; } // 手番文字列の出力 ss << (sideToMove == WHITE ? " w " : " b "); // -- キャスリング関係の出力 if (can_castle(WHITE_OO)) ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K'); if (can_castle(WHITE_OOO)) ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q'); if (can_castle(BLACK_OO)) ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k'); if (can_castle(BLACK_OOO)) ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q'); if (st->castleRights == CASTLES_NONE) ss << '-'; // アンパサンの出力 ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ") << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2; return ss.str(); } /// Position::pretty() returns an ASCII representation of the position to be /// printed to the standard output together with the move's san notation. // Position::pretty()は、標準出力へ出力する局面のASCII表現を指し手のsan表記とともに返す。 const string Position::pretty(Move move) const { const string dottedLine = "\n+---+---+---+---+---+---+---+---+"; const string twoRows = dottedLine + "\n| | . | | . | | . | | . |" + dottedLine + "\n| . | | . | | . | | . | |"; string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; for (Bitboard b = pieces(); b;) { Square s = pop_lsb(&b); // 4 = 1筋に4文字分使って表示しているから。 // 68 = (4文字*8筋 + 右端1文字 + 改行1文字 = 36)×(罫線の行と普通の行の2行) // 513 = 68×7段 + 36 + 1文字 // こういう計算式かな? brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)]; } std::ostringstream ss; // 引数で与えられた指し手の表示 if (move) ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") << move_to_san(*const_cast ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; // 王手している駒の表示 for (Bitboard b = checkers(); b;) ss << square_to_string(pop_lsb(&b)) << " "; // 合法手一覧の表示 ss << "\nLegal moves: "; for (const ExtMove& ms : MoveList ss << move_to_san(*const_cast // ※ 次の出力において改行が必要なら改行を出力するという仕様のようだな…。 return ss.str(); } /// Position:hidden_checkers() returns a bitboard of all pinned / discovery check /// pieces, according to the call parameters. Pinned pieces protect our king, /// discovery check pieces attack the enemy king. // 敵の大駒の影の利きにより、pinされている駒(自駒)および移動させると開き王手になる駒(敵駒)を列挙する。 // ksq = 玉の盤上の位置(自玉) // c = 手番c側の大駒によるpin(攻撃側) // toMove = この手番のほうのpinされている駒(or 開き王手となる駒)を列挙 Bitboard Position::hidden_checkers(Square ksq, Color c, Color toMove) const { Bitboard b, pinners, result = 0; // Pinners are sliders that give check when pinned piece is removed // pinnersとは、ピンしている駒が取り除かれたときに王手となる大駒(複数あり)である。 pinners = ((pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq]) | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c); // 王の位置から十字方向にある手番c側のROOK,QUEENと、 // 王の位置から斜め方向にある手番c側のBISHOP,QUEENを列挙 while (pinners) { // ksqと大駒とで挟まれている駒を列挙。 b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); // 挟まれている駒が2駒以上あれば、これはpinされているとはみなさない。 if (!more_than_one(b)) result |= b & pieces(toMove); } return result; } /// Position::attackers_to() computes a bitboard of all pieces which attack a /// given square. Slider attacks use occ bitboard as occupancy. // 盤上の升sに利く駒を列挙する。(bitboardで返す) // occ = 駒の存在する場所を表現するbitboard Bitboard Position::attackers_to(Square s, Bitboard occ) const { // toに利いている銀を調べるならば、 // 1) toの地点に先手の銀をおいたときの利きで、かつ後手の駒のある場所 // 2) toの地点に後手の銀をおいたときの利きで、かつ先手の駒のある場所 // のように1) + 2)として得られる。 // 以下のソースコードはそういう意味。 // ※ 全駒分調べるのでそこそこ遅いのだが、チェスだと駒種が少なくて、かつBitboardが64bitで、かつ駒の動きがPAWN以外は上下対称なので先後同時に // チェックできるのでそこまで問題とならない。将棋だと8倍ぐらい重いので気安く使うべきではないと思う。将棋ではこの設計は良くない。 return (attacks_from | (attacks_from | (attacks_from | (attacks_bb | (attacks_bb | (attacks_from } /// Position::attacks_from() computes a bitboard of all attacks of a given piece /// put in a given square. Slider attacks use occ bitboard as occupancy. // 盤上の升sにおかれた駒種pによる利きを列挙する // 大駒でも正しく生成する。 Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) { assert(is_ok(s)); switch (type_of(p)) { // 大駒の場合、遮断されていることがあるのでそれを考慮する必要がある。 case BISHOP: return attacks_bb case ROOK: return attacks_bb // 女王なら、角と飛の利きの合成という扱い。 case QUEEN: return attacks_bb // 小駒ならば遮断されることはないので固定テーブルで済む。 default: return StepAttacksBB[p][s]; } } /// Position::legal() tests whether a pseudo-legal move is legal // 指し手mが合法であるかをテストする。(素抜きに合わないか、あるいは、敵の利きのある場所への王の移動でないか) // pinned = pinされている自駒 // ※ それ以上のテストは行わないので、それ以上のテストが必要ならばpseudo_legal()を用いること。 bool Position::legal(Move m, Bitboard pinned) const { // 指し手がおかしくない assert(is_ok(m)); // 引数として渡されたpinned(ピンされている駒)は、手番側の駒である assert(pinned == pinned_pieces(sideToMove)); // 手番側 Color us = sideToMove; // 移動元 Square from = from_sq(m); // 動かす駒は手番側の駒である assert(color_of(moved_piece(m)) == us); // 王の升にはちゃんと王がいる assert(piece_on(king_square(us)) == make_piece(us, KING)); // En passant captures are a tricky special case. Because they are rather // uncommon, we do it simply by testing whether the king is attacked after // the move is made. // アンパサンの捕獲はトリッキーな特殊なケースである。なぜなら、それらは // 比較的普通ではなく、我々はそれを指し手が行われたあとにKINGが攻撃されているかを単にテストすることに // よって行う。 if (type_of(m) == ENPASSANT) { Color them = ~us; Square to = to_sq(m); Square capsq = to + pawn_push(them); Square ksq = king_square(us); Bitboard b = (pieces() ^ from ^ capsq) | to; assert(to == ep_square()); assert(moved_piece(m) == make_piece(us, PAWN)); assert(piece_on(capsq) == make_piece(them, PAWN)); assert(piece_on(to) == NO_PIECE); return !(attacks_bb< ROOK>(ksq, b) & pieces(them, QUEEN, ROOK)) && !(attacks_bb } // If the moving piece is a king, check whether the destination // square is attacked by the opponent. Castling moves are checked // for legality during move generation. // もし移動させる駒がKINGであるなら、行き先の升に相手側の利きがないかをチェックする。 // キャスリングの移動は指し手生成の間に合法性がチェックされる。 // 王の移動であるなら、その移動先に敵の駒が利いていては駄目 if (type_of(piece_on(from)) == KING) return type_of(m) == CASTLE || !(attackers_to(to_sq(m)) & pieces(~us)); // これだと、KINGを串刺し方向に逃げる手が含まれてしまうが、それは指し手生成のEVASIONSのほうで除外されているものとする // また、pseudo_legal()のほうではこの判定やっているから、ここではしなくていい。詳しくはpseudo_legal()の関数のなかの説明を読むこと。 // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. // 王以外の移動であれば、 // 1) pinされている駒がないなら合法 // 2) pinされている駒でなければ無条件で合法 // 3) pinされている駒でも王と(縦横斜において)直線上への移動であれば合法 return !pinned || !(pinned & from) || aligned(from, to_sq(m), king_square(us)); } /// Position::pseudo_legal() takes a random move and tests whether the move is /// pseudo legal. It is used to validate moves from TT that can be corrupted /// due to SMP concurrent access or hash position key aliasing. // Position::pseudo_legal()はランダムな指し手を取り、指し手が擬似合法(※ 自殺手であっても良い)かどうかをテストする。 // これはSMP(※ 他のプロセッサ)による並列アクセスや局面hash keyのエイリアシング(※ 不幸にも同一のhash keyになっている場合) // のため破損した置換表からの指し手の検証に使われる。 // ※ mがこの局面において合法かどうかをテストするための関数。 // killerのような兄弟局面の指し手がこの局面において合法かどうかをテストする。 // ※ 置換表の検査だが、pseudo_legal()で擬似合法手かどうかを判定したあとlegal()で自殺手でないことを // 確認している。このためpseudo_legal()とlegal()とで重複する自殺手チェックは不要。 bool Position::pseudo_legal(const Move m) const { Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); Piece pc = moved_piece(m); // Use a slower but simpler function for uncommon cases // 遅いがしかし通常でないケースのためにシンプルな関数を使う if (type_of(m) != NORMAL) // 全合法手のなかに含まれているか return MoveList // Is not a promotion, so promotion piece must be empty // 成りではないなら、成った駒は空でなければならない。 if (promotion_type(m) - 2 != NO_PIECE_TYPE) return false; // If the from square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. // もし移動元の升に動かす手番側の駒がなければ、指し手は明らかに非合法 if (pc == NO_PIECE || color_of(pc) != us) return false; // The destination square cannot be occupied by a friendly piece // 行き先の升に自駒があってはならない if (pieces(us) & to) return false; // Handle the special case of a pawn move // PAWNの指し手に関する特殊なケースを扱う if (type_of(pc) == PAWN) { // Move direction must be compatible with pawn color // 移動方向がpawnの色(手番側)と互換性がなければならない。 int direction = to - from; if ((us == WHITE) != (direction > 0)) return false; // We have already handled promotion moves, so destination // cannot be on the 8/1th rank. // 我々はすでに成りの指し手を処理したので、移動先の升は、8/1段目ではありえない。 if (rank_of(to) == RANK_8 || rank_of(to) == RANK_1) return false; // Proceed according to the square delta between the origin and // destination squares. // 移動元と移動先の升のdelta(差)に従い処理する。 switch (direction) { case DELTA_NW: case DELTA_NE: case DELTA_SW: case DELTA_SE: // Capture. The destination square must be occupied by an enemy // piece (en passant captures was handled earlier). // 捕獲。行き先の升は敵の駒がなければならない。(アンパサンの捕獲は早い段階で処理された) if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us) return false; // From and to files must be one file apart, avoids a7h5 // 移動元と筋は筋一つ以内でなければならない。a7h5(のような端のつながり)は除く(のでabsをとる)。 if (abs(file_of(from) - file_of(to)) != 1) return false; break; case DELTA_N: case DELTA_S: // Pawn push. The destination square must be empty. // Pawn push。行き先の升は空でなければならない。 if (!empty(to)) return false; break; case DELTA_NN: // Double white pawn push. The destination square must be on the fourth // rank, and both the destination square and the square between the // source and destination squares must be empty. // 先手の2倍のpawn push。(2手分のPAWN移動?) 行き先の升は4段目でなければならず、 // 行き先の升と、移動元から移動先までの升は空でなければならない。 if (rank_of(to) != RANK_4 || !empty(to) || !empty(from + DELTA_N)) return false; break; case DELTA_SS: // Double black pawn push. The destination square must be on the fifth // rank, and both the destination square and the square between the // source and destination squares must be empty. // 後手の2倍のpawn push。(上と同様) if (rank_of(to) != RANK_5 || !empty(to) || !empty(from + DELTA_S)) return false; break; default: return false; } } // 特殊な状況でないならば、fromからtoにpcが移動できる駒であるかをチェック else if (!(attacks_from(pc, from) & to)) return false; // Evasions generator already takes care to avoid some kind of illegal moves // and pl_move_is_legal() relies on this. So we have to take care that the // same kind of moves are filtered out here. // 王手回避手は非合法ないくつかの種類を回避するためにすでに注意し、 // pl_move_is_legal()はこれを信頼している。そこでここでは同じ種類の指し手はフィルターから除外されることに // 注意しなければならない。 // 王手している駒があるのか if (checkers()) { // 動かす駒はKING以外か? if (type_of(pc) != KING) { // Double check? In this case a king move is required // 両王手? このケースにおいてKINGの移動が要求される。 // ※ 両王手を逃れるのは王の移動以外にはないので、王以外の駒を動かしているということは // 回避手になっていない。 if (more_than_one(checkers())) return false; // Our move must be a blocking evasion or a capture of the checking piece // 指し手は、王手を遮断しているか、王手している駒の捕獲でなければならない。 // ※ 王手している駒と王の間に王手している駒の升を足した升が駒の移動先であるか。 // 例) 王■■■^飛 // となっているときに■の升か、^飛 のところが移動先であれば王手は回避できている。 // (素抜きになる可能性はあるが、そのチェックはここでは不要) if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to)) return false; } // In case of king moves under check we have to remove king so to catch // as invalid moves like b1a1 when opposite queen is on c1. // 王手がかかっているときに王を動かす場合において、相手側のQUEENがc1にいて、b1からa1に動かすというような // 不正な移動として捕捉するためにKINGは除外して利きを調べる必要がある。 // ※ 串刺し方向に移動できないのでKINGを除外して移動先への利きを調べるということ。 // ここでは串刺し方向の移動でないことだけ保証する。 // こうしておけばlegal()のほうではKINGを除外して利きをみなくて済む。 // また指し手生成において王手回避はEVASIONSで行われるが、そのときに串刺し方向(遠方駒に対して玉とLineBBで結んだ直線上)の // 移動は除外されているのでそもそも生成されていないからである。将棋の場合もEVASIONSはそのように実装しておくのが望ましいが、 // 将棋では馬・龍による近接王手があるので少しややこしい。 else if (attackers_to(to, pieces() ^ from) & pieces(~us)) return false; } return true; } /// Position::move_gives_check() tests whether a pseudo-legal move gives a check // Position::move_gives_check()は、仮の合法(擬似合法)の指し手が、(敵玉に)王手になるか // をテストする。 bool Position::gives_check(Move m, const CheckInfo& ci) const { // 指し手がおかしくないか assert(is_ok(m)); // 開き王手になる候補手は正しく設定されているか assert(ci.dcCandidates == discovered_check_candidates()); // 手番は、mで与えられた指し手の動かす駒側であるか // ※ moved_piece(m)==NO_PIECEであるassertはmoved_piece()内で行なっている。 assert(color_of(moved_piece(m)) == sideToMove); // 移動元 Square from = from_sq(m); // 移動先 Square to = to_sq(m); // 移動させる駒 PieceType pt = type_of(piece_on(from)); // Direct check ? // 直接王手か? // checkSqは敵玉に対して王手になる升が書かれたbitboard。駒種別。 // ※ これ、KINGに対してはcheckSq[KING]==0になっているのでtrueになることはないという扱い。 if (ci.checkSq[pt] & to) return true; // Discovery check ? // 開き王手になるか // 開き王手になる駒が存在するとして、その駒である if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from)) { // For pawn and king moves we need to verify also direction // pawnとkingは方向についても検証する必要がある。 // fromとtoがpinされている方向以外に移動させるなら // ※ チェスは、これ以外の駒は移動させると必ず開き王手になる if ((pt != PAWN && pt != KING) || !aligned(from, to, king_square(~sideToMove))) return true; } // Can we skip the ugly special cases ? // 面倒な特殊なケースをスキップできるのか? // 指し手の種類がキャスリングなどでなければこの時点で王手にならないことが確定したのでリターン。 if (type_of(m) == NORMAL) return false; Color us = sideToMove; Square ksq = king_square(~us); switch (type_of(m)) { case PROMOTION: return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq; // En passant capture with check ? We have already handled the case // of direct checks and ordinary discovered check, the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. case ENPASSANT: { Square capsq = file_of(to) | rank_of(from); Bitboard b = (pieces() ^ from ^ capsq) | to; return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK)) | (attacks_bb } case CASTLE: { Square kfrom = from; Square rfrom = to; // 'King captures the rook' notation Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1); Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1); return (PseudoAttacks[ROOK][rto] & ksq) && (attacks_bb } default: assert(false); return false; } } /// Position::do_move() makes a move, and saves all information necessary /// to a StateInfo object. The move is assumed to be legal. Pseudo-legal /// moves should be filtered out before this function is called. // Position::do_move()は指し手で局面を進め、StateInfo objectに対する必要なすべての情報を保存する。 // この移動は合法であると仮定している。仮の(擬似の)合法な指し手は、この関数が呼び出される前に // フィルターされるべきである。 // ※ 玉移動による自殺手とか二歩とかこの関数呼び出したら駄目みたいやな…。 // ※ あと、CheckInfoやらgives_checkで王手になるかを事前に調べていたりするので結構コストがかかる。 // 通常探索から呼び出すなら後者。 void Position::do_move(Move m, StateInfo& newSt) { // CheckInfoは現局面で手番側に王手がかかっているかを判定するためのヘルパー(やや重い) CheckInfo ci(*this); // gives_check()はこの指し手で王手になるのかを調べる関数。 do_move(m, newSt, ci, gives_check(m, ci)); } // 通常探索から呼び出すためのdo_move() // moveIsCheck : 今回の指し手で王手になるかどうか // newSt : 次の局面のStateInfo // ci : 王手情報 // moveIsCheck : 今回の指し手で敵玉に王手になるのか(事前に確定していないときはgives_check(m,ci)を使う) void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { // 指し手が合法で assert(is_ok(m)); // 新しく渡されたStateInfo objectは、現局面のStateInfo objectとは異なる。 assert(&newSt != st); // 探索したノード数をインクリメント ++nodes; // 現在の局面のhash keyはこれで、これを更新していき、次の局面のhash keyを求める Key k = st->key; // Copy some fields of old state to our new StateInfo object except the ones // which are going to be recalculated from scratch anyway, then switch our state // pointer to point to the new, ready to be updated, state. // いずれにせよ1から再計算されなければならないものは除くが、 // 古い状態のいくつかのフィールドから新しいStateInfo objectにコピーして、 // 我々の状態pointerを新しいStateInfoをポイントするように切り替えて、 // そのStateInfoが更新される準備をする。 // ※ StateInfoのkey(局面のhash key)までの値をブロックコピーしてしまう。 std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); // 前の局面へのポインタ newSt.previous = st; st = &newSt; // Update side to move // 相手の手番になるので局面のhash keyを更新する。 // ※ 先手は後手になり、後手は先手になるので手番hash keyとxorするだけでいい。 k ^= Zobrist::side; // Increment ply counters.In particular rule50 will be later reset it to zero // in case of a capture or a pawn move. // 手数カウンターを1増やす。rule50の場合に限り、捕獲かPAWNの移動があればのちにリセットされる。 // 開始局面からの手数 ++gamePly; // rule50での手数 ++st->rule50; // 最期のdo_null_move()からの手数 // ※ このカウンターはdo_null_move()が呼び出されたときに0にリセットされる。 ++st->pliesFromNull; // 自分の手番 Color us = sideToMove; // 相手の手番 Color them = ~us; // 引数で渡された指し手の移動元 Square from = from_sq(m); // 引数で渡された指し手の移動先 Square to = to_sq(m); // 移動元にある駒(この駒を動かす)。先後は不明。 Piece pc = piece_on(from); // 移動元にある駒種(この駒を動かす)。先後の区別はない PieceType pt = type_of(pc); // 捕獲する駒。これは、アンパサンの指し手ならPAWNを捕獲したものとし、さもなくば移動先にある駒の駒種を捕獲したものとする。 PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); // 移動元の駒は手番側の駒でなければならない assert(color_of(pc) == us); // 移動先は空の升か、もし駒があるならそれは相手の駒であるか、さもなくばキャスリングの指し手でなければならない assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); // 玉を捕獲する手であってはならない(その前に詰みであるから) assert(captured != KING); // 指し手がキャスリングならば.. if (type_of(m) == CASTLE) { assert(pc == make_piece(us, KING)); bool kingSide = to > from; Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); captured = NO_PIECE_TYPE; do_castle(from, to, rfrom, rto); st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom]; k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; } // 指し手が捕獲する指し手ならば if (captured) { // 捕獲する駒のある升 Square capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. // もし捕獲する駒がpawnならば、pawn hash keyを更新し、さもなくば // non-pawn meterial(pawn以外の駒の価値を合計したもの)を更新する。 // 捕獲するのがPAWNか if (captured == PAWN) { // アンパサンか if (type_of(m) == ENPASSANT) { capsq += pawn_push(them); assert(pt == PAWN); assert(to == st->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == NO_PIECE); assert(piece_on(capsq) == make_piece(them, PAWN)); board[capsq] = NO_PIECE; } // pawn hash keyの更新 // 相手のPAWNがcapsqから消失しているのでそのpsqでxor。 st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; } else // 相手のnpMaterial(pawn以外の駒の価値のトータル)から捕獲された駒の価値を減算 st->npMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists // 盤面とpiece listを更新 // capsqの升から相手の駒がなくなり、その駒種(PIECE_TYPE)はcapturedである remove_piece(capsq, them, captured); // Update material hash key and prefetch access to materialTable // material hash keyを更新して、materialTableへのアクセスのprefetchを行う。 // 捕獲された駒が盤上から消えるので、局面のhash keyを更新する。 k ^= Zobrist::psq[them][captured][capsq]; // meterial keyのzobristは、[them][captured][X] で、X = pieceCount[them][captured] // pieceCountは、手番themの駒種capturedが、盤上で何枚あるかを返す。 // material hash keyはこれをpsqの3番目の添字に指定してある。 st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]]; // material hash keyによってアクセスするmaterial値のcache用のテーブル。 // プリフェッチしておく。 // materialTable[]とアクセスしているが、これはoperator[]がオーバーライドされており、 // Entry*が返るようになっている。material.hを見ること。 // ※ entries[st->materialKey & (8192-1)]のようなアクセスである。 prefetch((char*)thisThread->materialTable[st->materialKey]); // Update incremental scores // スコアの差分更新 // ※ 駒の位置によるスコアpsqの更新 st->psq -= psq[them][captured][capsq]; // Reset rule 50 counter // rule 50 counterをリセットする(駒を捕獲したから) st->rule50 = 0; } // Update hash key // hash keyの更新 // ※ 動かした駒によるhash keyの変動 k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; // Reset en passant square // アンパサンの升のリセット if (st->epSquare != SQ_NONE) { k ^= Zobrist::enpassant[file_of(st->epSquare)]; st->epSquare = SQ_NONE; } // Update castle rights if needed // キャスリング権のリセット、もし必要なら if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to])) { int cr = castleRightsMask[from] | castleRightsMask[to]; k ^= Zobrist::castle[st->castleRights & cr]; st->castleRights &= ~cr; } // Prefetch TT access as soon as we know the new hash key // 我々が新しいhash keyについて知ってすぐに置換表をprefetchする。 prefetch((char*)TT.first_entry(k)); // Move the piece. The tricky Chess960 castle is handled earlier // 駒を動かす。Chess960のトリッキーなキャスリングは早い段階で処理される if (type_of(m) != CASTLE) move_piece(from, to, us, pt); // If the moving piece is a pawn do some special extra work // もし動かす駒がPAWNであれば、特別な追加作業がいくつかある。 if (pt == PAWN) { // Set en-passant square, only if moved pawn can be captured // アンパサンの升を設定する、もし移動させるpawnが捕獲されうるのであれば if ((int(to) ^ int(from)) == 16 && (attacks_from { st->epSquare = Square((from + to) / 2); k ^= Zobrist::enpassant[file_of(st->epSquare)]; } // もし成るのであれば if (type_of(m) == PROMOTION) { // 成ったあとの駒をえる PieceType promotion = promotion_type(m); assert(relative_rank(us, to) == RANK_8); assert(promotion >= KNIGHT && promotion <= QUEEN); // 成ったので元あったPAWNを盤上から除去して新たに成った駒を盤上に配置 remove_piece(to, us, PAWN); put_piece(to, us, promotion); // Update hash keys // hash keyの更新 k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; st->pawnKey ^= Zobrist::psq[us][PAWN][to]; st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion] - 1] ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; // Update incremental score // スコアの差分更新 st->psq += psq[us][promotion][to] - psq[us][PAWN][to]; // Update material // 駒の価値の更新 st->npMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; prefetch((char*)thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter // rule 50の引き分けカウンターをリセットする // ※ 駒が成ったので st->rule50 = 0; } // Update incremental scores // スコアの差分更新 // 移動させた駒がfromからなくなってtoに移動したことによるスコアの変動。 // ※ Zobrist::psq[][][]はハッシュキー、psq[][][]はスコアであることに注意せよ。 st->psq += psq[us][pt][to] - psq[us][pt][from]; // Set capture piece // 捕獲された駒をセットする。 st->capturedType = captured; // Update the key with the final value // 最終的な値でkeyを更新する // ※ この局面のhash key st->key = k; // Update checkers bitboard, piece must be already moved // 王手している駒のbitboardを更新する、(王手している駒があったとしてもその)駒はすでに移動させられたに違いない st->checkersBB = 0; // 今回の指し手で王手になる指し手だったのであれば if (moveIsCheck) { // アンパサン・キャスリング・成りであったのなら、相手玉の升に利きのある自駒を列挙してcheckersBBに代入 // ToDo : よくわからない。成りであるなら、別の駒になってしまっているので両王手の可能性もある…のか? if (type_of(m) != NORMAL) st->checkersBB = attackers_to(king_square(them)) & pieces(us); else { // Direct checks // 直接王手 // ptの駒で王手になる場所にtoがあるなら、toの場所をcheckersBBに加えておく。 // ※ ここ、|= ではなく、= で代入したほうがいいのではなかろうか。 if (ci.checkSq[pt] & to) st->checkersBB |= to; // Discovery checks // 開き王手 // 動かすと開き王手になる駒があるか。あるとして、その駒を動かしたのか。 if (ci.dcCandidates && (ci.dcCandidates & from)) { if (pt != ROOK) st->checkersBB |= attacks_from if (pt != BISHOP) st->checkersBB |= attacks_from } } } // 相手の手番に変更 sideToMove = ~sideToMove; // 局面の正当性チェック assert(pos_is_ok()); } /// Position::undo_move() unmakes a move. When it returns, the position should /// be restored to exactly the same state as before the move was made. // Position::undo_move()は指し手で局面を戻す。この関数からリターンしたとき、局面は // 指し手で進める(do_move()で)前と全く同じ状態に復元されているべきである。 // ※ 実際はpieceListが全く同じ状態にはならないが実用上問題ないし…。 void Position::undo_move(Move m) { assert(is_ok(m)); // 手番を戻す sideToMove = ~sideToMove; // 戻す予定の局面から見た手番 // ※ 戻す予定の局面で動かした駒を復元するので Color us = sideToMove; // 戻したあとの手番 Color them = ~us; // 移動元(ここに戻す) Square from = from_sq(m); // 移動先(ここから戻す) Square to = to_sq(m); // 移動先にあった駒種(この駒をfromに戻す) PieceType pt = type_of(piece_on(to)); // 捕獲した駒があるならば PieceType captured = st->capturedType; // 移動元には駒はないはず。 assert(empty(from) || type_of(m) == CASTLE); // 捕獲されたのはKINGではないはず assert(captured != KING); // 指し手が成りだった場合 if (type_of(m) == PROMOTION) { // 成りなのか? PieceType promotion = promotion_type(m); assert(promotion == pt); assert(relative_rank(us, to) == RANK_8); assert(promotion >= KNIGHT && promotion <= QUEEN); remove_piece(to, us, promotion); put_piece(to, us, PAWN); // ※ チェスでは成る前の駒はPAWNであると言えるが、 // 将棋では盤面を見て移動させた駒を調べないと…。 pt = PAWN; } // キャスリングの復元 if (type_of(m) == CASTLE) { bool kingSide = to > from; Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); captured = NO_PIECE_TYPE; pt = KING; do_castle(to, from, rto, rfrom); } else move_piece(to, from, us, pt); // Put the piece back at the source square // 通常の指し手なので元の場所に駒を移動させる // 捕獲する指し手だったのか? if (captured) { // 捕獲された駒があった場所 Square capsq = to; if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); assert(pt == PAWN); assert(to == st->previous->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(capsq) == NO_PIECE); } // 捕獲されていた駒を復元する put_piece(capsq, them, captured); // Restore the captured piece } // Finally point our state pointer back to the previous state // 最終的にstate pointerをひとつ前のstate(StateInfo)を指すように戻す st = st->previous; // 手数が1つ減る --gamePly; // 局面の正当性のチェック assert(pos_is_ok()); } /// Position::do_castle() is a helper used to do/undo a castling move. This /// is a bit tricky, especially in Chess960. void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { // Remove both pieces first since squares could overlap in Chess960 remove_piece(kfrom, sideToMove, KING); remove_piece(rfrom, sideToMove, ROOK); board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us put_piece(kto, sideToMove, KING); put_piece(rto, sideToMove, ROOK); } /// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips /// the side to move without executing any move on the board. // Position::do(undo)_null_move()は、"null move"で局面を進める(戻す)のに使われる。 // これはいかなるボード上の指し手も実施せずに手番のみ変更する。 void Position::do_null_move(StateInfo& newSt) { // 王手がかかっている局面でnull moveしてはいけない。(取られてしまうので) assert(!checkers()); // 丸ごとStateInfoをコピーしてしまう std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here newSt.previous = st; st = &newSt; // アンパサン if (st->epSquare != SQ_NONE) { // 局面のhash keyの更新 st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; st->epSquare = SQ_NONE; } // 手番が変わるので局面のhash keyも変わる st->key ^= Zobrist::side; // hash keyが変わった瞬間に置換表をprefetchしておく prefetch((char*)TT.first_entry(st->key)); // 50手ルール用カウンター ++st->rule50; // null moveからの手数カウンターのリセット st->pliesFromNull = 0; // 手番を相手番に sideToMove = ~sideToMove; // 局面の正当性チェック assert(pos_is_ok()); } // do_null_move()で進めた局面を戻す void Position::undo_null_move() { // 王手されていないはず(両者に王手されていない局面でしかnull moveしてないので) assert(!checkers()); // StateInfoへのポインターを戻す st = st->previous; // 元の手番に戻す sideToMove = ~sideToMove; } /// Position::see() is a static exchange evaluator: It tries to estimate the /// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes /// tempi into account. If the side who initiated the capturing sequence does the /// last capture, he loses a tempo and if the result is below 'asymmThreshold' /// the capturing sequence is considered bad. // Position::see()は静的交換評価器(SEE)である。これは、指し手による駒による得失の結果 // を見積ろうと試みる。'asymmThreshold'パラメーターはtempi(tempoの複数形。緩急?)として考慮される。 // もし、取り合いを開始したほうの手番が最後の捕獲する指し手を行なうなら、 // この手番側はtempoを失い、もし'asymmThreshold'以下の結果になるなら、捕獲する一連の動作は // 悪いものだと考えられる。 // ※ SEEの解説についてはググれ。いわゆる静止評価。 // また、asymmThresholdを下回るような取り合いはよろしくないという意味。 // ※ KINGを敵の利きに移動させる手は非合法手なので、ここで与えられる指し手にはそのような指し手は含まないものとする。 // また、SEEの地点(to)の駒をKINGで取る手は含まれるが、そのKINGを取られることは考慮しなければならない。 // seeの符号だけわかればいいときに使う。 // 正か、0か負かが返る。 int Position::see_sign(Move m) const { assert(is_ok(m)); // Early return if SEE cannot be negative because captured piece value // is not less then capturing one. Note that king moves always return // here because king midgame value is set to 0. // 捕獲される駒の価値が捕獲する駒の価値より低いのであれば、 // SEEは負であるはずがないので、このときは速攻帰る。 // ※ 例) 歩で飛車をとって、その升でどう取り合おうと、駒損になることはない。 // ※ 将棋だと歩で桂を取っても同歩成と最後に成られるパターンがあるので、それを考慮する必要があり、 // この処理のままだとまずい。 // KINGの指し手はつねにここでリターンすることに注意せよ。なぜなら、KINGの // 中盤での価値はゼロにセットされているからである。 // ※ KINGを取り返すような手を読まれても困るのでこれでいいのか…。 // ※ PieceValueには先後どちらの駒に対してもプラスの値が格納されている。 if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))]) return 1; // 第二パラメーターはdefault値(0)で呼び出される。 return see(m); } // ↑の関数の下請け。 int Position::see(Move m, int asymmThreshold) const { Square from, to; // occupied : 盤上の全駒を表すbitboard Bitboard occupied, attackers, stmAttackers; // to_sq(m)の地点で捕獲された駒の価値のリスト // slIndexは、swapListのindexの意味。swapList[slIndex++] = ... のようにして使う。 int swapList[32], slIndex = 1; PieceType captured; Color stm; assert(is_ok(m)); // 与えられた指し手の移動元、移動先 from = from_sq(m); to = to_sq(m); // toの地点にある駒を格納(最初に捕獲できるであろう駒) // 捕獲する駒がない場合はNO_PIECEとなり、PieceValue[MG][NO_PIECE] == 0である。 swapList[0] = PieceValue[MG][piece_on(to)]; // 最初に取り合いを開始する手番側 = fromの地点にある駒の持ち主 stm = color_of(piece_on(from)); // 盤上の全駒を表すbitboardのfromの地点を0にしておく occupied = pieces() ^ from; // Castle moves are implemented as king capturing the rook so cannot be // handled correctly. Simply return 0 that is always the correct value // unless in the rare case the rook ends up under attack. // キャスリングの指し手はKINGがROOKを捕獲しているものとして実装されているので、 // 正しく扱うことは不可能である。つねに正しい値として単に0を返し、 // レアケースにおいてrookに相手の利きがあるのでなければ。(キャスリングした瞬間取られる) if (type_of(m) == CASTLE) return 0; if (type_of(m) == ENPASSANT) { occupied ^= to - pawn_push(stm); // Remove the captured pawn swapList[0] = PieceValue[MG][PAWN]; } // Find all attackers to the destination square, with the moving piece // removed, but possibly an X-ray attacker added behind it. // 移動させた駒が取り除かれたものとして // 行き先の升(to)に対して利いているすべての駒を見つけ出すが、 // その背後にいるかも知れないX-ray攻撃駒(遠方駒のことか?)を追加する。 // ※ 最後、"& occupied"としてあるのは、最初に取り合いを開始した駒、このPositionクラス上からは除去されていないので // そいつも列挙されてしまうので除去するためのmask // pinされている駒の移動は除外されていることが望ましいのだが…。 // さもなくば、このなかで素抜きチェックか、1手詰め判定があるべき。 attackers = attackers_to(to, occupied) & occupied; // If the opponent has no attackers we are finished // もし相手番のtoに利いている駒がないのであればここで終了する // 相手番に stm = ~stm; // 手番側(相手番)の攻撃駒 // ※ "stm"はSideToMoveの略だと思われる。 stmAttackers = attackers & pieces(stm); // なくなったので最初に手番側が捕獲した駒の価値をSEE値として返す if (!stmAttackers) return swapList[0]; // The destination square is defended, which makes things rather more // difficult to compute. We proceed by building up a "swap list" containing // the material gain or loss at each stop in a sequence of captures to the // destination square, where the sides alternately capture, and always // capture with the least valuable piece. After each capture, we look for // new X-ray attacks from behind the capturing piece. // 移動先の升が防御されていると、計算するのがやや困難になる。 // 我々は移動先の升に対する一連の捕獲のそれぞれの指し手の捕獲に使った駒の // 得失を含む"swap list"を構築することによって、つねに最も価値の低い駒で捕獲する。 // このそれぞれの捕獲のあと、我々は捕獲に使った駒の背後にある新しいX-ray攻撃駒(遠方駒) // を探し出す。 // 最初に捕獲される駒はfromにあった駒である captured = type_of(piece_on(from)); do { // 盤上の駒は有限であり、32回以上の取り合いが一箇所で起こりえない。 // ※ 将棋でも香4,桂4,角2,飛2と8方向で..一箇所での取り合いの最大は20か? assert(slIndex < 32); // Add the new entry to the swap list // swap listに対する新しいentryとして追加する // 今回のSEE値をいまの手番側から見たもの = - (ひとつ前のSEE値) + 今回捕獲した駒の価値 swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; ++slIndex; // Locate and remove the next least valuable attacker // 次のもっとも価値の低い攻撃駒の位置を示し、取り除く // 最も価値の低い駒 // ※ この関数が呼び出されたあと、occupied,attackersは更新されている。影にあった遠方駒はこのとき追加されている。 // また、stmAttackers(手番側の攻撃駒)はnon zeroであり、toに利く攻撃駒がどこかにあるはず。 captured = min_attacker // 相手番に stm = ~stm; // 次の手番側の攻撃駒 stmAttackers = attackers & pieces(stm); // Stop before processing a king capture // KINGの捕獲が処理される前に停止する。 // 捕獲される駒がKINGでかつまだtoの地点に利く駒があるのであれば // KINGというのはデリミタである。KING以外で捕獲する駒が終了したときにこれが返る。 if (captured == KING && stmAttackers) { swapList[slIndex++] = QueenValueMg * 16; // 1箇所で16回以上の取り合いは起こりえないのでQueenValueの16個分以上の変動はない。 // Value.MaxValue的な意味。これを末尾に入れてデリミタ代わりにする。 break; } // 手番側の攻撃駒がある限り回る。 } while (stmAttackers); // If we are doing asymmetric SEE evaluation and the same side does the first // and the last capture, he loses a tempo and gain must be at least worth // 'asymmThreshold', otherwise we replace the score with a very low value, // before negamaxing. // もしasymmetric SEE評価を行なっているなら、最初の取り合いを開始したのと最後の駒を捕獲したのとが // 同じ手番側であるなら、彼が失ったtempoと得たものは少なくとも'asymmThreshold'の価値があるに // 違いなく、さもなくば我々はnegamaxの前に、かなり低い値でスコアを置換する。 if (asymmThreshold) for (int i = 0; i < slIndex; i += 2) if (swapList[i] < asymmThreshold) swapList[i] = -QueenValueMg * 16; // asymmThresholdが設定されているなら、 // swapListを見ていく。そこにasymmThreshold以下の駒が含まれているなら // これをかなり低いスコアで置き換えてしまう。(この駒は取りたくないので) // Having built the swap list, we negamax through it to find the best // achievable score from the point of view of the side to move. // swap listの構築が完了したので、手番側から見て到達可能な最大の点数を // 見つけるためにそれ(swap list)を通じてnegamaxを行う。 // この状況でのnegamaxは、手番側にとって // 取りあいを続ける or そこで取り合いをやめるの2択であり、 // その2択のうち高いほうを採用する。ゆえに、ひとつ前の手番側から見ると // この2択の低いほうがそのノードのスコアとなる。 // ※ ToDo : このSEEの処理、途中で切り上げるような処理にしたほうがいいのではなかろうか…。 while (--slIndex) swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]); return swapList[0]; } /// Position::clear() erases the position object to a pristine state, with an /// empty board, white to move, and no castling rights. // Position::clear()は局面オブジェクトを綺麗な状態になるよう // 空の盤面、先手番、キャスリング権なしとして消去する。 void Position::clear() { // ゼロクリア std::memset(this, 0, sizeof(Position)); // アンパサン初期化 startState.epSquare = SQ_NONE; // stは最初、初期値としてthis->startStateを指すように初期化される。 st = &startState; // 盤面の駒の場所を示す配列にSQ_NONEを突っ込む for (int i = 0; i < PIECE_TYPE_NB; ++i) for (int j = 0; j < 16; ++j) pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE; } /// Position::compute_key() computes the hash key of the position. The hash /// key is usually updated incrementally as moves are made and unmade, the /// compute_key() function is only used when a new position is set up, and /// to verify the correctness of the hash key when running in debug mode. // Position::compute_key()は、局面のhash keyを計算する。hash keyは、通例、 // 指し手で局面を進める/戻すときに差分で更新される。compute_key()関数は、 // 新規局面のセットアップされるときと、デバッグモードで走らせたときにhash key // の正確さを検証するときにのみ使われる。 Key Position::compute_key() const { Key k = Zobrist::castle[st->castleRights]; // 盤面すべての駒に対して for (Bitboard b = pieces(); b;) { Square s = pop_lsb(&b); // psq[手番][駒種][升]でxorしていく k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s]; } // アンパサン if (ep_square() != SQ_NONE) k ^= Zobrist::enpassant[file_of(ep_square())]; // 手番も含める if (sideToMove == BLACK) k ^= Zobrist::side; return k; } /// Position::compute_pawn_key() computes the hash key of the position. The /// hash key is usually updated incrementally as moves are made and unmade, /// the compute_pawn_key() function is only used when a new position is set /// up, and to verify the correctness of the pawn hash key when running in /// debug mode. // Position::compute_pawn_key()は、局面のhash keyを計算する。 // このhash keyは通例、指し手のmake/unmakeのときに差分更新され、 // compute_pawn_key()関数は新しい局面がセットアップされたときと、 // デバッグモードで走っているときにpawn hash keyの正確性を検証する // ためにのみ用いられる。 Key Position::compute_pawn_key() const { Key k = 0; for (Bitboard b = pieces(PAWN); b;) { // PAWNの駒に対してだけ Square s = pop_lsb(&b); k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; } return k; } /// Position::compute_material_key() computes the hash key of the position. /// The hash key is usually updated incrementally as moves are made and unmade, /// the compute_material_key() function is only used when a new position is set /// up, and to verify the correctness of the material hash key when running in /// debug mode. // Position::compute_material_key()は局面のhash keyを計算する。 // このhash keyは通例指し手のmake/unmakeが行われたときに差分更新され、 // compute_material_key()は新しい局面のセットアップ時にのみ使われ、 // デバッグモードで走っているときにmaterial hash keyの正確さを検証するのに使われる。 // ※ このhash keyは、キャスリング・アンパサン・手番が入っていないkeyとなっている。 Key Position::compute_material_key() const { Key k = 0; for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= QUEEN; ++pt) for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt) k ^= Zobrist::psq[c][pt][cnt]; // 3番目の要素が、何枚目の駒であるかによる値(cnt)であることに注意せよ。 // (ここは升目を表すはずだが、この乱数テーブルを部分的に流用してある) return k; } /// Position::compute_psq_score() computes the incremental scores for the middle /// game and the endgame. These functions are used to initialize the incremental /// scores when a new position is set up, and to verify that the scores are correctly /// updated by do_move and undo_move when the program is running in debug mode. // Position::compute_psq_score()は中盤・終盤のための「差分スコア」を計算する。 // この関数は新しい局面がセットアップされたときに「差分スコア」の初期化時と、 // このプログラムがデバッグモードで走っているときにdo_moveとundo_moveによって更新される // スコアが正しいか検証するときに使われる。 // ※ 要するに1から評価値を計算するものであり、差分計算するものではない。 Score Position::compute_psq_score() const { Score score = SCORE_ZERO; for (Bitboard b = pieces(); b;) { // bitboardの駒のある地点に対して Square s = pop_lsb(&b); Piece pc = piece_on(s); // 駒の位置ごとに評価値を足していくだけ! score += psq[color_of(pc)][type_of(pc)][s]; } return score; } /// Position::compute_non_pawn_material() computes the total non-pawn middle /// game material value for the given side. Material values are updated /// incrementally during the search, this function is only used while /// initializing a new Position object. // Position::compute_non_pawn_material()は、与えられた手番(引数のc)に対する // 中盤の駒の価値についてpawn以外の駒のトータルを計算する。 // 駒の価値は探索中に差分更新され、この関数は新しい局面オブジェクトを初期化 // するときにのみ使用される。 Value Position::compute_non_pawn_material(Color c) const { Value value = VALUE_ZERO; // PAWN以外 = KNIGHTからQUEENまで // チェスには手駒はないので、単に盤上の駒の枚数×駒の価値 for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt) value += pieceCount[c][pt] * PieceValue[MG][pt]; return value; } /// Position::is_draw() tests whether the position is drawn by material, /// repetition, or the 50 moves rule. It does not detect stalemates, this /// must be done by the search. // Position::is_draw()は、局面が駒価値(入玉時の駒点数ルールみたいなもの?)、 // 千日手、もしくは50手ルールのときに引き分けかどうかを判定する。 // この関数はステイルメイトは検出しない。それ(ステイルメイトの検出)は探索時になされるべき。 bool Position::is_draw() const { // Draw by material? // 駒点数による引き分け // 両者のPAWNが盤面上になく、 // pawn以外の駒の価値の合計を足したものがBishopValueMg以下である。 // → チェスのルールなの?なんなの? if (!pieces(PAWN) && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg)) return true; // Draw by the 50 moves rule? // 50手ルールによる引き分け // 100手(50手ルールは自分が50手指したの意味か)を超えていて、 // 王手がかかっていなくて、かつ、合法な指し手がある(詰みではない) // なら引き分け if (st->rule50 > 99 && (!checkers() || MoveList return true; // i = 何手前か // st->rule50はrule50のときの対局開始から現在までの経過手数 // st->pliesFromNullは初期局面からの手数 // この手数分だけ遡れるはず…。 int i = 4, e = std::min(st->rule50, st->pliesFromNull); if (i <= e) { // 2手前の局面 StateInfo* stp = st->previous->previous; // 千日手は4手前、6手前、8手前、…の局面のhash keyと現局面のhash keyが一致すれば千日手 do { // 2手ずつさかのぼっていく。 stp = stp->previous->previous; // 一度目の合致で千日手扱いしてしまう if (stp->key == st->key) return true; // Draw after first repetition i += 2; } while (i <= e); } return false; } /// Position::flip() flips position with the white and black sides reversed. This /// is only useful for debugging especially for finding evaluation symmetry bugs. // Position::flip()は盤面を上下反転させる。これは、 // 特に評価関数の対称性があるかどうかのバグを発見するためのデバッグにおいてのみ有用である。 // flip()で用いる、大文字と小文字を入れ替えるヘルパー関数。 // fen形式では大文字か小文字かで駒が先後どちらの駒であるかを表現するので // 盤面を上下反転させるときにこの処理が必要になる。 static char toggle_case(char c) { return char(islower(c) ? toupper(c) : tolower(c)); } // 盤面を先後反転させる void Position::flip() { string f, token; // 自分のfenを得て、それを文字置換して、自分にfenとして読み込ませる、という仕組みらしい std::stringstream ss(fen()); for (Rank rank = RANK_8; rank >= RANK_1; --rank) // Piece placement { // 最後の行以外なら'/'が区切り文字 std::getline(ss, token, rank > RANK_1 ? '/' : ' '); // 読み込んだものを逆順になるように前方にinsertしていく f.insert(0, token + (f.empty() ? " " : "/")); } // 現局面の手番を読み込み、それを反転させる。自分で出力しているfen()なので、小文字の文字が続いていると仮定して良い。 ss >> token; // Active color f += (token == "w" ? "B " : "W "); // Will be lowercased later // キャスリング権? // ※将棋だとここに持ち駒がくるが… ss >> token; // Castling availability f += token + " "; // 大文字と小文字を入れ替える。これで先後入れ替わる std::transform(f.begin(), f.end(), f.begin(), toggle_case); // アンパサン ss >> token; // En passant square f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); // 手数が入っている。これはこのままリダイレクトしておけば良い。 std::getline(ss, token); // Half and full moves f += token; set(f, is_chess960(), this_thread()); assert(pos_is_ok()); } /// Position::pos_is_ok() performs some consitency checks for the position object. /// This is meant to be helpful when debugging. // Position::pos_is_ok()は局面オブジェクトに対していくつかの一貫性のチェックを行う。 // これは、デバッグのときに有用であると言えるだろう。 // failedStepが指定されていれば失敗した検証が何番目の検証であるかを返す。 bool Position::pos_is_ok(int* failedStep) const { int dummy, *step = failedStep ? failedStep : &dummy; // What features of the position should be verified? // 局面のどの特徴を検証すべきなのか? const bool all = false; // bitboardの正当性の検証 const bool debugBitboards = all || false; // 盤面上の玉の数が先後1つずつあるかを検証する const bool debugKingCount = all || false; // 玉がとれてしまうかを検証する const bool debugKingCapture = all || false; // 王手している駒が3以上あるかを検証する。(開き王手で2になるのが最大であって3以上あるのはおかしい) const bool debugCheckerCount = all || false; // hash keyがおかしくなっていないかを検証する const bool debugKey = all || false; // 駒の価値のhash keyがおかしくなっていないかを検証する const bool debugMaterialKey = all || false; // pawnのhash keyがおかしくなっていないかを検証する const bool debugPawnKey = all || false; // 差分評価で計算している駒の位置による評価値がおかしくなっていないかを検証する const bool debugIncrementalEval = all || false; // pawn以外の駒の価値がおかしくなっていないかを検証する const bool debugNonPawnMaterial = all || false; // 盤上の駒の枚数がpieceCount[c][pt]と一致するかを検証する const bool debugPieceCounts = all || false; // pieceListとindex[],boardの整合性がとれているかを検証する const bool debugPieceList = all || false; // キャスリングがおかしくなっていないかを検証する const bool debugCastleSquares = all || false; *step = 1; // 手番が先手もしくは後手でないと駄目 if (sideToMove != WHITE && sideToMove != BLACK) return false; // 先手玉の場所に先手の玉がいなければならない。 if ((*step)++, piece_on(king_square(WHITE)) != W_KING) return false; // 後手玉の場所に後手の玉がいなければならない if ((*step)++, piece_on(king_square(BLACK)) != B_KING) return false; // 盤面上の玉の数が先後1つずつあるかを検証する if ((*step)++, debugKingCount) { int kingCount[COLOR_NB] = {}; // 盤面それぞれの升に対して for (Square s = SQ_A1; s <= SQ_H8; ++s) if (type_of(piece_on(s)) == KING) ++kingCount[color_of(piece_on(s))]; if (kingCount[0] != 1 || kingCount[1] != 1) return false; } // 玉がとれてしまうか if ((*step)++, debugKingCapture) // 相手玉に利きのある手番側の駒があってはならない。 if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove)) return false; // 王手している駒が3以上ある。(開き王手で2になるのが最大であって3以上あるのはおかしい) if ((*step)++, debugCheckerCount && popcount return false; // bitboardの正当性の検証 if ((*step)++, debugBitboards) { // The intersection of the white and black pieces must be empty // 先手と後手の駒が存在する場所を表現するbitboardの交差(積集合)は空でなければならない。 // ※ 先手・後手の両方に属する駒は存在しないため。 if (pieces(WHITE) & pieces(BLACK)) return false; // The union of the white and black pieces must be equal to all // occupied squares // 先手と後手の駒の和集合は、全駒のある場所が1であるbitboardと等しくなければならない。 if ((pieces(WHITE) | pieces(BLACK)) != pieces()) return false; // Separate piece type bitboards must have empty intersections // 別種の駒のbitboardの交差は空でなければならない。 // ※ 2つの種類に属する駒はないため。将棋で、馬を角のbitboardと馬龍王のbitboardのように // ふたつのbitboardに属させる場合はこの限りではない。 for (PieceType p1 = PAWN; p1 <= KING; ++p1) for (PieceType p2 = PAWN; p2 <= KING; ++p2) if (p1 != p2 && (pieces(p1) & pieces(p2))) return false; // ※ ここ、p2 = p1 + 1として、ループを回したほうがいいな。 // そうしないのは、operator &の対称性も仮定できないということなのかな。 } // アンパサン if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6) return false; // hash keyがおかしくなっていないか if ((*step)++, debugKey && st->key != compute_key()) return false; // pawnのhash keyがおかしくなっていないか if ((*step)++, debugPawnKey && st->pawnKey != compute_pawn_key()) return false; // 駒の価値のhash keyがおかしくなっていないか if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key()) return false; // 差分評価で計算している評価値がおかしくなっていないか if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score()) return false; // pawn以外の盤上の駒の価値 if ((*step)++, debugNonPawnMaterial) if (st->npMaterial[WHITE] != compute_non_pawn_material(WHITE) || st->npMaterial[BLACK] != compute_non_pawn_material(BLACK)) return false; // 盤上の駒の枚数がpieceCount[c][pt]と一致するか if ((*step)++, debugPieceCounts) for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) if (pieceCount[c][pt] != popcount return false; // pieceListとindex[],boardの整合性がとれているか if ((*step)++, debugPieceList) for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) for (int i = 0; i < pieceCount[c][pt]; ++i) if (board[pieceList[c][pt][i]] != make_piece(c, pt) || index[pieceList[c][pt][i]] != i) return false; // キャスリング権のチェック(よくわからん) if ((*step)++, debugCastleSquares) for (Color c = WHITE; c <= BLACK; ++c) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) { CastleRight cr = make_castle_right(c, s); if (!can_castle(cr)) continue; if ((castleRightsMask[king_square(c)] & cr) != cr || piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK) || castleRightsMask[castleRookSquare[c][s]] != cr) return false; } *step = 0; return true; } |