A Wizard for GAME-Code Generation
Using the Play-Test Applet to automate Game-Courier presets
When your CV doesn't have extremely exotic rules, making a Game Courier preset enforce the rules and indicate legal moves can be done very easily, using the Play-Test Applet for Chess Variants. This Applet can generate GAME code that would test the entered moves for legality, highlights the moves a piece that you select with the mouse can make, and tests for conditions that should end the game, such as checkmate, stalemate, repetitions or the 50-move rule. This works as follows:
Type the board dimensions and number of ranks of the promotion zone in the text entries above the board diagram. If promotion is not to every non-royal piece, (which is the Applets' default), click that you want to 'specify more rules', and type the (single-letter) IDs of the piece types that should be elegible for promotion in the 'promotion choice' text entry. Click on 'Help' to see what you have to do if not every piece can be chosen in the entire promotion zone at all times. If your variant requires shuffling to randomize the initial position, you can simililarly type the IDs of the pieces that need to be shuffled in the other text entry. (There also is a 'Help' there, which explains how you could specify complex shuffling rules.) The 'more rules' also shows a few checkboxes, which you can tick to indicate the stalemate result, what will happen in the case of multiple Kings, etc. Of most interest is probably the 'Asymmetric setup' checkbox; tick this if the black pieces should not be automatically arranged as the mirror image of the white setup. (This is especially important in shuffle games, where it determines if white and black will be shuffled independently.) After you typed and ticked everything, hit the 'Apply' button to enforce it. The board diagram should then take on the requested dimensions.
The next step is to set up the initial position. Actually, if your only interest is to get GAME code for a preset, (and not a working Interactive Diagram, or a valid FEN code), you only have to place one copy of each participating piece, and it doesn't matter where. To do that, click the pieces you want to place on the board in the table, and then click the squares you want to put them on. If you did not ask for asymmetry, each piece you place will be automatically accompanied by its color-flipped mirror image on the other side. If you misplace a piece, you can move it to another location on the board. You can even capture pieces that you placed by mistake, and want to get rid of, but only through legal moves. After all pieces are placed, you hit the 'Start position' button to let the computer remember it.
When the CV has piece types that are only available from promotion, you would have to place these on the board too, to let the Applet know they participate. You can do that after you pressed 'Start position'. Then they will not spoil the FEN that will be printed when you generate the GAME code, which will be of the start position.
Many piece in the table have predefined moves and names. These might not always be what you need. You can change the name, move and ID of a piece in the table (also after you placed it on the board), by typing the desired values in the text entries under the table, and then clicking on the table cell that holds the move of the piece you want to alter. Data for which you did not enter a new value will remain unchanged. Always use single-letter capitals for the ID that correspond to the piece labels of the preset.
The non-obvious thing here might be to describe the move. You can see what move the piece currently has by clicking on its name in the table; its move diagram will then be temporarily shown on the board. If this is not the move you want, but you want a relatively simple other move (any combination of slides, leaps, hops), you can use the 'move-definition aid' below the board to create the description. By clicking squares you want a piece to be able to move to in the move pane, and then pressing the buttons to the right of it, you can build a description of the move in the 'Betza move description' text entry. And when that is complete, assign it to the piece of choice.
Pay special attention to the King; the default move for this will include a castling, which is automatically adapted (w.r.t. the number of steps taken by the King) to the board width you specified. You might want other, or no castling. In that case you would have to change the isO2 part of the preconfigured move. The number in that represented the number of King steps. If the variant allows several different castling destinations you have to write each of those, e.g. isO2isO3isO4. If castling is not with the piece at the board edge, but with the piece one square closer, add an extra j, e.g. isjO2.
After you corrected and verified all the moves, hit the 'GAME code' button at the bottom of the Applet article. This will make the GAME code that you need to automate a preset for the variant appear in the textbox below it. It will consist of five parts, intended for copy-pasting into the various GAME-code sections of the preset. If you set up the correct initial position, you can also use the FEN code it prints at the bottom to create an entirely new preset.
Alternative code for handling the mouse
Multiple royal or promoting types
The Applet will always assume the piece with the King image is the only royal type. If the variant has several piece types that are royal, you would have to make the GAME code for the preset aware of that. Similarly, the Applet assumes only the first piece type you define can promote. You can configure the GAME code to make several types promote, provided that these all promote in the same way. This would require altering or adding the following lines in the Pre-Game section, adapting the list of pieces they contain. For a game without royal pieces the list with the parentheses has to be replaced by the word array.
set wroyal (K); set broyal (k); set promotables (P p);
Customizing check and draw rules
You can for instance disable the check rule (so that Kings can happily wander into check without the preset complaining), by adding the line
set checkrule 0;
at the bottom of the Pre-Game code. If you want to deviate from the orthoChess 50-move or 3-fold-repeat rule, you can do that by adding one or more of the lines below (but with the value altered to your liking):
set repeats 3; // repetitions to claim game end set reploses 0; // repeater loses set rulemoves 100; // reversible plies to claim draw set resetpieces (P p); // pieces that reset the counter when they move set nullban 1; // forbids moves that do not change the board
If you don't want to enforce one of these rules, just specify a ridiculously large number for it. Setting 'reploses' to 1 makes a 3-fold repetition a loss for the player bringing it about. The 'resetpieces' are those whose moves should reset the 50-move counter. By default these are only Pawns, but you can add other pieces IDs between the parentheses. Never forget to mention the pieces for each player (capital and lower case)! If no piece should reset the counter, add the line
set resetpieces array;
Moves with side effects
If your variant involves moves with side effects, such as locust capture, there are two ways to have the preset handle their entering with the mouse. The default manner would be to enter them as two moves, the first to make the capture in a normal way (i.e. by moving to the square that contains the victim), and then move from there to the intended destination. If the normal capture is also legal, and you want to play that, you can indicate this by pressing the Pass button for the second move. The alternative method is used by pieces that you defined as 'shooters', through a line like
set shooters (L l);
where all the shooters are mentioned between parentheses. These pieces then always have to be moved directly to their final destination. When that can cause a side effect on another square, you will be prompted to enter that. You then have to make a 'fake' move of the piece to the square it should affect. The preset will re-interpret that move as a removal of the piece on, or dropping of a piece on the target square, without actually moving the piece you selected for that second move there. This would be a more natural way for entering moves that capture pieces that are not in your path, or for re- positioning something you captured.
It is also possible to activate enforcement of a variety of anti-trading rules, (which hardly any chess variant would need), through any of the lines
set iron (...); // permanently uncapturable piece types set counterstrike (...); // uncapturable after opponent's was captured set protected (...); // pieces that cannot be traded when protected set negligible (...); // side catch of these does not break trade ban
where the ... is the (space-separated) list of IDs of the pieces that you want the rule to be applied to. (Again, always mention both colors!) The 'iron' pieces can never be captured. 'Counterstrike' pieces become uncapturable for one turn, when a piece within the set is captured by a piece outside the set. The pieces listed as 'protected' cannot be captured by other pieces in the same group when recapture is possible; it is like their capturer becomes an absolute royal for one turn. This only counts for normal captures, not when the capture was a side effect. In fact, when something else was captured in addition to the protected piece, as a side effect, the rule does not apply. Unless that extra piece that was captured occurs in the 'negligible' list.
The interactive diagram supports a lot of different moves, but has its limitations. GAME code, on the other hand, is a Turing-complete programming language, that could describe anything. To not lose that possibility without sacrificing the convenience of using the Applet-generated code, the latter allows you to still provide your own hand-written GAME code for handling a piece. This section explains how to do that. But it would only be understandable for people that know how to program in GAME code.
The first step is to make sure the Applet would reserve some space for your custom piece in the legdefs table. It is best to reserve one line for each direction the piece can start in. To that end you can define the piece with a fake move that has the desired number of directions in the Applet. E.g. a piece with 12 move directions could be given the move NF. The Applet will then reserve 12 lines of 5 numbers for that piece in legdefs. If the piece is not symmetric, you would want to have a separate white and black instance of it, and you can do that by specifying an asymmetric move, like KfhN.
0 1 99 0 1 3 // queen(59) 1 99 1 1 3 1 99 1 0 3 1 99 1 -1 3 1 99 0 -1 3 1 99 -1 -1 3 1 99 -1 0 3 1 99 -1 1 3 0
Next step is to modify the piece its description in legdefs so it will invoke your own code. Above you see a section of a legdefs table describing the moves of an orthodox Queen, and the piece you defined would have something similar. You replace the second number on the line of a move (which normally stands for the range of the move) by the number -3. And you replace the last (5th) number on the line with the name of the GAME-code subroutine that should be called to handle the move. Whenever the Applet-generated code tries to generate that move for the piece, it would call the given subroutine with three arguments: the square the piece is on, and the 3rd and 4th number of the line (which normally represent the leap in x-y form). Your routine could use these numbers for any purpose, and you can change them in the table accordingly; they will not be used for any other purpose than passing to your subroutine. It is suggested, though, that you use them to distinguish the various moves from each other, and have the subroutine you provide not generate all moves for the piece, but just the one leap or ride specified somehow by these numbers.
Note that lines in legdefs for which you do not change the range into -3 keep there original meaning. There is nothing against having some of the moves of the piece be generated by your custom routine, but leave the generation of others to the Applet-generated code. Then you only have to focus on the moves that were too outlandish for the Applet.
The final step is to provide the subroutine to generate the moves. The code below gives an example for how such a subroutine could look; in this case it would generate a leap over an adjacent friendly Pawn.
sub MyPiece startsqr dx dy: my destsqr jumpsqr piece; set destsqr where #startsqr * 2 #dx * 2 #dy; // destination of a leap twice the given size verify onboard #destsqr; // make sure it doesn't leave the board set jumpsqr where #startsqr #dx #dy; // leap once the size (automatically on board) set piece space #jumpsqr; // piece that is there verify samecase #piece space #startsqr; // must be friend to piece on startsqr verify match #piece (P p); // and it must be a pawn gosub GotMove #startsqr #destsqr 0 0 0 0; // process the move endsub;
When you would have changed the section of the legdefs table shown above to this:
0 1 99 0 1 3 // queen(59) 1 -3 1 1 MyPiece 1 99 1 0 3 1 -3 1 -1 MyPiece 1 99 0 -1 3 1 -3 -1 -1 MyPiece 1 99 -1 0 3 1 -3 -1 1 MyPiece 0your 'Queen' would now move as a Rook that would also be able to jump diagonally (as an Alfil) over friendly Pawns. If you wonder what all the zeroes are that are passed to GotMove: these are (respectively) a locust square, a drop square, what is dropped on the latter, and something that you should always leave at 0. If you would have replaced the first 0 by #jumpsqr, the move would have captured the Pawn it jumped over, like in Checkers. Except that it would be your own Pawn, so that would make the move practically useless. OTOH, if you would replace the second 0 by #jumpsqr, and the third by N, jumping over a Pawn would replace it by a Knight. (Always a white Knight, sadly, so if you wanted to use this as a description for the pieces of both colors, you would have to take care to use tolower or toupper to cook the right piece to drop.)
There is one problem with the presented code, though: the legality test for highlighting moves used by the Applet-generated code would not see those moves, and would highlight the square they attack as a possible destination for an enemy King. And when you would already be in check with the move, it would not realize that capturing the Pawn would be a valid evasion, and should be highlighted. The legality test could be made aware of that by adding some extra code, but this is alas not as simple as one could have wished. The code below gives the perfected version:
sub MyPiece startsqr dx dy: my destsqr jumpsqr piece; set destsqr where #startsqr * 2 #dx * 2 #dy; // destination of a leap twice the given side verify onboard #destsqr; // make sure it doesn't leave the board set jumpsqr where #startsqr #dx #dy; // leap once the size (automatically on board) set piece space #jumpsqr; // piece that is there verify samecase #piece space #startsqr; // must be friend to piece on startsqr verify match #piece (P p); // and it must be a pawn if == 9 #task: setelem #destsqr 0 1; // mark destination as attacked endif; gosub GotMove #startsqr #destsqr 0 0 0 0; // process the move endsub;
Task 9 is the code's 'augmented check test', where the moves are not just generated to see if they actually capture a King, but which also marks squares where a King could be potentially captured, had it been there. This cannot be left to GotMove, which is only called for moves that are pseudo-legal. But it can happen that a move that can only capture ends on a square that is empty, e.g. a diagonal Pawn move. Or that a non-divergent move ends on a friendly piece. Those would then not be pseudo-legal, so it would not lead to calling GotMove. But we still should mark the destination as being inacessible to the opponent King.
There is yet another thing the augmented check test must do, namely indicating 'potential pins'. These are riding / sliding moves that are blocked by a piece before they exhausted their range. Moving away that piece then would create attacks on the downstream part of the ride, and it would have to be tested whether this exposes your King. Such moves are put on a stack for the square of the blocker, so that the legality testing can see what moves should be tried to ascertain the moved piece was not pinned when that blocker moves away. The example above did not involve slides, and thus did not need to do this. But suppose we change the move to a ride that can only be made by first jumping over an adjacent Pawn, like a Contra-Grasshopper:
sub MyPiece startsqr dx dy: my destsqr jumpsqr piece tosqrs k; set jumpsqr where #startsqr #dx #dy; // try requested leap set piece space #jumpsqr; // piece that is there verify samecase #piece space #startsqr; // must be friend to piece on startsqr verify match #piece (P p); // and it must be a pawn set tosqrs ride #jumpsqr #dx #dy; // array of squares of the ride set k 0; do while < #k count #tosqrs: // loop through the ride set destsqr elem #k #tosqrs; // next square if == 9 #task: setelem #destsqr 0 1; // mark destination as attacked if not empty #destsqr: // potentially pinned piece on it push #destsqr #ss; // remember the move as potentially pinning this piece push #destsqr #mv; endif; endif; verify not samecase #piece space #destsqr or empty #destsq; // must hold enemy or be empty gosub GotMove #startsqr #destsqr 0 0 0 0; // process the move inc k; loop; endsub;
The code inserted for the augmented check test now pushes ss and mv, which indicate the square from which the move started and the location of the move in legdefs, onto the stack for destsqr when the latter is occupied. This then enables the legality test to reject moves with the occupant when there happened to be a King behind it. You only have to do that for moves that can capture (a King). If your are defining hopper moves, you would likewise have to push ss and mv on the stack of the empty squares the pre-hop leg of the move passes through, because occupying such squares would alter the hopping move, and thus potentially activate a hopper attack on the King.
A good place to do this is in the Post-Game sections, after the calls to HandleMove. There the variables ori, desti, mover and victim will have been set to the origin and destination square of the move, the label of the moved piece, and the captured piece, respectively. Any side effects will be available in the variables suicide, locustvictim, freedrop and dropped, which would be 0 in case the corresponding side effect is absent.
You could add code there that tests these variables for the condition under which the piece-type change should occur, and then order Game Courier to apply the corresponding change to the board. E.g. to implement 'contageon' in Werewolf Chess, where every non-royal piece that captures a Werewolf would change into a Werewolf, you could add the code
if != K #mover and == w #victim: // non-royal captures black Werewolf add W #desti; // change it into white one endif;
and similar (color-reversed, i.e. using opposite case for W and K) code in the Post-Game 2 section.
The identity of a piece, i.e. the description of its moves, is stored in the array legdefs. The Applet-generated code includes a function for each piece to specify where in legdefs the corresponding identity can be found. Each piece type thus has its own set of moves, and the normal way to change that is by changing the piece type, i.e. promotion. Sometimes this is not convenient, though, because the piece type would change outside the promotion zone that the Pawns use, or perhaps even depends on things that happen elsewhere on the board. Also, promotion would change how the piece is depicted in the board diagram shown to the user.
An alternative to promotion is to store two different descriptions for a piece in the legdefs table, and then redefine the function for that piece type to point to one or the other. Or make a slightly more complex function that decides which set of moves to use dynamically. This is a good way for pieces that temporarily should move differently, without having to change their appearence and then change it back. E.g. if you have a piece X that you want to move as a Rook on light squares, and as a Knight on dark squares, you could define X as a Knight, and make sure that the Applet also generates moves for a normal Rook, by putting a Rook on the board before you generate the GAME code. And then redefine the functions X and x the Applet puts in the generated code from
def X cond #0 NNN 0;
(where NNN would be the index in legdefs where the Knight moves are described) to
def X cond #0 (cond & 1 + file var ori rank var ori RRR NNN) 0;
(where RRR is the index in legdefs of the Rook you defined, which can be found in the comment the Applet puts behind those, in the parentheses). This replaces the fixed move NNN by a choice between RRR and NNN, made on the basis of a rather complex expression, which calculates the shade of the origen square (which in the code used by the Applet is called 'ori').
Another example where this technique can be useful is the Bishop Conversion Rule. This rule specifies Bishops can move on their initial move as either a Bishop or Wazir, but the two Bishops cannot start in the same way. One way to implement that is by having separate definitions of a Bishop, a Wazir and their compound in the legdefs table, and let the Bishop function dynamically decide for virgin Bishops which of the definitions to use. You would need an extra variable for each player to remember whether one of the possibilities has already been used up by the other Bishop, and if so, which one. If nothing has been done yet, a virgin Bishop moves like the compound. But if a Wazir move was done, it should move like a Bishop, and vice versa. Non-virgin Bishops would always move like a Bishop.
// in the Pre-Game section: set many wstart 0 bstart 0; // indicate no Bishop has moved yet // in the Post-Move sections, after the gosub HandleMove if == B #mover and not flag #ori: // a virgin Bishop moved set wstart cond checkleap #ori #desti 0 1 1 2; // 1 = as Wazir, 2 = as Bishop endif;
That is for the Post-Move 1 section (white moves); in Post-Move 2 you would use b and bstart instead of B and wstart. The 'flag' on a square tells you whether the piece on there has moved before, mover, ori and desti are the label for the piece that moved, the origin square and the destination square, respectively, all left by HandleMove. You can then change functions B and b in the Pre-Game code to
def B cond #0 (cond flag var ori BBB cond == 1 var wstart BBB cond var wstart WWW BWBWBW) 0;
and a similar function for black, where BBB, WWW and BWBWBW are the index of the Bishop, Wazir and BW compound moves in legdefs. The expression between parentheses would return BBB when the flag on the square of origin was set (meaning this Bishop had moved before, so that it should keep moving as a Bishop), or when wstart=1 (meaning the other Bishop has already started as a Wazir). Otherwise, when wstart=0 it returns the index for the moves of the compound (as this then is the first Bishop we move in this game), and if none of that is the case, it moves like a Wazir (as the other Bishop must have started as a Bishop, wstart=2).
Zonal confinement and capture restrictions
The user can suppy a GAME-code function BadZone in the Pre-Game section, to veto moves that would otherwise have been pseudo-legal. The routines in the betza.txt include file will consult this function for every move they generate when the variable zonal is set to true, passing it the move description in terms of the square coordinates (origin, destination, locust square and drop square) and the dropped piece type (if any). Non-applicable items will be passed as 0. When the function returns true the move will not be considered pseudo-legal.
BadZone could then access the board (through space #sqr expressions) to get the involved pieces. (The function is called when the board position is that from before the move, so the moved piece will still be on the origin, and the captured piece on the destination.) It can then test if the piece is allowed to go to the intended location, or whether it would be allowed to capture that type of victim.
As an example, consider a piece type E that is not allowed to move into the opponent's board half of a 10-rank board. This could be enforced through:
set zonal 1; def BadZone xor > rank #dest 4 > rank #orig 4 and == E toupper space #orig =orig =dest =L =D =T;
The five parameters prefixed with '=' should always be present at the end of the definition, (although you can use other names for them, as long as you use the same names in evaluating the function result), even when the calculation does not use them, to indicate that the function will be called with 5 arguments. This example only uses the origin and destination square, which are the first two arguments. It first tests whether the origin contains a piece E or e. If so, it tests whether the origin and destination are in different board halfs.
This 'user submitted' page is a collaboration between the posting user and the Chess Variant Pages. Registered contributors to the Chess Variant Pages have the ability to post their own works, subject to review and editing by the Chess Variant Pages Editorial Staff.
By H. G. Muller.
Web page created: 2020-08-16. Web page last updated: 2020-08-16