# Parlett's movement notation

Some chess variants use interesting non-standard pieces. In order to concisely describe new pieces or to describe pieces without requiring everyone to memorize a bunch of non-standard names some new notation can be invented.

In this challenge we are going to look at Parlett's movement notation and convert it to nice at a glance diagrams.

A move description consists of two parts. First is the distance. Some pieces can move 1 unit, some pieces can move two, and some pieces can move however as far as they wish. The second part is the pattern, that expresses what basic units a move can use. When moving a piece selects a unit from it's pattern and then repeat that unit based on it's distance.

For example a bishop has any non-negative distance, and it's pattern is all the diagonal movements 1 square. So the bishop can move in any diagonal direction any amount (other than 0).

## Distances

• Any postive integer means exactly that distance. e.g. 1 means 1.
• n means that the piece can move any non-negative distance

For this challenge you can assume that the integers will not exceed 9.

## Move patterns

There are 5 basic patterns that all other patterns can be composed of. They are the 8 squares surrounding the piece except since the system doesn't distinguish between left and right moves, these moves are grouped into pairs.

Number Description
1 Diagonal forward
2 Directly forward
3 Left or right
4 Diagonal backward
5 Directly backward

Here's these described with a diagram:

1 2 1
3   3
4 5 4


We can then describe the symbols for patterns. in terms of which of these they allow:

Symbol Allowed patterns
x 1 or 4
+ 2, 3 or 5
* 1, 2, 3, 4 or 5
> 2
< 5
<> 2 or 5
= 3
>= 2 or 3
<= 5 or 3
x> 1
x< 4

These two parts are then concatenated to make a symbol. So for our bishop earlier its distance is n and its pattern is x so it's symbol is nx.

## Grouping

You can group multiple moves together into larger moves using two extra symbols.

• . represents and then. For the symbol a.b the piece must perform a valid a move and then a valid b move.
• , represents or. For the symbol a,b the piece can do a valid a move or a valid b move.

. has higher precedence and there is no option for parentheses. So a.b,c means "(a then b) or c", not "a then (b or c)".

As an example the knight from chess can be expressed with the rather complex compound symbol 2=.1<>,2<>.1=, in plain(er) English "(2 moves to the side and 1 forward or back) or (2 moves forward or back and one to the side)".

In this challenge your program or function is going to take in a symbol as a string and draw an diagram of where it can move.

The diagram will show a 9x9 board with the piece placed in the center, (5,5), and will show all the places the piece can move in 1 turn with X and all the places it cannot with .. We will ignore the case where the piece does not move (whether it is described by the rules or not) and place a O at the original coordinates to indicate its place.

A piece may not move out of the board during the course of the move. For example 1>.1< (one forward, then one backwards) requires the piece to move one forward while staying in the board.

Your output should be arranged into 9 space separated columns with 9 newline separated rows. You may have trailing whitespace on any lines or after the end of the output.

This is so the goal is to minimize the size of your source code as measured in bytes.

## Test cases

1+
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . X . . . .
. . . X O X . . .
. . . . X . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .

2x
. . . . . . . . .
. . . . . . . . .
. . X . . . X . .
. . . . . . . . .
. . . . O . . . .
. . . . . . . . .
. . X . . . X . .
. . . . . . . . .
. . . . . . . . .

2*
. . . . . . . . .
. . . . . . . . .
. . X . X . X . .
. . . . . . . . .
. . X . O . X . .
. . . . . . . . .
. . X . X . X . .
. . . . . . . . .
. . . . . . . . .

n+,1x
. . . . X . . . .
. . . . X . . . .
. . . . X . . . .
. . . X X X . . .
X X X X O X X X X
. . . X X X . . .
. . . . X . . . .
. . . . X . . . .
. . . . X . . . .

2=.1<>,2<>.1=
. . . . . . . . .
. . . . . . . . .
. . . X . X . . .
. . X . . . X . .
. . . . O . . . .
. . X . . . X . .
. . . X . X . . .
. . . . . . . . .
. . . . . . . . .

n=.1<>
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
X X X X X X X X X
. . . . O . . . .
X X X X X X X X X
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .

1x.1+
. . . . . . . . .
. . . . . . . . .
. . . X . X . . .
. . X . X . X . .
. . . X O X . . .
. . X . X . X . .
. . . X . X . . .
. . . . . . . . .
. . . . . . . . .

1+.1+
. . . . . . . . .
. . . . . . . . .
. . . . X . . . .
. . . X . X . . .
. . X . O . X . .
. . . X . X . . .
. . . . X . . . .
. . . . . . . . .
. . . . . . . . .

1+.3>=
. . . . X . . . .
. . . X . X . . .
. . . . X . . . .
. X . . . . . X .
X . X . O . X . X
. X . . . . . X .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .

3=.4=.5=
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
X . . . O . . . X
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .

n<>.nx<
. . . . X . . . .
. . . X X X . . .
. . X X X X X . .
. X X X X X X X .
X X X X O X X X X
X X X X X X X X X
X X X X X X X X X
X X X X X X X X X
X X X X X X X X X

4*.1*
. X . X . X . X .
X X . X X X . X X
. . . . . . . . .
X X . . . . . X X
. X . . O . . X .
X X . . . . . X X
. . . . . . . . .
X X . X X X . X X
. X . X . X . X .


As a final note, you may notice that this notation fails to capture a lot of information about pieces. Like how blocking works, castling or anything about how pawns move. The thing is that these symbols are not really all that useful in expressing most interesting stuff. But they do make for a bit of an interesting code-golf challenge.

• Is the [ascii-art] output mandatory, or could we output an integer-matrix with .=0; O=1; X=2 for example (or any other three distinct values representing .OX)? Oct 21, 2022 at 16:00
• In case of a.b, can the piece be outside the board after a, if it's inside it after b? For example, what would be the output for n<>.nx<? Oct 22, 2022 at 1:54
• @CommandMaster This is a good question, sorry it took me so long, but I've clarified this. The piece must stay in the board at every . Oct 25, 2022 at 21:07

# JavaScript (V8), 305 bytes

Prints the ASCII art.

s=>s.split,.map(s=>(g=(x,y,[s,...a])=>(r=m[y])&&r[x]&&b.map((v,d)=>s?',x<,>,,=,,>=,,<,,<>,,<=,,+,,x>,x'.split,.indexOf(s.slice(1))>>v&d!=4&&b.map((_,n)=>n-s[0]||g(x+d%3*n-n,y+n*~-(d/3),a)):r[x]='X'))(4,4,s.split.),b=[...'4142.2030'],m=b.map(_=>[...b].fill.))|m.map(r=>print(r.join ),m[4][4]='O')


Try it online!

### Encoding

Each symbol is encoded by its position into a lookup array, packed as the following comma-separated string:

',x<,>,,=,,>=,,<,,<>,,<=,,+,,x>,x'


The position is then interpreted as a 5-bit mask. Note that the symbol * is not stored at all, so its position is -1 and all bits are set in that case.

 symbol | position | bitmask      bits:
--------+----------+---------
x<   |     1    |  00001       4 3 2 1 0
>    |     2    |  00010       | | | | |
=    |     4    |  00100       | | | | +--> Diagonal backward
>=   |     6    |  00110       | | | +----> Directly forward
<    |     8    |  01000       | | +------> Left or right
<>   |    10    |  01010       | +--------> Directly backward
<=   |    12    |  01100       +----------> Diagonal forward
+    |    14    |  01110
x>   |    16    |  10000
x    |    17    |  10001
*    |    -1    |  11111
-----
43210


Given a symbol and a direction $$\d\in[0..3,5..8]\$$, we retrieve one of the bitmasks described above and get the character at position $$\d\$$ in the lookup string "4142.2030" to figure out which bit should be tested. If the bit is set, we are allowed to move in this direction.

The direction vector is $$\\big((d\bmod 3)-1,\lfloor d/3 \rfloor-1\big)\$$, leading to the following compass:

$$\begin{matrix}0&1&2\\3&&5\\6&7&8\end{matrix}$$

# Charcoal, 136 bytes

ＵＯ⁹.Ｆ⪪Ｓ,⊞υ⁺⮌⪪ι.⟦⁴¦⁴⟧Ｆυ«≔⊟ιθ≔⊟ιη≔⊟ιζＦ⌕Ａ⮌⍘§⦃x¹⁷⁰+⁸⁵*²⁵⁵>⁴<⁶⁴<>⁶⁸=¹⁷>=²¹<=⁶⁹x>χx<¹⁶⁰⦄Φζλ²1Ｆ…·Σζ∨Σζ⁹«ＪθηＭλ✳κ¿⬤⟦ⅉⅈ⟧⁼μ﹪μ⁹¿ι⊞υ⁺ι⟦ⅉⅈ⟧X»»Ｊ⁴¦⁴OＵＥ¹


Attempt This Online! Link is to verbose version of code. Explanation:

ＵＯ⁹.


Draw a 9×9 board.

Ｆ⪪Ｓ,⊞υ⁺⮌⪪ι.⟦⁴¦⁴⟧


Get the input string, split it on ,s, split each piece on .s, and append two 4s to each reversed list of instructions as being the starting coordinates.

Ｆυ«


Loop over the list of instruction and coordinate entries.

≔⊟ιθ≔⊟ιη≔⊟ιζ


Remove the coordinates and one instruction and assign them to their own variables.

Ｆ⌕Ａ⮌⍘§⦃x¹⁷⁰+⁸⁵*²⁵⁵>⁴<⁶⁴<>⁶⁸=¹⁷>=²¹<=⁶⁹x>χx<¹⁶⁰⦄Φζλ²1


Map the instruction into a binary number and loop over the positions of the 1 bits (counting back from the end) which represent the allowed directions.

Ｆ…·Σζ∨Σζ⁹«


Loop over the allowed distances. (This assumes that zero distance means any amount up to and including 9. Larger explicit distances are supported, and the range for arbitrary distances could be increased to 1000 without changing the byte count.)

ＪθηＭλ✳κ


Move to the resulting position for the given direction and distance.

¿⬤⟦ⅉⅈ⟧⁼μ﹪μ⁹


If the current position is on the board, then...

¿ι⊞υ⁺ι⟦ⅉⅈ⟧X


... if there are more instructions to process then push them with the current position to the list of instructions, otherwise mark the current position with an X.

»»Ｊ⁴¦⁴O


Mark the initial position with an O.

ＵＥ¹


Space the board out horizontally.

# Java 10, 631608606597578504489475 470 bytes

s->{int i=0,k,d,n,x,y,l,e[],f[];var m=new boolean[81];for(var p:s.split(",")){e=f=new int[81];e[40]=k=1;for(var P:p.split("\\.")){f=e.clone();k++;for(i=81;i-->0;)if(e[i]==k-1)for(n=P.charAt(0),d=9;d-->0;)for(l=n>99?0:n-49;d*n==440|("ǰ« )ņ©+  Ł ".charAt((P.chars().sum()-n)%42%15)-1>>d&1)>0&&l++<n%48;)if((y=i/9-d/3*l+l)>=0&y<9&(x=i%9-d%3*l+l)>=0&x<9)f[y*9+x]=k;e=f;}for(;++i<81;)m[i]|=e[i]==k;}for(;i-->0;)System.out.print((i==40?"O":m[i]?"X":".")+(i%9<1?"\n":""));}


-74 bytes, opening up another -22 bytes, thanks to @Arnauld
-5 bytes thanks to @ceilingcat

Try it online.

Explanation:

s->{                            // Method with String parameter and no return-type
int i=0,                      //  Index-integer, starting at 0
k,                        //  'and then'-iteration integer
d,                        //  Direction-integer
n,                        //  Step-size integer
o,                        //  Operation-integer
x,y,                      //  x,y-coordinates
l,                        //  Inner loop integer (when the step-size is 'n')
e[],f[];                  //  Temp integer-arrays
var m=new boolean[81];        //  Result boolean-array, starting at 81x false
for(var p:s.split(",")){      //  Split the input by "," and loop over the parts:
e=f=new int[81];            //   Set e to an empty list
//   (and f as well to prevent uninitialized errors)
e[40]=k=1;                  //   (Re)set k to 1
//   And also fill the center cell with 1
for(var P:p.split("\\.")){  //   Inner loop over the parts split by ".":
f=e.clone();              //     Set f to a copy of e
k++;                      //     Increase k by 1
for(i=81;i-->0;)          //     Loop over all the cells:
if(e[i]==k-1)           //      If the cell contains the previous k:
for(n=P.charAt(0),    //       Set n to the first character of P
d=9;d-->0;)       //       Loop over the directions d in the range (9,0]:
for(l=n>99?         //        If the step-size is 'n':
0            //         Set l to 0
:             //        Else:
n-49;        //         Set l to digit 'n' minus 1
d*n==440       //         If n = 'n' and the direction is 4,
//         or bitmask magic† to check whether the direction applies to operation:
&&l++<n%48;)    //          If n != 'n': simply use l once
//          Else (n = 'n'): loop l in the range [0,14):
//          (any integer >= 8 would be fine here)
if((y=i/9-d/3*l+l)//          Set y to l steps into direction d
>=0&y<9        //          And check if it's still in bounds
&(x=i%9-d%3*l+l)>=0&x<9)
//          Change and check bounds for x as well:
f[y*9+x]=k;     //           Fill the x,y'th cell with value k
e=f;}                     //     Set f as new e for the next iteration
for(;++i<81;)               //   Loop over all the cells again:
m[i]|=                    //    OR the boolean at cell i with:
e[i]==k;            //     Check if the i'th cell of e contains value k
}                             //  After we've looped over the entire input-String:
for(i=81;i-->0;)              //   Loop one last time over all the cells:
System.out.print(           //    Print:
(i==40?                   //     If it's the center cell:
"O"                     //      Print "O"
:m[i]?                   //     Else-if the i'th cell of the boolean-matrix is true:
"X"                     //      Print "X"
:                        //     Else (it's false instead):
".")                    //      Print "."
+(i%9<1?"\n":""));}       //     And print a newline every 9 cells


Operation P.chars().sum()
-n
%42 %15 "ǰ« )ņ©+ Ł "
.charAt(...)-1
>>d for direction d
in range $$\[0,8]\$$
&1
x 120 36 6 325 325,162,81,40,20,10,5,2,1 101
000
101
+ 43 1 1 170 170,85,42,21,10,5,2,1,0 010
101
010
* 42 0 0 495 495,247,61,30,15,7,3,1 111
101
111
> 62 20 5 2 2,1,0,0,0,0,0,0 010
000
000
< 60 18 3 128 128,64,32,16,8,4,2,1,0 000
000
010
<> 122 38 8 130 130,65,32,16,8,4,2,1,0 010
000
010
= 61 19 4 40 40,20,10,5,2,1,0,0,0 000
101
000
>= 123 39 9 42 42,21,10,5,2,1,0,0,0 010
101
000
<= 121 37 7 168 168,84,42,21,10,5,2,1,0 000
101
010
x> 180 14 14 5 5,2,1,0,0,0,0,0 101
000
000
x< 182 12 12 320 320,160,80,40,20,10,5,2,1 000
000
101