今回はmaterial.hとmaterial.cppの解説をします。と言っても、この二つ、将棋では使わないからあまり関係ないです。ソースコードには簡単なコメントしかつけていません。
ついでにendgame.hとendgame.cppも貼り付けておきます。こちらはチェスの終盤用の処理のようですが、これも将棋では関係ないのであくまでおまけと言うことで…。将棋ではこれに代わり、詰将棋探索を用意したり、しなかったりします。
あと、Scoreという構造体は、序盤と終盤の両方の値を持っていて、実際使うときはそれを進行度に応じて内分して使っているようです。将棋では序盤と終盤で評価値を分けて成功した事例というのはほとんどないですが、理由はよくわかりません。ゲームとしての性質の違いによるものなのかも知れません。
それからpsqtab.hのpsqtabとは、PieceSquareTableの略だと思います。駒がある場所にあるときの評価値。KPですらないですね。チェスでもKPとかKPPを導入すればいまより強くなると思うんですが、大駒ばかりで位置評価があまり利いてこないチェスでは評価関数が重くなるデメリットのほうが大きいのかも知れませんね。
・追記 [2015/2/20 1:00]
gpsfishは序盤と終盤の値を入れていたがちゃんと動いているかまでは確認しなかった。あと配布版は金と玉の値が逆だった RT Stockfish DD – material,endgame,etc.. | やねうら王 公式サイト http://t.co/BVo9ZLmpCb
— woodyring (@woodyring) February 19, 2015
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 |
/* 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 MATERIAL_H_INCLUDED #define MATERIAL_H_INCLUDED #include "endgame.h" #include "misc.h" #include "position.h" #include "types.h" namespace Material { /// Material::Entry contains various information about a material configuration. /// It contains a material balance evaluation, a function pointer to a special /// endgame evaluation function (which in most cases is NULL, meaning that the /// standard evaluation function will be used), and "scale factors". /// /// The scale factors are used to scale the evaluation score up or down. /// For instance, in KRB vs KR endgames, the score is scaled down by a factor /// of 4, which will result in scores of absolute value less than one pawn. // Material::Entryは駒の価値の設定に関するさまざまな情報を含んでいる。 // これは、駒の貸借の評価を含み、特殊な終盤の評価関数(ほとんどの場合においてNULLで、 // これは標準的な評価関数が使われるであろうことを意味する)へのポインターを含み、 // "scale factors"(値を定数倍してあることを意味するスケール値)を含む。 // scale factorは、評価関数のスコアをスケールアップ/スケールダウンさせるのに使われる。 // たとえば、KRB(王、飛、角) vs KR(王、飛)の終盤において、スコアは4というfactorで // スケールダウンされており、これは1つのPAWNより絶対的価値の低いスコアという結果を得るであろうものである。 struct Entry { Score material_value() const { return make_score(value, value); } Score space_weight() const { return spaceWeight; } Phase game_phase() const { return gamePhase; } bool specialized_eval_exists() const { return evaluationFunction != nullptr; } Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } ScaleFactor scale_factor(const Position& pos, Color c) const; Key key; int16_t value; uint8_t factor[COLOR_NB]; EndgameBase EndgameBase Score spaceWeight; Phase gamePhase; }; typedef HashTable Entry* probe(const Position& pos, Table& entries, Endgames& endgames); Phase game_phase(const Position& pos); /// Material::scale_factor takes a position and a color as input, and /// returns a scale factor for the given color. We have to provide the /// position in addition to the color, because the scale factor need not /// to be a constant: It can also be a function which should be applied to /// the position. For instance, in KBP vs K endgames, a scaling function /// which checks for draws with rook pawns and wrong-colored bishops. // Material::scale_factorは局面と手番を引数にとり、与えられた手番側の // scale factorを返す。局面に加えて手番を渡さないといけないのは、 // scale factorが定数である必要がないからである。これは、局面に対して // 適用される関数でありうる。たとえば、KBP vs Kの終盤において、スケーリング関数は // rook pawns(?)と間違った手番のbishopによる引き分けをチェックする。 // ToDo : 最後の文、意味わからん。 inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const { return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos); } } #endif // #ifndef MATERIAL_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 |
/* 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 "material.h" using namespace std; namespace { // Values modified by Joona Kiiski // non_pawn_material()がいくらになれば中盤とみなし、いくら以上になれば終盤とみなすかの定数。 const Value MidgameLimit = Value(15581); const Value EndgameLimit = Value(3998); // Scale factors used when one side has no more pawns const int NoPawnsSF[4] = { 6, 12, 32 }; // Polynomial material balance parameters // pair pawn knight bishop rook queen const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -52 }; const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = { // pair pawn knight bishop rook queen { 0 }, // Bishop pair { 39, 2 }, // Pawn { 35, 271, -4 }, // Knight { 0, 105, 4, 0 }, // Bishop { -27, -2, 46, 100, -141 }, // Rook { 58, 29, 83, 148, -163, 0 } // Queen }; const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = { // THEIR PIECES // pair pawn knight bishop rook queen { 0 }, // Bishop pair { 37, 0 }, // Pawn { 10, 62, 0 }, // Knight OUR PIECES { 57, 64, 39, 0 }, // Bishop { 50, 40, 23, -22, 0 }, // Rook { 106, 101, 3, 151, 171, 0 } // Queen }; // Endgame evaluation and scaling functions accessed direcly and not through // the function maps because correspond to more then one material hash key. Endgame Endgame Endgame Endgame Endgame Endgame // Helper templates used to detect a given material distribution template const Color Them = (Us == WHITE ? BLACK : WHITE); return !pos.count && pos.non_pawn_material(Them) == VALUE_ZERO && pos.non_pawn_material(Us) >= RookValueMg; } template return pos.non_pawn_material(Us) == BishopValueMg && pos.count && pos.count } template const Color Them = (Us == WHITE ? BLACK : WHITE); return !pos.count && pos.non_pawn_material(Us) == QueenValueMg && pos.count && pos.count && pos.count } /// imbalance() calculates imbalance comparing piece count of each /// piece type for both colors. template int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { const Color Them = (Us == WHITE ? BLACK : WHITE); int pt1, pt2, pc, v; int value = 0; // Second-degree polynomial material imbalance by Tord Romstad for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) { pc = pieceCount[Us][pt1]; if (!pc) continue; v = LinearCoefficients[pt1]; for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; value += pc * v; } return value; } } // namespace namespace Material { /// Material::probe() takes a position object as input, looks up a MaterialEntry /// object, and returns a pointer to it. If the material configuration is not /// already present in the table, it is computed and stored there, so we don't /// have to recompute everything when the same material configuration occurs again. Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { Key key = pos.material_key(); Entry* e = entries[key]; // If e->key matches the position's material hash key, it means that we // have analysed this material configuration before, and we can simply // return the information we found the last time instead of recomputing it. if (e->key == key) return e; std::memset(e, 0, sizeof(Entry)); e->key = key; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->gamePhase = game_phase(pos); // Let's look if we have a specialized evaluation function for this // particular material configuration. First we look for a fixed // configuration one, then a generic one if previous search failed. if (endgames.probe(key, &e->evaluationFunction)) return e; if (is_KXK { e->evaluationFunction = &EvaluateKXK[WHITE]; return e; } if (is_KXK { e->evaluationFunction = &EvaluateKXK[BLACK]; return e; } if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN)) { // Minor piece endgame with at least one minor piece per side and // no pawns. Note that the case KmmK is already handled by KXK. assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); if ( pos.count && pos.count { e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; return e; } } // OK, we didn't find any special evaluation function for the current // material configuration. Is there a suitable scaling function? // // We face problems when there are several conflicting applicable // scaling functions and we need to decide which one to use. EndgameBase if (endgames.probe(key, &sf)) { e->scalingFunction[sf->color()] = sf; return e; } // Generic scaling functions that refer to more then one material // distribution. Should be probed after the specialized ones. // Note that these ones don't return after setting the function. if (is_KBPsKs e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; if (is_KBPsKs e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; if (is_KQKRPs e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; else if (is_KQKRPs e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); if (npm_w + npm_b == VALUE_ZERO) { if (!pos.count { assert(pos.count e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; } else if (!pos.count { assert(pos.count e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; } else if (pos.count { // This is a special case because we set scaling functions // for both colors instead of only one. e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; } } // No pawns makes it difficult to win, even with a material advantage. This // catches some trivial draws like KK, KBK and KNK if (!pos.count { e->factor[WHITE] = (uint8_t) (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count } if (!pos.count { e->factor[BLACK] = (uint8_t) (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count } // Compute the space weight if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) { int minorPieceCount = pos.count + pos.count e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0); } // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", this allow us to be more flexible // in defining bishop pair bonuses. const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { { pos.count pos.count { pos.count pos.count e->value = (int16_t)((imbalance return e; } /// Material::game_phase() calculates the phase given the current /// position. Because the phase is strictly a function of the material, it /// is stored in MaterialEntry. // Material::game_phase()は、与えられた現在の局面のフェーズを計算する。 // なぜならフェーズは厳密にmaterialの関数であり、これはMeterialEntryに格納されている。 // ToDo : いまひとつ意味わからん Phase game_phase(const Position& pos) { Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); // npmがMidgameLimit以下ならMIDGAME扱い。 // npmがEndgameLimit以上ならENDGAME扱い。 // npmがその中間に位置していれば内分値にする。 return npm >= MidgameLimit ? PHASE_MIDGAME : npm <= EndgameLimit ? PHASE_ENDGAME : Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); } } // namespace Material |
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 |
/* 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 ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED #include #include #include #include #include #include "position.h" #include "types.h" /// EndgameType lists all supported endgames enum EndgameType { // Evaluation functions KNNK, // KNN vs K KXK, // Generic "mate lone king" eval KBNK, // KBN vs K KPK, // KP vs K KRKP, // KR vs KP KRKB, // KR vs KB KRKN, // KR vs KN KQKP, // KQ vs KP KQKR, // KQ vs KR KBBKN, // KBB vs KN KmmKm, // K and two minors vs K and one or two minors // Scaling functions SCALE_FUNS, KBPsK, // KB+pawns vs K KQKRPs, // KQ vs KR+pawns KRPKR, // KRP vs KR KRPKB, // KRP vs KB KRPPKRP, // KRPP vs KRP KPsK, // King and pawns vs king KBPKB, // KBP vs KB KBPPKB, // KBPP vs KB KBPKN, // KBP vs KN KNPK, // KNP vs K KNPKB, // KNP vs KB KPKP // KP vs KP }; /// Endgame functions can return a Value or a ScaleFactor, according to EndgameType template using eg_fun = std::conditional<(E < SCALE_FUNS), Value, ScaleFactor>; /// Base and derived templates for endgame evaluation and scaling functions template struct EndgameBase { virtual ~EndgameBase() {} virtual Color color() const = 0; virtual T operator()(const Position&) const = 0; }; // ※ VC++2013でコンパイルが通らない。 // templateのdefault classを指定するときにそのなかでE(もう片方のtemplate typename)を指定しているのがいけないのか? template struct Endgame : public EndgameBase explicit Endgame(Color c) : strongSide(c), weakSide(~c) {} Color color() const { return strongSide; } T operator()(const Position&) const; private: const Color strongSide, weakSide; }; /// Endgames class stores in two std::map the std::unique_ptr to endgame /// evaluation and scaling base objects. Then we use polymorphism to invoke /// the actual endgame function calling its operator() that is virtual. class Endgames { template template void add(const std::string& code); template Map std::pair public: Endgames(); template { return *eg = map }; #endif // #ifndef ENDGAME_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 |
/* 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 "bitboard.h" #include "bitcount.h" #include "endgame.h" #include "movegen.h" using std::string; namespace { // Table used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. const int PushToEdges[SQUARE_NB] = { 100, 90, 80, 70, 70, 80, 90, 100, 90, 70, 60, 50, 50, 60, 70, 90, 80, 60, 40, 30, 30, 40, 60, 80, 70, 50, 30, 20, 20, 30, 50, 70, 70, 50, 30, 20, 20, 30, 50, 70, 80, 60, 40, 30, 30, 40, 60, 80, 90, 70, 60, 50, 50, 60, 70, 90, 100, 90, 80, 70, 70, 80, 90, 100, }; // Table used to drive the king towards a corner square of the // right color in KBN vs K endgames. const int PushToCorners[SQUARE_NB] = { 200, 190, 180, 170, 160, 150, 140, 130, 190, 180, 170, 160, 150, 140, 130, 140, 180, 170, 155, 140, 140, 125, 140, 150, 170, 160, 140, 120, 110, 140, 150, 160, 160, 150, 140, 110, 120, 140, 160, 170, 150, 140, 125, 140, 140, 155, 170, 180, 140, 130, 140, 150, 160, 170, 180, 190, 130, 140, 150, 160, 170, 180, 190, 200 }; // Tables used to drive a piece towards or away from another piece const int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; #ifndef NDEBUG bool verify_material(const Position& pos, Color c, Value npm, int num_pawns) { return pos.non_pawn_material(c) == npm && pos.count } #endif // Map the square as if strongSide is white and strongSide's only pawn // is on the left half of the board. Square normalize(const Position& pos, Color strongSide, Square sq) { assert(pos.count if (file_of(pos.list sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 if (strongSide == BLACK) sq = ~sq; return sq; } // Get the material key of a Position out of the given endgame key code // like "KBPKN". The trick here is to first forge an ad-hoc fen string // and then let a Position object to do the work for us. Note that the // fen string could correspond to an illegal position. Key key(const string& code, Color c) { assert(code.length() > 0 && code.length() < 8); assert(code[0] == 'K'); string sides[] = { code.substr(code.find('K', 1)), // Weak code.substr(0, code.find('K', 1)) }; // Strong std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); string fen = sides[0] + char('0' + int(8 - code.length())) + sides[1] + "/8/8/8/8/8/8/8 w - - 0 10"; return Position(fen, false, nullptr).material_key(); } } // namespace /// Endgames members definitions Endgames::Endgames() { add add add add add add add add add add add add add add add add add } template void Endgames::add(const string& code) { map map } /// Mate with KX vs K. This function is used to evaluate positions with /// King and plenty of material vs a lone king. It simply gives the /// attacking side a bonus for driving the defending king towards the edge /// of the board, and for keeping the distance between the two kings small. template<> Value Endgame assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(!pos.checkers()); // Eval is never called when in check // 現局面で合法手がないなら詰みである。 // Stalemate detection with lone king if (pos.side_to_move() == weakSide && !MoveList return VALUE_DRAW; Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Value result = pos.non_pawn_material(strongSide) + pos.count + PushToEdges[loserKSq] + PushClose[square_distance(winnerKSq, loserKSq)]; if ( pos.count || pos.count || pos.bishop_pair(strongSide)) result += VALUE_KNOWN_WIN; return strongSide == pos.side_to_move() ? result : -result; } /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// defending king towards a corner square of the right color. template<> Value Endgame assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Square bishopSq = pos.list // kbnk_mate_table() tries to drive toward corners A1 or H8, // if we have a bishop that cannot reach the above squares we // flip the kings so to drive enemy toward corners A8 or H1. if (opposite_colors(bishopSq, SQ_A1)) { winnerKSq = ~winnerKSq; loserKSq = ~loserKSq; } Value result = VALUE_KNOWN_WIN + PushClose[square_distance(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; return strongSide == pos.side_to_move() ? result : -result; } /// KP vs K. This endgame is evaluated with the help of a bitbase. template<> Value Endgame assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square psq = normalize(pos, strongSide, pos.list Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; if (!Bitbases::probe_kpk(wksq, psq, bksq, us)) return VALUE_DRAW; Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); return strongSide == pos.side_to_move() ? result : -result; } /// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without /// a bitbase. The function below returns drawish scores when the pawn is /// far advanced with support of the king, while the attacking king is far /// away. template<> Value Endgame assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); Square wksq = relative_square(strongSide, pos.king_square(strongSide)); Square bksq = relative_square(strongSide, pos.king_square(weakSide)); Square rsq = relative_square(strongSide, pos.list Square psq = relative_square(strongSide, pos.list Square queeningSq = file_of(psq) | RANK_1; Value result; // If the stronger side's king is in front of the pawn, it's a win if (wksq < psq && file_of(wksq) == file_of(psq)) result = RookValueEg - Value(square_distance(wksq, psq)); // If the weaker side's king is too far from the pawn and the rook, // it's a win. else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) && square_distance(bksq, rsq) >= 3) result = RookValueEg - Value(square_distance(wksq, psq)); // If the pawn is far advanced and supported by the defending king, // the position is drawish else if ( rank_of(bksq) <= RANK_3 && square_distance(bksq, psq) == 1 && rank_of(wksq) >= RANK_4 && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) result = Value(80 - square_distance(wksq, psq) * 8); else result = Value(200) - Value(square_distance(wksq, psq + DELTA_S) * 8) + Value(square_distance(bksq, psq + DELTA_S) * 8) + Value(square_distance(psq, queeningSq) * 8); return strongSide == pos.side_to_move() ? result : -result; } /// KR vs KB. This is very simple, and always returns drawish scores. The /// score is slightly bigger when the defending king is close to the edge. template<> Value Endgame assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Value result = Value(PushToEdges[pos.king_square(weakSide)]); return strongSide == pos.side_to_move() ? result : -result; } /// KR vs KN. The attacking side has slightly better winning chances than /// in KR vs KB, particularly if the king and the knight are far apart. template<> Value Endgame assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); Square bksq = pos.king_square(weakSide); Square bnsq = pos.list Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]); return strongSide == pos.side_to_move() ? result : -result; } /// KQ vs KP. In general, a win for the stronger side, however, there are a few /// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can /// be a draw, so we scale down to distance between kings only. template<> Value Endgame assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Square pawnSq = pos.list Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]); if ( relative_rank(weakSide, pawnSq) != RANK_7 || square_distance(loserKSq, pawnSq) != 1 || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) result += QueenValueEg - PawnValueEg; return strongSide == pos.side_to_move() ? result : -result; } /// KQ vs KR. This is almost identical to KX vs K: We give the attacking /// king a bonus for having the kings close together, and for forcing the /// defending king towards the edge. If we also take care to avoid null move /// for the defending side in the search, this is usually sufficient to be /// able to win KQ vs KR. template<> Value Endgame assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Value result = QueenValueEg - RookValueEg + PushToEdges[loserKSq] + PushClose[square_distance(winnerKSq, loserKSq)]; return strongSide == pos.side_to_move() ? result : -result; } /// KBB vs KN. This is almost always a win. We try to push enemy king to a corner /// and away from his knight. For a reference of this difficult endgame see: /// en.wikipedia.org/wiki/Chess_endgame#Effect_of_tablebases_on_endgame_theory template<> Value Endgame assert(verify_material(pos, strongSide, 2 * BishopValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Square knightSq = pos.list Value result = VALUE_KNOWN_WIN + PushToCorners[loserKSq] + PushClose[square_distance(winnerKSq, loserKSq)] + PushAway[square_distance(loserKSq, knightSq)]; return strongSide == pos.side_to_move() ? result : -result; } /// Some cases of trivial draws template<> Value Endgame template<> Value Endgame /// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// will be used. template<> ScaleFactor Endgame assert(pos.non_pawn_material(strongSide) == BishopValueMg); assert(pos.count // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. Bitboard pawns = pos.pieces(strongSide, PAWN); File pawnFile = file_of(pos.list // All pawns are on a single rook file ? if ( (pawnFile == FILE_A || pawnFile == FILE_H) && !(pawns & ~file_bb(pawnFile))) { Square bishopSq = pos.list Square queeningSq = relative_square(strongSide, pawnFile | RANK_8); Square kingSq = pos.king_square(weakSide); if ( opposite_colors(queeningSq, bishopSq) && square_distance(queeningSq, kingSq) <= 1) return SCALE_FACTOR_DRAW; } // All pawns on same B or G file? Then potential draw if ( (pawnFile == FILE_B || pawnFile == FILE_G) && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) && pos.non_pawn_material(weakSide) == 0 && pos.count { // Get weakSide pawn that is closest to home rank Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); Square strongKingSq = pos.king_square(strongSide); Square weakKingSq = pos.king_square(weakSide); Square bishopSq = pos.list // Potential for a draw if our pawn is blocked on the 7th rank // the bishop cannot attack it or they only have one pawn left if ( relative_rank(strongSide, weakPawnSq) == RANK_7 && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) && (opposite_colors(bishopSq, weakPawnSq) || pos.count { int strongKingDist = square_distance(weakPawnSq, strongKingSq); int weakKingDist = square_distance(weakPawnSq, weakKingSq); // Draw if the weak king is on it's back two ranks, within 2 // squares of the blocking pawn and the strong king is not // closer. (I think this rule only fails in practically // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) if ( relative_rank(strongSide, weakKingSq) >= RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) return SCALE_FACTOR_DRAW; } } return SCALE_FACTOR_NONE; } /// K and queen vs K, rook and one or more pawns. It tests for fortress draws with /// a rook on the third rank defended by a pawn. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(pos.count assert(pos.count Square kingSq = pos.king_square(weakSide); Square rsq = pos.list if ( relative_rank(weakSide, kingSq) <= RANK_2 && relative_rank(weakSide, pos.king_square(strongSide)) >= RANK_4 && relative_rank(weakSide, rsq) == RANK_3 && ( pos.pieces(weakSide, PAWN) & pos.attacks_from & pos.attacks_from return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// K, rook and one pawn vs K and a rook. This function knows a handful of the /// most important classes of drawn positions, but is far from perfect. It would /// probably be a good idea to add more knowledge in the future. /// /// It would also be nice to rewrite the actual code for this function, /// which is mostly copied from Glaurung 1.x, and not very pretty. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, RookValueMg, 0)); // Assume strongSide is white and the pawn is on files A-D Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square wrsq = normalize(pos, strongSide, pos.list Square wpsq = normalize(pos, strongSide, pos.list Square brsq = normalize(pos, strongSide, pos.list File f = file_of(wpsq); Rank r = rank_of(wpsq); Square queeningSq = f | RANK_8; int tempo = (pos.side_to_move() == strongSide); // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. if ( r <= RANK_5 && square_distance(bksq, queeningSq) <= 1 && wksq <= SQ_H5 && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) return SCALE_FACTOR_DRAW; // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. if ( r == RANK_6 && square_distance(bksq, queeningSq) <= 1 && rank_of(wksq) + tempo <= RANK_6 && (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3))) return SCALE_FACTOR_DRAW; if ( r >= RANK_6 && bksq == queeningSq && rank_of(brsq) == RANK_1 && (!tempo || square_distance(wksq, wpsq) >= 2)) return SCALE_FACTOR_DRAW; // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // and the black rook is behind the pawn. if ( wpsq == SQ_A7 && wrsq == SQ_A8 && (bksq == SQ_H7 || bksq == SQ_G7) && file_of(brsq) == FILE_A && (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5)) return SCALE_FACTOR_DRAW; // If the defending king blocks the pawn and the attacking king is too far // away, it's a draw. if ( r <= RANK_5 && bksq == wpsq + DELTA_N && square_distance(wksq, wpsq) - tempo >= 2 && square_distance(wksq, brsq) - tempo >= 2) return SCALE_FACTOR_DRAW; // Pawn on the 7th rank supported by the rook from behind usually wins if the // attacking king is closer to the queening square than the defending king, // and the defending king cannot gain tempi by threatening the attacking rook. if ( r == RANK_7 && f != FILE_A && file_of(wrsq) == f && wrsq != queeningSq && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) && (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo)) return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq)); // Similar to the above, but with the pawn further back if ( f != FILE_A && file_of(wrsq) == f && wrsq < wpsq && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo) && ( square_distance(bksq, wrsq) + tempo >= 3 || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) return ScaleFactor( SCALE_FACTOR_MAX - 8 * square_distance(wpsq, queeningSq) - 2 * square_distance(wksq, queeningSq)); // If the pawn is not far advanced, and the defending king is somewhere in // the pawn's path, it's probably a draw. if (r <= RANK_4 && bksq > wpsq) { if (file_of(bksq) == file_of(wpsq)) return ScaleFactor(10); if ( abs(file_of(bksq) - file_of(wpsq)) == 1 && square_distance(wksq, bksq) > 2) return ScaleFactor(24 - 2 * square_distance(wksq, bksq)); } return SCALE_FACTOR_NONE; } template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); // Test for a rook pawn if (pos.pieces(PAWN) & (FileABB | FileHBB)) { Square ksq = pos.king_square(weakSide); Square bsq = pos.list Square psq = pos.list Rank rk = relative_rank(strongSide, psq); Square push = pawn_push(strongSide); // If the pawn is on the 5th rank and the pawn (currently) is on // the same color square as the bishop then there is a chance of // a fortress. Depending on the king position give a moderate // reduction or a stronger one if the defending king is near the // corner but not trapped there. if (rk == RANK_5 && !opposite_colors(bsq, psq)) { int d = square_distance(psq + 3 * push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) return ScaleFactor(24); else return ScaleFactor(48); } // When the pawn has moved to the 6th rank we can be fairly sure // it's drawn if the bishop attacks the square in front of the // pawn from a reasonable distance and the defending king is near // the corner if ( rk == RANK_6 && square_distance(psq + 2 * push, ksq) <= 1 && (PseudoAttacks[BISHOP][bsq] & (psq + push)) && file_distance(bsq, psq) >= 2) return ScaleFactor(8); } return SCALE_FACTOR_NONE; } /// K, rook and two pawns vs K, rook and one pawn. There is only a single /// pattern: If the stronger side has no passed pawns and the defending king /// is actively placed, the position is drawish. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, weakSide, RookValueMg, 1)); Square wpsq1 = pos.list Square wpsq2 = pos.list Square bksq = pos.king_square(weakSide); // Does the stronger side have a passed pawn? if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) return SCALE_FACTOR_NONE; Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); if ( file_distance(bksq, wpsq1) <= 1 && file_distance(bksq, wpsq2) <= 1 && relative_rank(strongSide, bksq) > r) { switch (r) { case RANK_2: return ScaleFactor(10); case RANK_3: return ScaleFactor(10); case RANK_4: return ScaleFactor(15); case RANK_5: return ScaleFactor(20); case RANK_6: return ScaleFactor(40); default: assert(false); } } return SCALE_FACTOR_NONE; } /// K and two or more pawns vs K. There is just a single rule here: If all pawns /// are on the same rook file and are blocked by the defending king, it's a draw. template<> ScaleFactor Endgame assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); assert(pos.count assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); Square ksq = pos.king_square(weakSide); Bitboard pawns = pos.pieces(strongSide, PAWN); Square psq = pos.list // If all pawns are ahead of the king, all pawns are on a single // rook file and the king is within one file of the pawns then draw. if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) && !((pawns & ~FileABB) && (pawns & ~FileHBB)) && file_distance(ksq, psq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// K, bishop and a pawn vs K and a bishop. There are two rules: If the defending /// king is somewhere along the path of the pawn, and the square of the king is /// not of the same color as the stronger side's bishop, it's a draw. If the two /// bishops have opposite color, it's almost always a draw. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Square pawnSq = pos.list Square strongBishopSq = pos.list Square weakBishopSq = pos.list Square weakKingSq = pos.king_square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away if ( file_of(weakKingSq) == file_of(pawnSq) && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) && ( opposite_colors(weakKingSq, strongBishopSq) || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops if (opposite_colors(strongBishopSq, weakBishopSq)) { // We assume that the position is drawn in the following three situations: // // a. The pawn is on rank 5 or further back. // b. The defending king is somewhere in the pawn's path. // c. The defending bishop attacks some square along the pawn's path, // and is at least three squares away from the pawn. // // These rules are probably not perfect, but in practice they work // reasonably well. if (relative_rank(strongSide, pawnSq) <= RANK_5) return SCALE_FACTOR_DRAW; else { Bitboard path = forward_bb(strongSide, pawnSq); if (path & pos.pieces(weakSide, KING)) return SCALE_FACTOR_DRAW; if ( (pos.attacks_from && square_distance(weakBishopSq, pawnSq) >= 3) return SCALE_FACTOR_DRAW; } } return SCALE_FACTOR_NONE; } /// K, bishop and two pawns vs K and bishop. It detects a few basic draws with /// opposite-colored bishops. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, BishopValueMg, 2)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Square wbsq = pos.list Square bbsq = pos.list if (!opposite_colors(wbsq, bbsq)) return SCALE_FACTOR_NONE; Square ksq = pos.king_square(weakSide); Square psq1 = pos.list Square psq2 = pos.list Rank r1 = rank_of(psq1); Rank r2 = rank_of(psq2); Square blockSq1, blockSq2; if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) { blockSq1 = psq1 + pawn_push(strongSide); blockSq2 = file_of(psq2) | rank_of(psq1); } else { blockSq1 = psq2 + pawn_push(strongSide); blockSq2 = file_of(psq1) | rank_of(psq2); } switch (file_distance(psq1, psq2)) { case 0: // Both pawns are on the same file. Easy draw if defender firmly controls // some square in the frontmost pawn's path. if ( file_of(ksq) == file_of(blockSq1) && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) && opposite_colors(ksq, wbsq)) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; case 1: // Pawns on adjacent files. Draw if defender firmly controls the square // in front of the frontmost pawn's path, and the square diagonally behind // this square on the file of the other pawn. if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 || (pos.attacks_from || abs(r1 - r2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq1 || (pos.attacks_from return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; default: // The pawns are not on the same file or adjacent files. No scaling. return SCALE_FACTOR_NONE; } } /// K, bisop and a pawn vs K and knight. There is a single rule: If the defending /// king is somewhere along the path of the pawn, and the square of the king is /// not of the same color as the stronger side's bishop, it's a draw. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); Square pawnSq = pos.list Square strongBishopSq = pos.list Square weakKingSq = pos.king_square(weakSide); if ( file_of(weakKingSq) == file_of(pawnSq) && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) && ( opposite_colors(weakKingSq, strongBishopSq) || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// K, knight and a pawn vs K. There is a single rule: If the pawn is a rook pawn /// on the 7th rank and the defending king prevents the pawn from advancing, the /// position is drawn. template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, KnightValueMg, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D Square pawnSq = normalize(pos, strongSide, pos.list Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// K, knight and a pawn vs K and bishop. If knight can block bishop from taking /// pawn, it's a win. Otherwise, drawn. template<> ScaleFactor Endgame Square pawnSq = pos.list Square bishopSq = pos.list Square weakKingSq = pos.king_square(weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. if (forward_bb(strongSide, pawnSq) & pos.attacks_from return ScaleFactor(square_distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; } /// K and a pawn vs K and a pawn. This is done by removing the weakest side's /// pawn and probing the KP vs K bitbase: If the weakest side has a draw without /// the pawn, she probably has at least a draw with the pawn as well. The exception /// is when the stronger side's pawn is far advanced and not on a rook file; in /// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). template<> ScaleFactor Endgame assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); // Assume strongSide is white and the pawn is on files A-D Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square psq = normalize(pos, strongSide, pos.list Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; // If the pawn has advanced to the fifth rank or further, and is not a // rook pawn, it's too dangerous to assume that it's at least a draw. if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) return SCALE_FACTOR_NONE; // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. return Bitbases::probe_kpk(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } |
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 |
/* 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 PSQTAB_H_INCLUDED #define PSQTAB_H_INCLUDED #include "types.h" #define S(mg, eg) make_score(mg, eg) /// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on /// a given square a (midgame, endgame) score pair is assigned. PSQT is defined /// for white side, for black side the tables are symmetric. static const Score PSQT[][SQUARE_NB] = { { }, { // Pawn S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8), S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8), S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8), S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8), S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, { // Knight S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104), S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79), S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55), S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42), S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42), S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55), S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79), S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104) }, { // Bishop S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59), S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59) }, { // Rook S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3) }, { // Queen S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80), S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80) }, { // King S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18), S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77), S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105), S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135), S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135), S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105), S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77), S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18) } }; #undef S #endif // #ifndef PSQTAB_H_INCLUDED |
質問です。material.cppの285行目以降ですが、
npm >= MidgameLimit ? PHASE_MIDGAME
: npm <= EndgameLimit ? PHASE_ENDGAME
などとなっているので、この説明としては「npmがMidgameLimit以上ならMIDGAME扱い。npmがEndgameLimit以下ならENDGAME扱い。」とするのが適当かと思いましたが、いかがでしょうか。
ソースコードはよく読めてないのですが、ポーン以外の駒の点数を足し算して、閾値以下になったら(ポーン以外の駒が減ったら)終盤と見なす、というプログラムかと思いました。間違いでしたらすみません。
あー、なるほどです。(`・ω・´)b ご指摘感謝です。