fairy-stockfish / Fairy-Stockfish

chess variant engine supporting Xiangqi, Shogi, Janggi, Makruk, S-Chess, Crazyhouse, Bughouse, and many more
https://fairy-stockfish.github.io/
GNU General Public License v3.0
625 stars 195 forks source link

Don't count (excessive) checks and check evasions in Xiangqi 50-move rule #405

Closed cloudfish closed 2 years ago

cloudfish commented 3 years ago

Currently Fairy-SF delivers many checks if it thinks it is losing in Xiangqi endgames, trying to obtain a draw by 50-move rule. This is, however, meaningless by Xiangqi rules. Xboard does not count any checks or check evasions in 4.5.0.

Alternatively you can use the AXF rule. The rule says, 在限着内提和方“将军”最多只计十着,超过则为无效步,要扣除。 At most 10 plies of checks delivered by the player who claims a draw by 50-move rule can be counted. The excessive checks are "null moves" and should be subtracted from the ply count. There are subtleties understanding this. "The ply count" is not the 100-ish total ply count; if so, the evasions will still be counted. It is the ply count of the claiming player. Traditionally in Xiangqi, we check the ply count of both players. Only if both players have made 50 (valid) moves can we say 50 full moves have passed. So the game is drawn if and only if the ply count of the claiming player after excluding null moves >= 50, and the ply count of the other player >= 50. Or equivalently, the total ply count before excluding null moves >= 100 (the original condition), and the ply count of the claiming player after excluding null moves >= 50.

Below is a game example. Fairy-SF is clearly losing, but claims a draw by 50-move rule after delivering many checks. Red is the intergrated engine of TianTian Xiangqi (Android version), GM difficulty level.

[Event "Computer Chess Game"] [Site "cloud"] [Date "2021.11.21"] [Round "-"] [White "TTXQ"] [Black "Fairy-Stockfish 14.0.1 XQ"] [Result "1/2-1/2"] [TimeControl "600+6"] [Variant "xiangqi"] [Annotator "1... -0.31"]

1. Che2 Hg7 {-0.31/23 10} 2. Hg2 Hc7 {-0.21/22} 3. Rh0 Rh9 {-0.21/24} 4. c4 g5 {-0.07/24} 5. Rh6 Ece7 {-0.06/21} 6. Rg6 Hce8 {-0.15/23} 7. Hc2 Cb6 {-0.08/24} 8. Hd4 c5 {+0.03/25} 9. Hc6 cxc4 {+0.00/28} 10. Cbc2 c3 {+0.15/21} 11. Rb0 cxc2 {+0.18/25} 12. Rxb6 cd2 {+0.13/24} 13. Hb8 Ra8 {+0.08/25} 14. Cf2 Rh8 {+0.06/27} 15. Afe1 d1 {+0.27/19} 16. Cd2 Hc9 {+0.47/25} 17. Rxg7 Ch2 {+0.32/25} 18. Cxh2 Rxh2 {+0.19/26} 19. Rb2 Ade8 {+0.15/23} 20. Rg6 Hb7 {+0.00/28} 21. Rxb7 Rxg2 {+0.00/33} 22. Ee2 Rxe2 {+0.00/35} 23. Rxe6 dxd0+ {+0.00/36} 24. Kxd0 Rxe1 {+0.00/38} 25. Rb3 Ra9 {+0.00/39} 26. Rxa6 Rc9 {+0.00/50} 27. Rc6 Ra9 {+0.00/52} 28. Rxi6 Rc1 {+0.13/23} 29. Rc6 Rh1 {+0.00/32} 30. e4 Rh0+ {+0.00/34} 31. Kd1 Re0 {+0.00/41} 32. Rc4 g4 {+0.00/33} 33. gxg4 Ra7 {+0.00/38} 34. Hc6 Rd7+ {+0.00/31} 35. Rd4 Rc7 {+0.00/37} 36. Rd8 Rc9 {+0.00/37} 37. Rc3 Rf0 {+0.00/35} 38. e5 Rf4 {+0.00/30} 39. Rdd3 Rb9 {+0.00/35} 40. Rb3 Rc9 {+0.00/33} 41. Rdc3 Rc7 {+0.00/27} 42. ed5 Rd7 {-0.18/26} 43. Ke1 Rxg4 {-0.22/28} 44. He5 Rg1+ {-0.30/28} 45. Ke0 Rd9 {-0.33/26} 46. Rb5 Rg0+ {-0.18/25} 47. Ke1 Rg1+ {-0.18/25} 48. Ke0 Rg0+ {-0.19/28} 49. Ke1 Rg7 {-0.25/26} 50. a4 Rg1+ {-0.19/23} 51. Ke0 Rg0+ {-0.31/24} 52. Ke1 Rg1+ {-0.28/25} 53. Ke0 Rg8 {-0.29/25} 54. a5 Rg0+ {-0.31/24} 55. Ke1 Rg1+ {-0.32/26} 56. Ke0 Rg0+ {-0.32/29} 57. Ke1 Rg1+ {-0.36/26} 58. Ke0 Rg8 {-0.36/28} 59. d6 Rxd6 {-0.35/30} 60. Rd3 Rxd3 {-0.36/29} 61. Hxd3 Rg0+ {-0.35/27} 62. Ke1 Ec9 {-0.37/27} 63. He5 Ege7 {-0.33/27} 64. Ee2 Rg1+ {-0.35/25} 65. Ke0 Rg2 {-0.34/28} 66. Ec4 Rg0+ {-0.33/26} 67. Ke1 Rg1+ {-0.37/27} 68. Ke0 Rd1 {-0.37/24} 69. i4 Rd4 {-0.37/29} 70. i5 Re4+ {-0.37/26} 71. Kd0 Rd4+ {-0.39/29} 72. Ke0 Rh4 {-0.39/27} 73. Rd5 Rh0+ {-0.43/28} 74. Ke1 Rh1+ {-0.45/27} 75. Ke0 Rh0+ {-0.42/28} 76. Ke1 Rh1+ {-0.46/28} 77. Ke0 Rh2 {-0.43/27} 78. ih5 Rh0+ {-0.48/28} 79. Ke1 Rh1+ {-0.49/26} 80. Ke0 Rh0+ {-0.43/28} 81. Ke1 Rh1+ {-0.39/27} 82. Ke0 Rb1 {-0.46/27} 83. Kd0 Rb0+ {-0.50/28} 84. Kd1 Rb1+ {-0.52/25} 85. Kd0 Rb0+ {-0.54/26} 86. Kd1 Rb3 {-0.54/27} 87. Hg4 Rb1+ {-0.65/26} 88. Kd0 Rb0+ {-0.69/24} 89. Kd1 Rb3 {-0.77/25} 90. Hh6 Rf3 {-0.81/26} 91. ab5 Rf1+ {-1.03/23} 92. Kd0 Rf0+ {-1.27/27} 93. Kd1 Rf1+ {-1.36/25} 94. Kd0 Rf6 {-1.46/24} 95. b6 Ea7 {-1.67/23} 96. b7 Eec5 {-2.10/22} 97. bc7 Rg6 {-2.92/23} 98. c8 Rg0+ {-3.82/25} 99. Kd1 Rg1+ {-4.58/26} 100. Kd0 Rg0+ {-3.22/31} 101. Kd1 Rg1+ {-4.61/26} 102. Kd0 Rg7 {-5.28/27} 103. Ke0 Rg0+ {-7.07/27} 104. Ke1 Rg1+ {-1.23/29} 105. Ke0 Rg0+ {-0.78/29} 106. Ke1 Rg1+ {-0.86/27} 107. Ke0 Rg6 {-0.98/26} 108. cd8 Ee7 {-1.00/27} 109. Rd6 Rg0+ {+0.00/64} 110. Ke1 Rg1+ {+0.00/64} 111. Ke0 {Draw} 1/2-1/2

shuangqiao999 commented 3 years ago

你好 象棋引擎有没有配置文件?比如配置线程 残局库之类的 感谢您能回复

ianfab commented 3 years ago

Thanks for reporting. The AXF rule perhaps is what other programs use, I guess?

Since in engine games there usually are no claims but rather automatic adjudication, it is hard to determine the claiming side though. Therefore, I guess in practice the formula to determine the effective ply count condition in pseudo-code could be something like ply - 2 * max(max(redChecks, blackChecks) - 10, 0) >= 100.

One caveat is that this rule likely can not be properly reflected in the FEN. Or do you know how other engines or GUIs handle this?

cloudfish commented 3 years ago

Ideally the engine should use the protocol's "offer draw" command both to claim draws by 50-move rule, and to offer ordinary draws. It's the GUI's resopnsibility to check if a graw can be granted. This is Xboard's convention. However UCI doesn't support draw offers.

TianTian Xiangqi uses something like ply - 2 * max((redChecks + blackChecks) - 20, 0) >= 120 in online human vs human matches. (rule, in gameplay 1.(5)) I think it's better to grant a draw only if it can be granted when either side claims; it is similar to the condition you proposed, but slightly different. If red has made 51 moves since last capture, 11 of which are checks, and black has made 50 moves, then either side can be granted a draw if he claims, but the effective ply in your formula will be 99.

The UCCI protocol assumes FEN is the position just after the last capture, and moves are the subsequent moves, when the GUI sends position fen FEN [moves ...]. Information provided by FEN alone will be inadequate, like in chess, you can't properly handle 3-fold repetiton using just FEN. In UCI you use position startpos moves ....

TianTian Xiangqi's integrated engine uses a different adjudication system than human vs human matches, and it can't properly handle 50-move rule, either. When I played the game above, it adjudicated the game to a draw at the same time as Fairy-SF. This engine is a depth-limited version of XQMS. So I'm afraid the original XQMS engine cannot handle this either. XQMS is among the weaker commercial engines nowadays. I wonder how CCyclone or BugChess will handle this.

@shuangqiao999 The options of Fairy-SF are exposed to the GUI. In user-friendly GUIs the number of cores can be set in menus, etc. If your GUI needs a configuration file to configure the engines, you can get all the available UCI options here and here. Fairy-SF currently does not support endgame tablebases in Xiangqi AFAIK. You can ask further questions in discord, there is a 中文 channel so you can ask in Chinese there. Or you can use discussions if you have difficulty connecting to discord, but please raise your questions in English there so that everyone can understand the discussion.