Check out Grant Acedrex, our featured variant for April, 2024.


[ Help | Earliest Comments | Latest Comments ]
[ List All Subjects of Discussion | Create New Subject of Discussion ]
[ List Earliest Comments Only For Pages | Games | Rated Pages | Rated Games | Subjects of Discussion ]

Comments/Ratings for a Single Item

EarliestEarlier Reverse Order LaterLatest
Play-test applet for chess variants. Applet you can play your own variant against.[All Comments] [Add Comment or Rating]
🕸Fergus Duniho wrote on Sat, Jan 9, 2021 05:21 PM UTC in reply to H. G. Muller from Fri Jan 8 09:13 PM:

I have the feeling something changed in the implementation of the GAME-code askpromote command that broke it.

Not exactly broke. More that your expectations about how it works are not up-to-date.

Above the Pawn there is a message that says it declines promotion. I don't recall having seen that before.

This appears only where there is an option to not promote.

After you make the choice and submit it through the button, it seems the original move is gone. The askpromote screen is supposed to append the choice to the move you had so far, in the form "; X-dest" (where X is the label of the chosen piece). But here it seems to replace the move by that, so that this now becomes the only move primitive. Which is of course invalid.

I am not aware that it replaces anything. Looking at the code for askpromote, which you can view yourself here, I only see where it keeps the original move without appending anything additional to it. This is for when the piece does not promote. Adding something like P-dest to the end of the move is an ugly kludge that I figured out how to do without. If you take a look at the Pawn subroutines in the fairychess include file, you will find code that handles optional Pawn promotion without the need to append a fake promotion to the end of the move. I believe the critical code looks like this:

if onboard where #to 0 #pzs: // Not yet in promotion zone
  if != space #to $moved:
    set name alias const alias $moved;
    die "You may not promote a" #name "until it reaches the promotion zone.";
  endif;
elseif onboard where #to 0 1: // Not yet on last rank
  if == White_Pawn const alias space #to and count var wprom:
    if not $answered and == mln $maxmln:
      push wprom space #to;
      askpromote #wprom;
  endif;
elseif ...

The first if-block is executed if it is not yet in the promotion zone. So, each subsequent elseif-block can be executed only if it is in the promotion zone. The second elseif-block executes if it is not yet on the last rank. So, if it does execute, the Pawn is in the promotion zone but not yet on the last rank. It then checks whether there is anything to promote to. If there is, then it checks two more things. And these are the important things to pay attention to. It checks whether this is the last move by comparing mln with $maxmln, and it checks whether askpromote has already been used on this move by checking the $answered flag. As long as it is the last move, and askpromote has not already been used on this move, it adds the Pawn to the promotion options and calls askpromote. Because it checks these two things, it does not have to check whether the move already contains a promotion, and that allows a player to decline a promotion without adding a fake promotion (or a skip or pass) to the move.


💡📝H. G. Muller wrote on Sat, Jan 9, 2021 06:47 PM UTC in reply to Fergus Duniho from 05:21 PM:

Have you tried the link that Daniel posted? (You would have to scroll down beyond the debug output, which is still there because of the 'resign' problem.)

What the included code does is very similar to what you describe. If the moved piece is promotable it first looks up a (rank-dependent) array of choices in #choice (which might or might not include a Pawn, and would be 0 outside the zone), and then it does:

  if #promo:                         // move already specifies promotion
    if not #choice:                  // apparently outside the zone
      die "You cannot promote on this move";
    elseif not match #promo #choice: // or to a non-allowed piece
      die "You cannot promote to such a piece";
    endif;
  elseif #choice and not $answered:  // it should promote, but doesn't yet
    askpromote #choice;
  endif;

(#promo contains the promotion choice that was specified in the input move). If a choice was already specified, it is checked whether that choice is allowed. If not, and the promotion question was not already $answered (it could be if the user declined), and a choice is possible, it presents the choice through askpromote.

Note that the problem does NOT occur when you decline. It occurs when you choose to promote. (But only in games where there also is the option to decline.) When, in Daniel's link, I choose a white Pawn, it does play P g7-g8. But when I choose a white Zebra it dies, showing a game record that ends in:

 39. Y g2-f7 39... k g8-f9 40. P g7-g8 40... ; Z-dest

So it has treated the Z-dest as a new move, for black! (Which my move parser of course doesn't recognize as a valid move.; hence the die.)

[Edit] I call this code from the Post-Game section, though. Could that make the difference? The same code does work without problems in the Mighty-Lion preset. And I recall having tested it too for cases that allow deferral.


🕸Fergus Duniho wrote on Sat, Jan 9, 2021 08:33 PM UTC in reply to H. G. Muller from 06:47 PM:

I call this code from the Post-Game section, though. Could that make the difference? The same code does work without problems in the Mighty-Lion preset. And I recall having tested it too for cases that allow deferral.

If you were checking the move line number with mln, it wouldn't work in the Post-Game code. The value of $mln is regularly updated by the moveindex command, which executes between moves. In the Post-Game code, mln should be equal to $maxmln. So, you would need another method for checking whether it is on the last move.

Note that the problem does NOT occur when you decline. It occurs when you choose to promote. (But only in games where there also is the option to decline.) When, in Daniel's link, I choose a white Pawn, it does play P g7-g8. But when I choose a white Zebra it dies, showing a game record that ends in:

The source code for the page shows me that the original move has not been included in the move. It just includes a blank space before the semi-colon. The value there should have been supplied by the variable $moves, and this value is entered into the Moves field when you submit a form to move. The problem may be that askpromote is being called a second time, since it should be used only when someone has submitted a move. Since this link has a query string, that cannot be the case here, for a move is always entered via POST, not GET.

Before calling askpromote, you have to check for two different things. Check that this is the very latest move, and make sure askpromote has not already been called for this move. I do not see anything in your code that is checking whether it is the very latest move. Since you are running all the moves in the Post-Game section, you will have to come up with a different method than I use. Perhaps you can count how many moves there are and allow it only on the move whose index matches the last move.


💡📝H. G. Muller wrote on Sat, Jan 9, 2021 10:09 PM UTC in reply to Fergus Duniho from 08:33 PM:

In the Post-Game code, mln should be equal to $maxmln. So, you would need another method for checking whether it is on the last move.

The Post-Game code is only executed after the last move. So I shouldn't have to check anything, other than whether the preceding move could be a promotion. The preceding move is guaranteed to be the very latest move.

Since you are running all the moves in the Post-Game section, you will have to come up with a different method than I use.

I don't run any moves in the Post-Game section. Each move is run in the Post-Move section. The Post-Game section just tests whether the latest move (which could be the one the user entered, and was not checked before) is legal. That includes checking whether any specified promotion is legal, or whether it is legal to not specify one.


🕸Fergus Duniho wrote on Sat, Jan 9, 2021 11:36 PM UTC in reply to Daniel Zacharias from Fri Jan 8 07:38 PM:

Something seems to be wrong with this game. When I declined to promote my pawn, the promotion options keep showing up for both players. I did have to modify the promotion options from the generated code to get them the way I wanted, but it all seemed to be working before.

I figured the problem was that it was running askpromote at the wrong time. It should run when a move has just been entered, but it should not run when a move has not been entered. To fix this, I modified it to do nothing if the $moves string is empty, and the game you linked to now works correctly.


Daniel Zacharias wrote on Sun, Jan 10, 2021 05:48 AM UTC in reply to Fergus Duniho from Sat Jan 9 11:36 PM:

thanks!


💡📝H. G. Muller wrote on Sun, Jan 10, 2021 08:29 AM UTC in reply to Fergus Duniho from Sat Jan 9 11:36 PM:

@Fergus: Just for my understanding: Was this a consequence of calling askpromote in the Post-Game code rather than the Post-Move code? Would the code you posted from the fairy include be immune to this, or would it have suffered from the same problem?

Please note that the problem with resign still has not been solved. Resigning still draws an error message. (In Play mode; I did not test it in a two-player game.)


🕸Fergus Duniho wrote on Sun, Jan 10, 2021 03:50 PM UTC in reply to H. G. Muller from 08:29 AM:

Just for my understanding: Was this a consequence of calling askpromote in the Post-Game code rather than the Post-Move code? Would the code you posted from the fairy include be immune to this, or would it have suffered from the same problem?

As far as I know, the code from the fairychess include file was immune to this, as it has never had this problem. It probably had something to do with placing your code in the Post-Game section, but I did not analyze it enough to determine what the exact problem was, for I did not try to debug your code.

Please note that the problem with resign still has not been solved. Resigning still draws an error message. (In Play mode; I did not test it in a two-player game.)

The resign command has no use in Play mode, and its behavior there is undefined. If you're not testing it in a two-player game, you're not testing it.


Greg Strong wrote on Sun, Jan 10, 2021 04:16 PM UTC in reply to Fergus Duniho from 03:50 PM:

It doesn't work in a real game either. Here is the game I'm trying to resign:

https://www.chessvariants.com/play/pbm/play.php?game=Kinglet&log=mageofmaple-cvgameroom-2020-357-122&userid=mageofmaple


🕸Fergus Duniho wrote on Sun, Jan 10, 2021 06:51 PM UTC in reply to Greg Strong from 04:16 PM:

It doesn't work in a real game either.

The error message you're getting is coming from H. G. Muller's code. It's up to him to debug that.


💡📝H. G. Muller wrote on Sun, Jan 10, 2021 11:01 PM UTC in reply to Fergus Duniho from 06:51 PM:

@Fergus: No, as I reported before, the error message comes because the resign command does not work as you advertized. It does not terminate the execution of the GAME code.


🕸Fergus Duniho wrote on Mon, Jan 11, 2021 02:38 AM UTC in reply to H. G. Muller from Sun Jan 10 11:01 PM:

No, as I reported before, the error message comes because the resign command does not work as you advertized. It does not terminate the execution of the GAME code.

Okay, I have now fixed it. I have added code after each recursive call of runsubroutine() that immediately returns if $exit is true.


Greg Strong wrote on Mon, Jan 11, 2021 03:39 AM UTC in reply to Fergus Duniho from 02:38 AM:

Confirmed. I was able to resign the game. Thanks!


💡📝H. G. Muller wrote on Mon, Jan 11, 2021 07:14 AM UTC:

@Fergus: Great! That still leaves the problem of why the piece label Xdummy compares as equal to 1.


🕸Fergus Duniho wrote on Mon, Jan 11, 2021 02:43 PM UTC in reply to H. G. Muller from Sun Jan 3 07:08 PM:

if == #hit Xdummy:

As the preceding print shows that #hit contains 1 here, how can it compare as equal to the constant string Xdummy???

Presuming that Xdummy is supposed to be a constant, you can access its value with either const Xdummy or @Xdummy. By itself, Xdummy is just a string literal.

I ran two tests on your code. In one, I set hit equal to one, and I got the error message "This exposes your royal piece to capture." In the second, I set hit equal to true, and I got the error message "Trading of this piece is not allowed." Here's what's happening. The variable hit had the value of true, not the value of 1. Despite this, it prints the value of true as 1, which made things confusing. When it actually had the value of 1, the string literal Xdummy was not equal to it. But when hit had the Boolean value of true, the string literal Xdummy was evaluated as equal, because the == operator cast the string literal to its Boolean value before comparing for equality with a Boolean value.


💡📝H. G. Muller wrote on Mon, Jan 11, 2021 04:04 PM UTC in reply to Fergus Duniho from 02:43 PM:

Ughh, I would never have thought of that. It seems strange to cast operands to boolean, since it is the least informative of all data types, and makes the == operator least discriminating. I'd better avoid the usage of true and false in the future, and use 0 and 1. Or would that cast the string literal to a number before the comparison, so that I would still have the same problem?

Isn't there an operator to compare without any casting? (I.e. which evaluates to false whenever the types of the operands is not equal?)

In any case, the preset will now print the correct error message. I even diversified it, so that you get another message for moving through check and exposing your King to normal capture.


🕸Fergus Duniho wrote on Mon, Jan 11, 2021 06:31 PM UTC in reply to H. G. Muller from 04:04 PM:

It seems strange to cast operands to boolean, since it is the least informative of all data types, and makes the == operator least discriminating.

This is built into PHP. The == operator is capable of being used between values of different types. When two values are of different types, it will typically cast the more complex one to the simpler one.

Isn't there an operator to compare without any casting? (I.e. which evaluates to false whenever the types of the operands is not equal?)

Yes, it is ===.

Here is a test script and its output:

set a true;
set b int 1;
set c 1;
set d TRU;
set e int 0;
set f 0;
print . "d has the integer value of " int var d;
sub compare x y:
  if === var #x var #y:
    echo #x and #y are identical;
  elseif == var #x var #y:
    echo #x and #y are equal but not identical;
  else:
    echo #x and #y are unequal;
  endif;
endsub;

gosub compare a a;
gosub compare a b;
gosub compare a c;
gosub compare a d;
gosub compare b b;
gosub compare b c;
gosub compare b d;
gosub compare c c;
gosub compare c d;
gosub compare d d;
gosub compare d e;
gosub compare d f;

OUTPUT:

d has the integer value of 0
a and a are identical
a and b are equal but not identical
a and c are equal but not identical
a and d are equal but not identical
b and b are identical
b and c are equal but not identical
b and d are unequal
c and c are identical
c and d are unequal
d and d are identical
d and e are equal but not identical
d and f are unequal

Here's what's going on. a is a Boolean, b and e are both integers, and the rest are strings. When b, c, or d is compared with a, the == operator casts it to its Boolean value, which is true. When a string is compared with a number, the == operator casts the string to its numeric value for the comparison. If the string does not match a decimal number, its numeric value will be 0. This is equal to e, which is numeric. When two strings are compared, there is no casting, and they are compared as strings. This is why d and f are unequal.

The == operator is useful when you think you might be comparing numeric strings with actual numbers. The === operator is useful when it is important for the values being compared to be the same type. It is thanks to using the == operator that a bug was found in your code. If you had used the === operator, that bug would have remained hidden longer. Remember that the bug had to do with using a string literal as though it were a constant, not with using == instead of ===.


Daniel Zacharias wrote on Mon, Jan 11, 2021 08:26 PM UTC:

I'm trying to define a pawn that can move forward two spaces any time there is a piece immediately behind it, and can be captured en passant. I have tried fmWfceFbpabmamfamfnW and fmWfceFbpabmamfnamfW but neither work. Should I be putting the n somewhere else? Does it not work in this way?

I also tried an en passant capturable rook with mnRceR but that didn't work either.


💡📝H. G. Muller wrote on Mon, Jan 11, 2021 10:53 PM UTC in reply to Daniel Zacharias from 08:26 PM:

I will still have to do some work on the Diagram before the n modifier can be used to do real e.p. captures, rather than as a device for preventing royals can pass through check. The problem is that the Diagram currently uses an old and a new move generator, the latter only for the AI. These use different methods for encoding e.p. squares (just the two end-points of a ray, versus a list of squares). Translating one representation into the other when the e.p. starts thinking on a move does work for the normal Pawn case, but is not implemented generally enough to do the more complex things that can be done with the n modifier. Which the old system would not do anyway; that just checks whether the path of a slider leg of a royal contains an attacked square (which are marked previously), to decide whether the highlight should be grey.

My plan is to abandon the old system, and make the input of user moves controlled by the move generator of the AI. This would make all problems go away. But I haven't gotten to doing that yet.


💡📝H. G. Muller wrote on Mon, Jan 11, 2021 10:57 PM UTC in reply to Fergus Duniho from 06:31 PM:

@Fergus: Thanks for the explanation; this will help me avoid many errors in the future! I am used to C, where boolean values are cast to the numbers 0 and 1, rather than the other way around.

Remember that the bug had to do with using a string literal as though it were a constant, not with using == instead of ===.

No, I used the string literal as a string literal; perhaps I expressed it wrong because I am not used to GAME-code terminology. I wanted to know if the variable 'hit' contained the text "Xdummy". The === operator would have done that.


Daniel Zacharias wrote on Mon, Jan 11, 2021 11:13 PM UTC in reply to H. G. Muller from 10:53 PM:

Ok, thanks But I'm still wondering, even if it doesn't work yet, what would be the correct way to define that? Should the n be on the last step or the one before that?


💡📝H. G. Muller wrote on Tue, Jan 12, 2021 10:14 AM UTC in reply to Daniel Zacharias from Mon Jan 11 11:13 PM:

The idea is that a stepper leg with n modifier would create the rights at the square(s) it steps to, not on the square it departs from. So that nR can be used on a royal that cannot pass through check, but is still allowed to make the move out of check. Formally this also makes the final destination of the move an e.p. square, but that doesn't do any harm; normal (replacement) capture can be considered a 'degenerate case' of e.p. capture, where the moved piece still happens to be on the e.p. square. For non-final legs it creates a real e.p. square, though. So for FIDE Pawns the double push should be written fnafmW.


Greg Strong wrote on Tue, Jan 12, 2021 02:46 PM UTC in reply to H. G. Muller from 10:14 AM:

I thought n was for lame leaps. The Elephant in Chinese Chess would be nA, but it can't be captured en passant on the square passed over.


💡📝H. G. Muller wrote on Tue, Jan 12, 2021 03:28 PM UTC in reply to Greg Strong from 02:46 PM:

That is also true. But the Diagram has always used the convention that in combination with i, the n would create e.p. rights on the squares it can be blocked, to make it possible to describe a FIDE Pawn with an ifmnD component. And to make Metamachy Pawns possible, I later added the convention that nn had the same effect (i.e. not only make the move lame, but also rights-creating). So nnA would be a XQ elephant that can be e.p. captured (by fsmceW XQ Pawns...).

But the n had no meaning on the stepper atoms W and F, and their sliding multiples R and B. And the in and nn conventions are really unsatisfactory, because the n on which they are based is: they are ambiguous for oblique moves. XBetza solves the lameness problem by explicitly specifying the path through multiple W or F legs, with m-only capability on the intermediate squares where blocking can occur (and mp on squares that are jumped over). So it would be natural to also allow explicit specification of the squares where e.p. rights are created. And the n (which already had a similar meaning in the other contexts, albeit only after duplication) was available for overloading.

So nW now means creation of e.p. rights on the destination square of the W step (only meaningful on non-final legs, as otherwise the piece would remain there to be captured anyway), and nR or nW4 creation of rights on the destination of every W step that makes up the total R move. Together with the convention that c moves can always e.p.-capture royalty (and e is only needed to enable e.p. capture of non-royals that created rights), this makes it possible to use n for defining sliding royals that cannot move through check.


Greg Strong wrote on Tue, Jan 12, 2021 06:36 PM UTC in reply to H. G. Muller from 03:28 PM:

Thank you for the detailed explanation


25 comments displayed

EarliestEarlier Reverse Order LaterLatest

Permalink to the exact comments currently displayed.