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

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 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. If there are types that can only be obtained through promotion, place those on the board too. (This would of course spoil the FEN that will be produced.) 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.

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.

Invoke the preset, hit the 'Menu' button, and then 'Edit'. Tick the checkbox "Do Not Include Moves in Code" just above the code boxes. THIS IS ESSENTIAL! If you forget it, every move will be rejected as illegal, because the piece you are moving will already be moved away when the code generated by the Applet tries to test the move for legality. Copy the GAME code shown by the Applet into the corresponding GAME-code text boxes of the preset. The Pre-Move sections can remain empty. Then press the 'Save' button at the top of the preset's edit page. In most cases thatw ould complete what you have to do to automate your preset. Some exceptional cases where you have to make some changes or additions to the automatically generated GAME code are discussed below.

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 use 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

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.

Anti-trading rules

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.

Unusual promotions

The Applet is geared to generating wester chess variants, which tend to have Pawns as the only promoting piece, and a choice of what it can become on promotion. It is less suitable for generating Diagrams for Shogi variants, where nearly all piece types tend to promote, but to a predetermined type, so that the only choice is to promote or not. This because the Diagram then requires the promoted types to be defined in the same order in the table as the corresponding base pieces. And in the Applet the table is predefined. The association between piece types for Shogi promotions has thus to be made in a different way, and a not-too-inconvenient method is to supply it directly as GAME code.

For one, the array 'promotables' would have to be extended a lot, containing all piece types that can promote. Their association with promoted types will then be specified by mentioning the latter in the same order in an array 'promoted'. E.g.

set promotables (P p N n L l S s B b R r E e);
set promoted    (G g G g G g G g H h D d K k);

To indicate the ranks where such a promotion can take place, the promotab would have to contain the word as one of

The betza.txt include file includes the GAME code subroutines needed to automate Game Courier presets for CVs the rules of which are summarized in tables generated by the Play-Test Applet. For normal use you would not have to know how these routines work; the only thing that is important is to call HandleMove and GameEnd from the Post-Move and Post-Game sections, respectively. Then these will do everything that needs to be done. For those interested in how the code works, however, this file provides some explanation. The basic strategy is this: checking input moves for pseudo-legality is done before Game Courier makes them, but nevertheless in the Post-Move code. To make that possible the checkbox "Do not include moves in GAME code" will have to be ticked when submitting the preset. With this setting the game state at the start of the Post-Move code is still that before the move. HandleMove then can generate the pseudo-legal moves from this position, and compare these to the input move. For that purpose the latter will have to be interpreted, as it will only be available as a text string in thismove. If there is a match between the input move and a pseudo-legal move, HandleMove then feeds the move to Game Courier. As it generated the move, it is also aware of whether the move is a castling or an e.p. capture, and can then apply the side effects implied by those, which Game Courier would miss. After the move is performed, it stores some info about it on how it could affect future moves: the piece that last moved, and the squares it passed throug, for the purpose of an e.p. capture that might follow, keeping track of which squares no longer contain the piece that started on them for castling, and some more exotic things that might be important for enforcing anti-trading rules that some chess variants have. It also updates the ply counter and the table of visited positions, used for detecting repetition and 50-move draws. Things like testing the move for full legality (i.e. whether it exposed its King to capture), detecting mates or draw conditions, and calculating the list of legal moves for the purpose of highlighting have only to be done after the last move of a stored game has been executed. These tasks are therefore handled by GameEnd, the subroutine for the Post-Game actions. This also takes care of askingthe user for additional information that the initially entered move did not specify, such as what the user wants to promote to. And it can prompt the user for playing a second move in the same turn. The basic calling scheme for HandleMove is: HandleMove --> ParseMove | +--------> -> GenMoves ---> fn #mover | / | +-> GenAll - +--> NextLeg --> [ NextLeg --> ... ] --> GotMove (match input) | ^ | | |__________________________________| | imitators/borrowers | +-> GenAll ---> GenMoves ---> NextLeg --> GotMove (highlight leg2) The routine ParseMove takes the input move text apart, first splitting it into individual moves (that were separated by semicolons), splitting those into a piece ID and a board step, and finally splitting the latter into an origin and a destination square. The piece ID is optional, but if it was given it is verified that the origin contained such a piece. The meaning of the move is extracted as the variables ori, desti and mover. Additional moves, when present in the input string, can also have other formats. The pass command is recognized there (and ignored), as are the Game Courier notations for promotions (the word behind the hyphen is 'dest'), freedrops (a piece ID before the hyphen) and suicides (an @ before the hyphen). These will lead to setting of the variables promo, freedrop and dropped, or suicide. A second board step is also allowed, when its origin coincides with the desti obtained from the previous step. This is interpreted as a suicide at the old desti, and redefines desti as the destination of the new move. Apart from syntax, the only thing that is checked here is whether mover matches the piece ID that was given, and whether this is a piece of the player that is on move. Testing moves for pseudo-legality is not needed while setting up a stored game; this was already done before the game got stored. We can thus rely on the moves to be legal, except for the last one of the game, which might have been newly entered. So the basic idea is to just feed these 'early' moves to Game Courier. But this would fail in the case of moves with implied side effects, such as e.p. capture and castling. The Game Courier move system only does what the move explicitly specifies, and we don't want to require from the user that he specifies both King and Rook move to castle. So we do have to check all the stored moves for having side effects, and apply the implied board modifications. To make that efficient, the move generator has a mode where it would only generate moves with side effects. To recognize an e.p. capture we in general have to know if there are e.p. rights, so creation of e.p. rights is also considered a side effect. Also, the routine GenMoves generates the moves of one given piece, as moves by another piece than that specified by the input move will certainly not match. For orthodox Chess thus only the King and Pawns would have any moves: at most two castlings for the King, and the Pawn might have a double-push and one of two possible e.p. captures. If any of these moves matches the input move, the implied side effect (moving the Rook, creating e.p. rights, or removing the e.p. victim) will be applied to the board. With the caveat that placement of the Rook will be postponed to after Game Courier has performed the explicit part of the move, as it cannot be excluded that the Rook should end up on the square where the King still was before the move. When the input move doesn't match any of the generated moves, no special action is required. Only for the final move in a stored game we have to subject it to a more elaborate test. In that case we let GenMoves generate all pseudo-legal moves of the given piece. Except for the moves with implied side effects, the input and generated move only match if they also specify the same side effects. There is no reason to act on these side effects; as they were explicit in the move, Game Courier's normal move procedure will handle them adequately. Any promotion choice is still ignored at this point. If there is no match, the move apparently is not pseudo-legal, and we could reject it with an error message. But... Unfortunately move induction is treated as a move of the inductor, while it is the inducee that moves. So if we only generate the moves of the mover, we would overlook any induced moves. When a piece makes a move that is not pseudo-legal according to its own definition, it might still be legal if it was induced. Apparently-illegal moves thus still have to go through a second phase of pseudo-legality testing, where we compare the input move to the pseudo-legal moves of all pieces. For this we have a subroutine GenAll, which loops over the pieces of the desired player. We would really only have to consider moves of pieces that can induce moves, which in most CVs would be none at all, so that we could skip this entire test. But we do not keep track of that yet. This could be a future improvement. But from a practical point of view, users would almost never enter moves that are not pseudo-legal, in a game that highlights (pseudo-)legal moves. They would even have to ignore a popup that warns them off, in order to do that. And who cares whether a case that will virtually never happen is handled a bit inefficiently? There are several other cases that can lead to apparently illegal input moves, though, because entering moves in Game Courier with the mouse requires entering them leg by leg. So the entered move might not yet be complete, its side effects yet to be specified. HandleMove tries to be smart in this respect. Apart from exact matches, it also keeps track of partial matches, in particular through 'pseudohits' (the base move matches, but the generated move has side effects, and the input move not) and 'halfhit' (a simple input move goes to the square where a generated move of that piece would cause a suicide). The latter case could mean a partially entered move, where a second board step will follow to bring the piece to its final destination, even if there was an exact match as well. In this case we will always prompt the user for a second leg, through the continuemove command, after having Game Courier perform the move. If the move was already complete, he can then enter 'pass' as second leg. The (pseudo-)legal possibilities for such a second leg will then be highlighted. If there was a single pseudohit, HandleMove will assume that it is dealing with a partially entered move, the side effects of which have yet to come. Because there apparently is only one side effect that is pseudo-legal, it will then 'auto-complete' the input move by adding that side effect to the input move string, before having Game Courier perform that. This effectively make such moves behave as those with an implied side effect, except that the side effect will be shown in the game notation. If there is more than one pseudohit, HandleMove will prompt the user for entering the side effect. As this would be a suicide or a freedrop, or a combination of those, the user would probably have to type it. If there wasn't even a pseudohit the move cannot become pseudo-legal by adding extra moves for the side effects, and will be rejected. GenMoves will generate moves for a given piece on a given location. It takes the description of these moves from the legdefs array. To know where in that to find the moves, it uses a function with the name of the piece. This function returns the starting location of its moves. Depending on an extra argument, this can be a list of all its moves, or just of those with implied side effects. The description of a move consist of a sequence of descriptions of its legs, each consisting of four numbers. These will be preceded by a number indicating how many legs the move has. (So moves take up 5, 9, 14, ... elements of the legdefs array.) The last move in the list will be followed by a 0, to indicate we are done with this piece. A leg is by definition a leap or ride, and the numbers describing it are its range (maximum number of leaps), the leap size in the two dimensions x and y, and a 'mode', which is a collection of bit flags indicating what the leg can do (e.g. capture). GenMoves will call a subroutine NextLeg for each move of the piece. NextLeg will try to find a 'realization' of the next leg in line, according to the description. This can succeed or fail (e.g. because the mode specifies the leg must be a capture, and there is nothing to capture within the range in the given direction). It can even result in multiple realization, in the case of a ride that must end on an empty square, in which case NextLeg loops over them all. For each realization it finds, NextLeg either recursively calls itself to try realizing the next leg, or will call GotMove if there are no more legs left. Realizing a move is thus in principle a tree search with GotMove invocations at the leaves. Many of these trees (e.g. the moves of an elementary leaper) have only a single (leaf-)node in it. But for a Hook Mover the tree can be large. GotMove contains a switch statement to process the generated moves, which are passed to it as the squares they affect (orisqr, destsqr, locustsqr, dropsqr) and a flag to indicate whether the side effects at the locustsqr and dropsqr are implied or not. The locustsqr and dropsqr are optional, and a value 0 indicates they are not valid. If there is a dropsqr there can be an 'unload' to specify what should be dropped on it. Depending on the setting of the global variable 'task', GotMove performs one of the switch cases on the move. This can be the matching with the input move for pseudo-legality testing, as we have discussed for HandleMove. It can also load the move in the legalmoves array for defining highlighting, test whether the move captures a royal piece for the InCheck test, or determine whether the move was legal by doing such a royal-capture test after having performed the move on the board. (And then take it back afterwards!) A variable 'hit' can be set when the task has succeeded (e.g. we did find a move that captured the King), and it would be pointless to go on; this aborts the move generation. Matching the input move can unfortunately not use this, because finding an exact match does not prove there are no halfhits as well. For this task it thus has to generate all the moves of the given piece. (This could be improved by listing the pieces that are in principle capable of moves with side effects, and only refraining from abortion after a match for pieces on that list, on average saving half the work.) NextLeg consists roughly of three parts. It first determines the 'end-point' of the leg, based on the specified range, the first obstacle (piece or board edge) in the given direction, and a possible requirement (specified in the mode) that it should match in length with an earlier leg. The mode can also specify that the move is 'non-jumping', in which case it would have to redo the ride with a smaller step, to visit the squares that the leaps of the ride jumped over, to see if there is a blocker there. Because such lame leaps can create e.p. rights, we also update the global variable epsqrs when the mode specifies we should do so. Currently this is done only for the first step of the ride. The end-point square can be occupied by friend or foe, or be empty. There might be a number of squares along the ride leading up to it; these would be all empty. The second part is looping over the latter, if the leg is capable of non-capture, and its length is not forced. For each of these square we then call NextLeg or GotMove. If more legs follow, we pass the number of steps taken to NextLeg as the parameter 'iso'; this can then be used to fix the length of a later riding leg. Finally it takes the move to the end-point square. It is tested whether the occupant matches the mode. Empty squares need non-capture rights, foes need capture or royal-capture rights. Friends need friendly-capture rights, move induction, or move borrowing. In the latter case, we (recursively) call GenAll to generate the moves of the encountered piece from our own startsqr. For move induction we will recursively call NextLeg to continue the move, but staring from the location of the encountered piece. For the other cases the square occupant will be captured; if this happens in a non-final leg, this defines the locustsqr. All these possibilities can be combined with an e.p. capture, when the end-point is an e.p. square. (XBetza can create e.p. rights on occupied squares!) For an occupied end-point of a non-final leg we might also have the possibility to use the occupant as a 'screen', and hop over it. (Well, actoally onto it, but the next leg will then make us hop off again.) Before it does do anything, NextLeg will test whether the mode specifies the move as an initial one, and if it does, abort when the piece making it is non-virgin. This is kept track of by a set of flags named after the board squares, which will be set as soon as a piece moves to that square. The only way for a piece to be on a square without that flag set is then if it has been there from the very beginning. After that it will test for the special case that the move is an imitation (also indicated by mode), in which case it will recursively call GenMoves for the imitated piece starting from the square of the imitator. This recursive calling can be a concern, as it might lead to infinite recursion. For the imitator this is prevented by keeping track of which pieces can imitate, and never allowing imitators to imitate each other. The imitated piece thus always is a non-imitator. For move borrowing it is currently solved by suppressing borrowing from its own type. (But this would fail when there is more than one move-borrowing type!) The routine GameEnd contains the Post-Game code. It does the things that only have to be done after the last move of a stored game (which could be a freshly added, so far unexamined move). Such as checking whether the move is unambiguous, fully legal, causes game termination, or generate the moves for the following moves for the purpose of highlighting. Note that the test for pseudo-legality, which also only is needed on the last move, could not be done in the Post-Game code: the way this test is implemented, by comparing to generated moves, requires it to be done in the position before the move is made. Testing whether this moves was legal concerns the position after the move, though, testing whether the opponent can capture the royal piece there. But the first thing that has to be done is decide whether the last move is complete. The board step could still be entered only half way (a value of ok = 2 will warn us about that), or it could still require a promotion piece to be chosen. If the latter is the case (based on the type of the moved piece, and the rank it moved to, from the tables promotables and promotab), askpromotion is invoked with the choices available on the destination rank. A second time, even if the user chooses not to promote, this is suppressed by testing $answered. After we are sure the move entry is complete, we test whether the move put the moving player in check, by calling the InCheck routine. The general calling scheme for GameEnd is this: GameEnd --> InCheck --> GetRoyals | | | +-> GenAll --> NextLeg -> GotMove (king capture) | +-> GetRoyals | +-------------> GenAll --> NextLeg -> GotMove (legality test) --> GenAll --> NextLeg --> GotMove (king capture) | +-------------> GenAll --> NextLeg -> GotMove (highlight) The InCheck test calls GenAll, making GotMove perform the task of detecting capture of an enemy royal piece. If we are dealing with extinction royalty it first counts how many royals we have, though. If that is more than one, it assumes we are not in check, as in such a case it is not illegal to allow one of the royals to be captured. (This is not strictly correct in the presence of pieces capable of multi-capture, so there are, mostly hypothetical cases where we could do a move that loses all our royals.) The InCheck test is also used to enforce trading bans (specified through the variable 'protected'). The piece making the possibly illegal capture is then temporarily replaced by a royal piece, so that the possibility to recapture would flag the capture as illegal. The InCheck routine is called in GameEnd (the Post-Game code) to determine if the player that just moved is in check, in which case his move would be illegal. There is no separate routine for mate testing; for this GameEnd directly calls GenAll with the appropriate setting of 'task'. GotMove will then perform the moves generated by this on the board, moving the piece from orisqr to destsqr, clearing the locustsqr and putting 'unload' on dropsqr. It then recursively calls GenAll to generate moves for the opponent, asking for a capture-of-royal test. The counting of extinction royals done by InCheck has been moved here to GameEnd itself, under the assumption that all moves will preserve their own royals. If a player has more than one extinction royal it will be assumed his moves are legal, and the entire mate test will be skipped. If he has one royal, it is assumed that loss of a royal after a move will make that move illegal. This can be incorrect when promotion to a royal is possible. In general the promotion choice does not affect the legality of a move, so the moves to be tested for legality refrain from promotion, but promotion to an extinction royal can make a difference. set mask elem 0 var #mover; if #mask: set hit & #mask elem + 200 + - file #ori file #desti * 40 - rank #ori rank #desti; endif; Directions: 8 K 8 Q 4 N 3 D 2 A 1 C 1 Z 1 H 1 G 1 Griffon 1 Antigrif To get a cheap filter for ruling out that a piece can capture the King, so that you don't have to bother generating all the moves for that piece to see if they happen to hit a King, we could use a footprint check. This would convert the square coordinates for the piece and King to a number in such a way that the difference of the two numbers would uniquely determine the leap between the pieces. (For 8x8 boards this is known as the 0x88 system, basically it maps the square of a WxH board to file + 2*W*rank.) For each of the possible leaps we we would store a bit mask, where each bit corresponds to a set of leaps or rides that would have that leap in its range. With each piece type we would associate a similar mask, to indicate which of these sets belong to its move. E.g. an Archbishop would have four diagonal rides, each a set of its own, four Ferz steps (likewise), and eight Knight jumps, which could be grouped as four left-right-symmetric sets. So it would have 11 bits set in its mask, and the mask would completely describe what the piece can do. To answer the Question whether that Archbishop can capture the King, we would just have to take the bitwise AND of this mask with the mask tabulated for the coordinate difference. If that results in a zero the Archbishop cannot capture the King. If one of the bits representing a Ferz move or a group of Knight-moves remains set, we know for sure that the Archbishop delivers check. If only one of the diagonal rides remains, we would have to test whether the ride was blocked. But we know in which direction the move must go, so we can use checkaride #kingpos sign #dx sign #dy for that. So there would be a bitmask to identify which of the bits are leaps, to determine from a matching result whether it is a direct leap or whether we have to use checkaride. If we cleverly assign the mask bits to move sets it should be possible to handle virtually every slider-leaper compound one would encounter in practice. Even complex pieces like the Chu-Shogi Lion, which is a bent leaper, has a well-defined footprint (KNAD); A good assignment of the bits seems this: The eight K steps would each have their own bit, as woul the eight Q rides. Shogi pieces like generals often break symmetry, even left-right symmetry, so from the fact that one of them occurs you cannot conclude any other symmetry-equivalent one would occur. As already suggested, the Knight moves could be divided into four left-right-symmetric sets; this would be necessary for describing pieces like Narrow and Wide Knight, or the CwDA Nutters pieces that have only forward Knight moves. Left-right asymmetry is virtually non-existent outside Shogi, (and even there it occurs only in the large variants), and Knight moves are virtually non-existent in Shogi. The eight Camel and eight Zebra moves could each be a set; I have never seen these moves used in an asymmetric way. For Alfil and Dababba this is different; forward-backward symmetry for those often occurs in Pawns (albeit not in capture-capable moves, so for check detection this might not be relevant). To err on the safe side we can also group them in symmetric pairs (i.e. two pairs for Alfil, one pair and two singletons for the Dababba). Betza H and G moves can be fully symmetric sets. So far that used 29 bits; an integer variable usually can contain 32 bits. We can use one bit for indicating the piece might be able to capture through an unspecified complex path. E.g. a Sissa could be marked that way. In this case we can neither confirm nor exclude it checks in a cheap way, and we use the move generator to generate all moves of the piece to see what it hits. The two bits that are left could be used to represent the bent rides of the Griffon and the Antigrif (aka Aanca). If these participate in the variant. If we are short of bits, we could adopt a flexible assignment of the C, Z, H, G, ony assigning those to a bit if they actually occur in the variant. If the mask for the piece is all zeros it means the range of the piece was not in the leap-indexed table, and the only way to find out whether it checks would be to generate all its moves. The time spent on move generation could further be reduced by having a mode of the move generator where it would only generate the complex moves that could not be filtered out. E.g. a piece could move as a Rook, but also capture as a Checker. Diagonally adjacent squares could then be under attack, but only a full move generation would be able to determine if there actually is one, by testing whether the second leg of the move ends on an empty square. In that case we would not want to generate all the Rook moves; the cheap filter would have already caught it if any of these would have been checking. Perhaps we should reserve some bits for such 'conditional attacks'; elementary hoppers would also fall in that class. They would align on orthogonals or diagonals, and if they don't, it would be pointless to try their moves. sub SetRay b x y dx dy: set start + #offs + #x * #ww #y; set step + #dx * #ww #dy; set i 2; do while < #i #bh: setelem codes #start | #b elem #start codes; set start + #start #step; inc i; loop; endsub; sub SetSqr x y b: set sqr + #offs + #x * #ww #y; setelem codes #start << 1 #b; set leaps | #leaps << 1 #b; endsub; sub TableInit: set ww dec * 2 #bw; set offs + #bw * #ww dec #bh; set lim * 2 #offs; set i 0; set codes array; do while < #i #lim: setelem codes #i 0; inc i; loop; set leaps 0; gosub SetSqr 1 0 8; // W gosub SetSqr -1 0 9; gosub SetSqr 0 1 10; gosub SetSqr 0 -1 11; gosub SetSqr 1 1 12; // F gosub SetSqr 1 -1 13; gosub SetSqr -1 1 14; gosub SetSqr -1 -1 15; gosub SetSqr 1 2 16; // N gosub SetSqr 1 -2 17; gosub SetSqr 2 1 18; gosub SetSqr 2 -1 19; gosub SetSqr -1 2 16; gosub SetSqr -1 -2 17; gosub SetSqr -2 1 18; gosub SetSqr -2 -1 19; gosub SetSqr 2 0 20; // D gosub SetSqr -2 0 20; gosub SetSqr 0 2 20; gosub SetSqr 0 -2 20; gosub SetSqr 2 2 21; // A gosub SetSqr 2 -2 21; gosub SetSqr -2 2 21; gosub SetSqr -2 -2 21; gosub SetSqr -1 -3 22; gosub SetRay 1 0 2 0 1; // R gosub SetRay 2 0 -2 0 -1; gosub SetRay 4 2 0 1 0; gosub SetRay 8 -2 0 -1 0; gosub SetRay 16 2 2 1 1; // B gosub SetRay 32 2 -2 1 -1; gosub SetRay 64 -2 2 -1 1; gosub SetRay 128 -2 -2 -1 -1; endsub; sub Checks piece sqr: my i; set i + #offs - file #sqr + #kfile * #ww - rank #sqr #krank; set pmask elem 0 var #piece; if #pmask: set mask & #pmask elem #i #codes; if & #mask #leaps: set hit 1; elseif & 255 #mask: set hit checkaride #sqr #king sign - file #sqr #kfile sigh - rank #sqr #krank; endif; endif; endsub; Try all moves, assuming the legs will work. If no assumption on intermediate occupancy has to be made, the move is unconditional, and all capture targets get flag 1 set. With an assumption we set flag 2. This also holds for locust squares. If a second slider leg is non-iso we abort. 2-leg combos First step (0,1) v = 0x55 (W) 0x01=D 0x10=O 0x82=N 0x44=F 0x28=W v = 0xAA (F) 0x01=A 0x10=O 0x82=N 0x44=D 0x28=W v = 0xFF (K) Negeer wat we al hebben. Anders 'difficult'. Iets is activeerbaar als er een non-final leg is die bezetting eist (dus p, c, zonder m). Als activeerbare stukken een eenduidig pad hebben tot ze vastlopen (dus geen rides!), moeten we de velden op dat pad markeren. We kunnen hiervoor een vlag in de leg opnemen: markeer lege velden die je passeert, met vermelding van stuk en zet. Gebruik variabelen a1 ... h8, normaal lege arrays, en een veldenstack. Markeer het veld door startsqr en startleg te pushen, en push de veldnaam op de veldenstack. Als een zet op een leeg gemarkeerd veld eindigt, activeert dat het stuk, en genereren we die zet opnieuw. Doe dit niet voor lineaire hoppers; die testen we apart (?) Na afloop alle velden op de veldenstack terugzetten naar leeg array. Riding non-final legs die niet op het eind van hun bereik geblokkerd worden door een vijand (die ze mogelijk kunnen slaan). Ze moeten daarna dan nog wel iets kunnen slaan. Ook daarvoor kunnen we een mode vlag maken. Wegzetten van een stuk op een dusdanig gemerkt veld moeten we de zet opnieuw bekijken. for sqr spaces: set sqr array; next; Voor elke non-final leg die kan slaan: behandel elk leeg veld alsof er een vijand staat. Bij succes verhogen we de attack count van de locustsqr. Dit betekent dat we ook bij c-mode een free-ride moeten maken. Als er geen m-rechten zijn moeten we bovendien ze zet op de stack van het veld zetten. I made a bit of progress towards speeding up the legality testing. I am going for the idea of an 'augmented check test' which will generate all moves for the opponent, not just to check whether these capture a king, but also to build an 'attack map': a board-size array, that for each square holds an array of the moves that touch that square. To that end I had to insert some code into the move generator that is conditionally executed when the task to perform is this augmented check test. (All other tasks are only done on the moves the generator delivers, but for this task things must be done all along the path of the move.) The result is an array for each board square, where the first element is an integer that currently only takes on the values 0 or 1, the latter meaning there would be a (normal) pseudo-legal capture to the square if it were occupied by an opponent. (Even when it is actually empty or occupied by a friend). For multi-leg moves normal captures only result from the final leg (capture in earlier legs are locust captures, as the piece will move on and leave the square empty), and if this leg is a slide, all empty squares that slide passes over will then be marked as attacked. The remaining elements of the arrays are pairs, indicating the starting square and the table address of the move that touched the square. For simple (= single-leg) moves, each move of a piece corresponds to a direction. For multi-leg moves an entry in the move table would correspond to a set of directions, one for each leg. So we know reasonably precise what hit the square. Not every move is recorded in the attack table this way. Conditions are that the move must be able to go beyond the square, so that a change of occupant on the square could affect moves that are downstream. For occupied squares this means that the current leg should have a range longer than was needed to reach the square, or should be able to do more legs from that squareif it were to be evacuated. And such continuations should be able to capture. This means the occupant is potentially pinned, because the (at this point unknown) continuation the move would get on evacuating the square might be an attack on the king. For empty squares, we only tabulate moves that could continue beyond them when they get occupied. Which is the case when the leg has the capability to hop or capture, and other legs follow it. Occupying the square would then continue from that square with the next leg, activating a move that before was not possible or went elsewhere. This now all seems to work. This augmented check test is run for the player that just moved in the Post-Game code, after temporarily moving the opponent's King. After that it can be easily tested whether the opponent is in check, by looking whether the square the king should be on is marked as attacked. But the most important thing is that the attack map can be used for the legality checking of the opponent's moves (which we want to put in the $legalmoves array). When the opponent was not in check, any of his moves of non-royal pieces that does not start from or go to a square that was 'touched' by one of of our moves, will not give us any new moves other than to these squares. So these are automatically legal. Only moving away potentially-pinned pieces, or landing on an empty square that activates a hopper or locust-capturer could make the opponent put himself in check. Only a minority of the moves would do such a thing, and for those only the moves that touched the squares they moved between would have to be retried in the reply to test if they actually do check.

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.

Invoke the preset, hit the 'Menu' button, and then 'Edit'. Tick the checkbox "Do Not Include Moves in Code" just above the code boxes. THIS IS ESSENTIAL! If you forget it, every move will be rejected as illegal, because the piece you are moving will already be moved away when the code generated by the Applet tries to test the move for legality. Copy the GAME code shown by the Applet into the corresponding GAME-code text boxes of the preset. The Pre-Move sections can remain empty. Optionally you can also copy the link to an alternative JavaScript file for handling mouse moves, to the "Rules, written in HTML" box of the preset, while in Source-code mode. (See the following section.) Finally press the 'Save' button at the top of the preset's edit page. In most cases that would complete what you have to do to automate your preset. Some exceptional cases where you have to make some changes or additions to the automatically generated GAME code are discussed below.

Alternative code for handling the mouse

Moves can be entered in Game-Courier pages not only by typing, but also through mouse clicks on the board. This is controlled by a JavaScript program that comes together with the web page, and is running in your browser. There is a dedicated version of this JavaScript program that is specifically tailored to work well with presets automated through the GAME code generated by the Applet. To get the optimal experience, you would have to switch the preset to using the "HTML Table" mode for displaying. In this mode all pieces in the board diagram are individual images, so that it is possible for the JavaScript to move them around. And it uses this opportunity for instance to already move the piece along the first leg of a multi-leg move to its intermediate square, while highlighting the final destinations in order for you to select one of those. It will also show a button bar that allows you to review the current game locally, i.e. without having to contact the Game-Courier server for every move.

To use this alternative mouse driver, you have to embed it in the HTML of the 'Rules' section. This can only be done when the text editor for that section is switched to 'Source code' mode by the button in the menu bar above the text window. Otherwise the link you paged there would just be shown as text on the web page, instead of being used to load and run the JavaScript program. Normally it is more convenient to edit text in WYSIWYG mode, but in this mode everything that is not simple text will be erased from the HTML. So the best way is to make sure firt that the rule-description text is exactly as you want it, then switch to Source code mode and copy-paste the link is <script> tags given by the Appet under the HTML for your text.

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.

Anti-trading rules

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.

Custom pieces

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
0
your '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


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.

Last revised by H. G. Muller.


Web page created: 2020-08-16. Web page last updated: 2020-08-16

Revisions of MSgame-code-generation