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
591 stars 186 forks source link

Janggi ideosyncracies (XBoard compatibility) #192

Open HGMuller opened 3 years ago

HGMuller commented 3 years ago

Now that I have added 'janggi' as a standard variant to XBoard, the following points deserve attention:

HGMuller commented 3 years ago

A question: when the Bikjang rule is not used, would it be legal for Kings to face each other (as in Chess) or illegal (as in Xiangqi)?

ianfab commented 3 years ago

Thanks a lot for your feedback.

Regarding bikjang, in Janggi it is never prohibited to cause the kings to face each other irrespective of the rule set. Whether bikjang is used or not then just decides if the game continues or might be adjudicated. However, when bikjang is used, it is illegal to make a move that is neither a pass (accepting the bikjang) nor breaks the bikjang situation, e.g., you can't just move the king forward or backward.

gbtami commented 3 years ago

Wasn't coding pass move as king moves in place was chosen because they just do the same OTB (turn king to other side)?

HGMuller commented 3 years ago
HGMuller commented 3 years ago

And out of curiosity: how do the rule variations affect move generation? It seems a moot issue whether moves other than passing or breaking Bikjang are illegal or also valid acceptance of the draw.

ianfab commented 3 years ago

Since draws in Janggi usually are decided by material counting (at least in tournament play), it is relevant whether a player in his last move accepting bikjang can still capture a piece to change the material balance, or can only pass. Furthermore, when playing without bikjang, passing is never allowed when in check, but in a bikjang situation it is (see https://github.com/ianfab/Fairy-Stockfish/issues/94).

HGMuller commented 3 years ago

OK, I see. But I guess that the problem arises because it is really a misconception to view the acceptance of the Bikjang offering as a move. It is really just accepting the (implied) draw offer. In FIDE rules, when someone offers you a draw on a checking move, you can also accept the draw without the need to resolve the check first. That you signal the acceptance by flipping your King has no more significance than the habit to knock down your own King to indicate resignation.

HGMuller commented 3 years ago

I suppose it still will need some consideration that staying in Bikjang is illegal if you decline the offer. I guess I would implement this by recording the presence of the King connection as a separate bit flag from being in ordinary check in the (ply-indexed) stack I have to maintain anyway for detecting perpetual checking. In positions for which this flag is set, you can declare a win (King-capture score) if it was also set in the preceding ply (and otherwise ignore it). It is like a 1-ply-delayed check. If the rule doesn't apply, you just skip the test for the connection so that the flag gets never set. This assumes pseudo-legal move generation, though, which rejects the moves by testing for King-capture (including a pre-existing Bikjang) after the moves are made.

HGMuller commented 3 years ago

I ran into some unexpected trouble. Beware that this was with the old Fairy-Stockfish .exe I downloaded from here two days ago, which still plays janggi as engine-defined variant. (I never managed to compile Stockfish myself, and I am not sure if the latest changes are already available as Windows binary.)

I noticed that Fairy-Stockfish uses N for Horse and B for Elephant in FEN. Is this standard in Janggi? The XBoard implementation I made was using H and E, as in Xiangqi. So SF got quite upset when WinBoard sent it a FEN. (Which it accidentally did as a kludge to avoid having to send a null move.)

Another problem is that when I finally fixed XBoard to send the null move, SF did not accept it. From the debug log:

18290 >first : usermove @@@@ 18298 <first : Error (unkown command): @@@@

ianfab commented 3 years ago

Fairy-SF should not have any trouble with H or E in FENs for Xiangqi or Janggi. The background is that in Xiangqi in UCCI N/B are used, while CECP uses H/E, which is why Fairy-Stockfish internally uses N/B but allows H/E as aliases on input FENs in order to be compatible with CECP (it btw also uses these aliases for SAN). The same convention is also applied to Janggi. The issue might be that Fairy-SF confuses XBoard if it sends the setup and piece commands using the "wrong" symbols. This usually does not happen, since Fairy-SF skips sending those commands for standard XBoard variants (e.g. Xiangqi), but it of course does not yet for Janggi since it used to be an engine defined variant.

To my knowledge there is no standard, since I am not aware of any UCI, UCCI, or CECP engine for Janggi apart from Fairy-SF. The only other relatively well-known Janggi engine is Janggidosa, which however comes with its own GUI and does not expose any text protocol for engine communication.

The workaround that XBoard sends an FEN after a null move currently is handled explicitly in Fairy-SF by detecting whether the new FEN only differs to the current one by the side to move, and if so applies a pass move instead of setting up a new position. This circumvents the issue of not being able to undo a passing move, but from my perspective it would be preferable to directly send a pass move.

Yes, currently Fairy-SF uses "pass" and therefore does not understand "@@@@" as a passing move, I should change that. Oh, there also is a typo in "unknown", I should fix that as well :).

Edit: The current version from branch https://github.com/ianfab/Fairy-Stockfish/tree/xboard_janggi (Janggi as standard variant, @@@@ for pass move) is attached. fairy-stockfish-largeboard_x86-64.zip

HGMuller commented 3 years ago

After some experience trying to play Janggi with Fairy-Stockfish & XBoard/WinBoard, I can say the following things:

The CECP 'piece' commands are really a bad idea in Janggi. The moves they can specify are really too poor an approximation for the complex Janggi rules. (Palace confinement, diagonals, ban on CxC.) The moves indicated by the also supported highlight protocol would overrule them anyway as far as highlighting is concerned, and now that Janggi is a standard variant and XBoard has the exact moves built in, they do more har than good. In the standard variant itself XBoard can defend itself against an engine's 'piece' commands by switching legality testing on, but in engine-defined variants there is no defense, and 'piece' commands always overrule built-ins. And janggicasual etc. are still engine-defined. The piece commands there just wreck notation and checkmate detection.

So I would like to see an if(v->variantTemplate == "janggi") return; in ucioptions.cpp just before sending any piece commands. The stuff about the v->diagonalLines there can then all disappear, as I think only Janggi variants would ever use that. I cannot envision that anyone would ever want to use Janggi as a parent variant for anything other than Janggi, but if there would be demand for that, we would just have to suppress the piece commands for piece types normally present in Janggi.

Then there is the pieceToCharTable in the setup command, which serves two purposes: defining piece symbols and ID letters. In Xiangqi this never mattered much, as the setup command is completely ignored in a standard variant with legality checking on. (It is after all not legal to change the rules.) And there never is any reason to switch legality checking off in Xiangqi. So it doesn't matter whether the engine uses BN or EH, as long as in understands both on input FENs. In Janggi, however, there is the issue of the 4 rule variations, and 3 of those are played as engine-defined variant. And in engine-defined variants XBoard always takes the info in the setup command for gospel, as it is suppoed to have no clue about the rules itself. There really is no defense against that.

So it is very annoying if the engine changes the ID, altering move notation in 3 of the 4 sub-variants, compared to the standard Janggi. But even more disastrous is when it alters the symbols: this breaks the Polyglot opening book, as XBoard calculates the hash key by symbol rather than ID. (As the pieceToCharTable is also intended to adapt to local notations such as S = Springer etc.) So I would like the Janggi pieceToCharTable to be changed to "PH.R.AE..K.C.ph.r.ae..k.c." to make it possible for all 4 Janggi sub-variants to use the same opening book, and the same opening book that would work (and could be created) by XBoard with no engine loaded at all.

ianfab commented 3 years ago

I generally try to avoid hard-coding any variant-specific logic as much as possible, since that unavoidably causes more issues and work at some later point, but I think in this case there is a relatively safe and isolated workaround by returning a hard-coded setup command in case all of parent variant, starting FEN, and piece types are matching to Janggi like in https://github.com/ianfab/Fairy-Stockfish/commit/ca17aa027c1bdedec016a2482af060fbc0e6ab56. This should be very unlikely to interfere with any user-defined variants, so I would be fine with this change if it is sufficient from your point of view. That would also allow to remove Janggi from the "standard variants" so that the setup command is still sent, which avoids completely breaking backwards compatibility with XBoard versions that do not have Janggi as a build-in variant.

HGMuller commented 3 years ago

I agree it is a hard problem to do this in a clean way. One could suppress all 'piece' commands of pieces that normally occur in a parent variant, and have the same move. This supposes that the engine has some way to compare move descriptions, and that it implements every variant that is used as a parent variant. (Which is true for the Janggi group, but need not be generally true.)

Janggi / Xiangqi is a bit of a special case, because the built-in moves there are of a type that cannot be conveyed by the piece commands. It is this non-representability (e.g. due to confinement to zones) that causes the problem; if the piece command would just send the move that was already built in, there is no harm in that. (There was for the Pawn in Xiangqi, but that was unintended and qualified as a (now fixed) XBoard bug.) The only pieces with unrepresentable built-in moves are E and A in Xiangqi, and P, A, C, R and K in Janggi. Perhaps we should declare these piece symbols 'sacred' in these parent variants, and never send any piece commands for them. If someone wants to define a variant that uses Xiangqi/Janggi as parent, but which contain pieces that move different from the pieces originally present, there are plenty of other symbols to choose from.

I don't see any possibility to stay compatible with XBoard versions that do not support Janggi: to do that you would have to specify Xiangqi as a parent.

ianfab commented 3 years ago

I commited the change to skip piece commands and change the setup command for Janggi variants to master. I will perhaps also remove the highlight of passing moves, but I have a question regarding that. @HGMuller In edit and analysis mode one can play a passing move by clicking the clock, but when playing that tries to claim a win on time instead and fails. So how does passing work when playing?

cjssh1002 commented 3 years ago

Yes you are right, this is buggy. If you use pass in analysis mode, the turn does not change. There is no problem in human VS AI. I click the clock to "pass".

HGMuller commented 3 years ago

This was an XBoard bug in the initial Janggi implementation. For clock clicking to work as turn pass in playing mode the current (parent) variant must be indicated as allowing null moves, and I had not made that indication for Janggi yet. A problem here could be that you are using XBoard from my on-line git repository, which might not have all patches in them that I applied directly to the source files I have on Windows, to make new WinBoard-AA packages for cjssh1002 to test. And there is a lot of patches (in connection with book creation / probing) that don't work yet.

But I will check and make sure the 'enable null move' patch is committed to my on-line repository.

[Edit] Well, it seems the patch is present there, as forelast commit in the v4.9.x branch.

ianfab commented 3 years ago

Oh, thanks, seems like I had used the version from two commits earlier in testing, now it works.

So from my perspective the only point that is still open here is to potentially implement a way to alter the starting position either via shuffling or a user dialogue.

HGMuller commented 3 years ago

When this issue came up in Musketeer Chess, I made a slight enhancement of the 'setup' command to conduct such dialogs by graphical means. An alternative would be to conduct it as text, through the CECP 'askuser' command. But that would still pose a problem, namely that in an engine-defined variant XBoard expects a 'setup' reply to 'variant', and before the dialog is completed the engine doesn't know what initial position to specify. I suppose it would be possible for the engine to use 'askuser' and wait for the reply before it answers the 'ping' command XBoard uses to test if a 'setup' reply can still be expected.

Anyway, I made it possible for engines to indicate a 'temporary setup' by prefixing the parent-variant name with an exclamation point. Such a temporary setup will be accepted as a reply to the 'variant' command, but it will not make XBoard ignore any further 'setup' commands, until a final one (without '!') is received. This allowed the engine to show arbitrary board positions to the user, while the highlight protocol informs it what the user clicks on those through 'lift' commands. By replying to the 'lift' commands with a next 'setup', it is prevented the user can ever enter a complete move in this stage. I think this method is highly superior to any text-based dialog, in terms of user experience.

The way it would work in Janggi is that after the variant Fairy-Stockfish would sent a 'setup' with a board that shows the 4 possible white back-rank constellations, as it is set for playing black at that stage, and expects white to choose first. It will then either receive a 'lift' command due to the user picking one, or a 'go' command to play white. (And it will be knowing whether it is playing against another engine, through the CECP 'computer' command.) If a 'lift' command indicates the white choice, the engine can make its own choice for black, (a small internal book for it seems the best way to handle that), send a final 'setup' with the negociated start position, and wait for the user to move. If, OTOH, it receives 'go', it can send a temporary setup that shows the user the engine's chosen white constellation, offering a choice between 4 black back-rank constellations, and start waiting for 'lift' to know which one the user picked. It then replies to that with the final 'setup', and starts thinking.

When playing against another engine it does nothing of this; it just sends the final 'setup' with the standard start position, or a randomly picked one. The GUI willl likely overrule it with a 'setboard' command from a book of opening lines, as engine-engine games are typically not played from the start position, to force game diversity.