Parsing the move description
Now let's turn attention to the parser. First we have to know what format the user has to specify the moves in, to know what we have to parse. The basis for this format is Betza notation. This specifies the type of move (in term of how it displaces the piece) as a capital letter, e.g. N for the groups of 8 Knight jumps. This can be tweeked by lower-case prefixes, to specify a subset of directions the piece can make this leap in, or what the move can do (capture and or move to empty squares). There can be a digit suffix indicating how often the basic leap can be repeated (in the same direction) if the move is a ride, while an unlimited ride is indicated by doubling the capital.
These basic notations can then be chained to indicate more complex paths, consisting of more than a single 'leg'. This is done by separating notations for the legs by hyphens, and enclosing this in square brackets. E.g. [R-sR] would indicate a Rook that can turn a corner, as the prefix 's' means sideways (compared to motion in the previous leg). When a question mark is used instead of a hyphen it means the following legs are optional; otherwise the move must be followed to the end. A leg with a prefix p indicates that the square it ends on must be occupied; [pR-R] would indicate a Rook that must jump over exactly one occupied square to move: first a rook move to that square, and then another Rook move continuing in the same direction. (The default for continuation legs; as an independent move the default would be all (4 or 8) directions.) So like a Janggi Cannon.
A p before an entire move (be it a simple one or a path specification within brackets) would mean the move is only valid if there is a single occupied square in the path. An n or j prefix means the unvisited squares on the ray the move or leg follows must all be empty or occupied.
A (simplified) formal syntax description is
<move description> ::= <homogeneous move> <move description>
<homogeneous move> ::= <leg> | <modifiers> '[' <path> ']'
<path> ::= <path> '-' <leg> | <path> '?' <leg> | <leg>
< leg> ::= <modifiers> <atom> <range>
<modifiers> ::= <isoini> <directional modifiers> <modes> <sweep>
<isoini> = 'i' | 0
<directional modifiers> ::= <direction> <directional modifiers> | 0
<direction> ::= 'f' | 'b' | 'r' | 'l' | 'v' | 's' | 'h'
<modes> ::= <mode> <modes> | 0
<mode> ::= 'm' | 'c' | 'd' | 'p'
<sweep> ::= 'n' | 'j' | 0
<range> ::= <number> | 0
<atom> ::= 'W' | 'F'' | 'D' | 'A' | 'N' | 'G' | 'H' | 'C' | 'Z' | 'U' | 'K' | 'Q' | 'R' | 'B' | 'DD' | 'AA' | 'NN' | 'CC' | 'ZZ' | 'GG' | 'HH'
The simplification is that this syntax allows many non-sensical move descriptions, because they contain redundant, meaningless or contradictory modifiers. This could have been improved in many cases by using a much more complex syntax. But we will let the parser solve it semantically, by ignoring what is meaningless and obeing only the last of a conflicting set of specifications.
The purpose of the parser is to convert the leg descriptors to a structure containing a direction set (that was specified by the directional modifiers interpreted in the context of the atom), a leap encoded as x and y displacement, a multiplier for those displacements, a number indicating the range (= maximum number of repetitions of the indicated leap), and a word that packs flags for indicating which isoini, modes and sweep modifiers were present.
A homogeneous move will be a sequence of such structures, (as many as the move has legs), and all these, for all homogeneous moves in the descriptor, will be held in an array. This data serves as input for the move-path generator that was described earlier.
A lookup table will translate the atom into a leap in the standard direction (strictly forward for orthogonal and diagonal moves) slightly to the right of forward for oblique moves), a multiplier, a default direction set and a default range, by a table lookup. If a range was specified, it will overrule the default. Likewise, directional modifiers would overrule the default. What the directional modifiers mean can depend on the atom, e.g. 'f' (forward) is a single direction for a Rook, but a pair for a Bishop.
The absence of a sweep modifier n or j will cause multiplication of the leap coordinates by the tabulated multiplier, which is then set to 1 in the structure. In presence of such a modifier the leap and multiplier will simply be copied to the structure, and a flag will record whether there was a j. (There is no need to record whether there was an n; this is assumed if there was no j. The n and j only are applied on steps of a ride that are not a multiple of the multiplier, and the latter would be 1 if there was neither an n nor a j, so that assuming an n has no effect if there was none.)
Custom fairy pieces in Fairy Stockfish
Moderator: Ras
-
hgm
- Posts: 28536
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller