# Rule file syntax and semantics

Updated for ver. 4.002. 2021-11-17

This document describes the syntax of rule files used by the Game Engine (the main component of the Rule Game Server), and the way these rules are interepreted, i.e. their semantics.

Note: this document describes the basic syntax as available in GS 4.*. It has been extended in GS 5.*, although it appears that most experiment designers continue to limit themselves to the GS 4.* syntax.

## General concepts

The game is played on an NxN board (currently, N=6). Each board position can be described by its row and column number (both of which range from 1 thru N=6) or by a single sequential number. The sequential numbers, 1 thru N2=36, are used to number cells by rows, from left to right within row, the rows ordered from the bottom to the top:

| Columns |
(Bucket 0) | 1 | 2 | 3 | 4 | 5 | 6 | (Bucket 1)
Row 6 | 31 | 32 | 33 | 34 | 35 | 36
Row 5 | 25 | 26 | 27 | 28 | 29 | 30
Row 4 | 19 | 20 | 21 | 22 | 23 | 24
Row 3 | 13 | 14 | 15 | 16 | 17 | 18
Row 2 | 7 | 8 | 9 | 10 | 11 | 12
Row 1 | 1 | 2 | 3 | 4 | 5 | 6
(Bucket 3) |  | (Bucket 2)

The four buckets are located outside the board, next to its corners. The buckets are numbered clockwise, starting from the one in the top left corner. The bucket's notional (row,column) coordinates are as follows:

Bucket No. 0: (N+1, 0)
Bucket No. 1: (N+1, N+1)
Bucket No. 2: (0, N+1)
Bucket No. 3: (0,0)

At the begining of an episode, objects (also known as game pieces) are located in some of the cells of the board, with no more than one piece per cell. Each piece is described by its immutable properties ( shape and color), and by its position on the border (either in terms of the row and column, or in terms of the sequential number of the cell).

Each move of the game consists of the player attempting to pick one of the pieces and to drop it into one the buckets. The rules of the game, described by a rule file, determine which pieces can be dropped into which buckets; this may depend on the location and properties of the piece itself, the other pieces on the board, and on the previous successful moves in this episode.

### The nature of game pieces

What set of objects a game can support has evolved greatly during our work on Rule Game.

In the original Rule Game Server 1.*, each object had exactly 2 properties: color and shape. Colors could be picked from the set of 4 colors (now known as "legacy colors": black, yellow, red, and blue), and shapes, from the set of 4 legacy shapes (circle, star, square, triangle). In other word, an object could be described as an object from a 4x4=16-element set of all possible (color,shape) pairs.

Since Rule Game Server 2.*, the game designer can specify an arbitrary set of colors ("custom colors") and/or an arbitrary set of shapes ("custom shapes"). This is done in the trial list file for your experiment. So if in your rule set file you use any colors and/or shapes outside of the "legacy" ones, you probably want for your colors and/or shapes to be listed in the appropriate columns in the trial list file. Otherwise, some parts of your rules will never get a chance to be activated. (See: Using custom shapes and colors in Rule Game Server 2.*

Since Rule Game Server 3.*, it is possible to create objects which are described not by two "legacy" properties (color and shapes) but by any number of properties you care to define. See: image-and-property-based description of objects.

## The structure of a rule file

The rules used in a particular episode are defined by a rule file. The file consists of two sections:

- the optional Order definition section, followed by
- the Rule section.

The rule file may also contain comment lines and blank lines.

### Comment lines

The rule file may contain comment lines beginning with a `#` character. These lines can be found anywhere in the file. They are only intended for a human reader, and are ignored by the game engine.

Blank lines are also permitted anywhere in the file, and are ignored by the parser. They server purely for readability.

As of ver 2.005 it is also possible to put a comment at the end of any "meaningful" line of the rule set file. The comment should start with a a `#` character.

## Notation

In this document, we use italics for meta-text, with square brackets to indicate optional parts, e.g. `[integer] string [string ...]` means "an integer, followed by one or more strings". The straight roman text is for literals (including square brackets), e.g. `[integer [, ...]]` means, "one or several integers, separated by commas, and surrounded by square brackets".

White space within lines is allowed in the rule file in roughly the same way as it is in a file in a typical shell scripting language or a programming language. That is, any amount of white space can be put between tokens (number, identifiers, punctuation); it will not affect the meaning. Line breaks, however, matter: each order definition, or each "rule line" must occupy one line of the file, with no line breaks within it allowed.

## The order definition section

The rules may be preceded with the order definition section. This section defines one or several custom orders. Each order defines a way to order the numbers 1 thru N2=36 (representing the cells of the board), possibly with ties (i.e. several elements having the same rank). (In mathematics, such an "order with ties" is known as a total preorder or a weak order). An order without ties is simply a permutation, written as a list of numbers surrounded by square brackets. For example, the line

    Order Manchu=[31, 25, 19, 13, 7, 1, 32, 26, 20, 14, 8, 2, ....]

defines an order in which the N2 cells are ordered like characters in a Manchu text, i.e. by columns, from top to bottom in each column, the columns being ordered from left to right.

An order may have ties. E.g.

    Order FromBottomLeftCorner=[ 1, [2,7], [3,8,13], ....]

orders the N2 cells by the L1 norm (the Manhattan norm) distance to the bottom left corner. Thus the cell no. 1 (the one at (1,1)) is the first one, followed by cells 2 and 7 which are equally ranked, and so on.

If an order definition does not include all N2 cells, it is understood as if it is ended with a tied group that includes all cells not otherwise listed.

Orders defined in the order section in order to be used in some of the rules in the rules section. There are also severl built-in orders, which you can use in rules without having to explicitly define them in the order definition section. The built-in rules include the following:

- `L1=[31, 32, ..., 36, 25, 26, ..., 30, ..., 1, 2, ..., 6]`, "English reading order" (left-to-right by row, rows ordered from top to bottom).
- `L2=[36, 35, ..., 31, 30, 29, ..., 6, 5, ..., 1]`, "Hebrew reading order" (right-to-left by row, rows ordered from top to bottom).
- `L3=[36, 30, 24, 18, 12, 6, 35, 29, ...,7, 1]`, "Chinese reading order" (top-to-bottom by column, columns ordered from right to left).
- `L4=[36, 30, 24, 18, 12, 6, ..., 31, 25, 19, 13, 7, 1]`, "Manchu reading order" (top-to-bottom by column, columns ordered from left to right).
- `T=[[31, 32, ..., 36], [25, 26, ..., 30], ..., [1, 2, ..., 6]]`, "from the top", by row from top to bottom, with a tie within each row. (This is similar to `L1`, but with a tie within each row)
- `B=[[1, 2, ..., 6] ..., [25, 26, ..., 30], [31, 32, ..., 36]]`, "from the bottom", by row from bottom to top, with a tie within each row. (This is the reverse of `T`.)
- `R=[[36, 30, 24, 18, 12, 6], [35, 29, ..., 5], ..., [31, ..., 7, 1]]`, "from the right", by column from right to left, with a tie within each column.
- `L=[[31, ..., 7, 1], ..., [35, 29, ..., 5], [36, 30, 24, 18, 12, 6]]`, "from the left", by column from left to right, with a tie within each column. (This is the reverse of `R`.)
- `NearestObject=[[1,6,31,36],...,[15,16,21,22]]`. The cells are ranked by the Euclidean distance to the nearest bucket. (The coordinates of the cells and buckets are defined in the General concepts section).
- `Farthest=[[15,16,21,22],...,[1,6,31,36]]`. The top-ranked are the cells that are maximally distant from their nearest buckets. (This is the reverse of `NearestObject`).

You can find the list of correctly spelled names of all build-in orders in the API page for the class Order.PositionSelector.

## The rule section

### Syntax

The rule file must contain at least one rule line. All rule lines together form the rule section, which is located after the order section, if there is one.

Each rule line is one line of text. It consists of an optional global counter, followed by one or several atoms.

        [global_count]  atom [atom ...]

global_count is either a star (`*`) or a positive integer.

If the rule line has no global_count, or global_count is `*`, we call the rule line "unmetered"; this means that no additional restriction is imposed on the number of times it can be used, beyond what's imposed by the counters in individual atoms (see below). If a global_count is given, the rule line is "metered", and the value of the global_count given in this line of the rule file is used to initialize the rule's global counter every time this rule line is reset. (For more details on counters, see Transfer of control between rule lines, under Semantics).

Atoms can be written in one of the two formats:

- The original (from GS 1.*) position-based format
- The property-based format (introduced in GS 3.*)

If the only properties your objects have are color and shape, then you can use either format for your atoms. You can write some atoms in one format and other atoms in the other format, and freely combine atoms in both format in your rule lines.

If you use property-based objects with properties other than color and shape, you must use property-based atoms.

#### Atoms in position-based format

Each atom wtitten in the original position-based format consists of 5 components, arranged as follows:

          (count, shapes, colors, positions, buckets)

The five components of the atom have the following syntax:

1. count is either a star (`*`) or a positive integer.

   If the atom's count is `*`, we call the atom "unmetered"; this means that no restriction on the number it can be used is imposed. If a count is given, the atom is "metered", and the count is used to initialize the atom's counter every time this rule line is reset.

2. shapes describes a list of shapes, in any of the 3 forms:

       *
       shape
       [shape [, ...]]

   A star (`*`) means "any shape". A single shape is synonymous to `[shape]`, i.e. is a shorthand for a list consisting of one shape. A comma-separated list in square brackets describes a list consisting of one, or more shapes. Each shape must be either an identifier (consisting of English letter, digits, and underscores, similar to an identifier in a programming language, e.g. `SQUARE`), or a double-quoted string of a form that could be legal as a UNIX file path (`"vm/arrows/up_arrow"`).
   Shapes are presently not case-sensitive; for each shape that is used in your rules, an appropriately named SVG file should exist in your shape file directory (or its subdirectory). For consistency, you may choose to use the same case (or case combination) in the names of the shapes in the rule set files as you use for the names of their SVG files.
3. colors describes a list of colors. Similarly to the list of shapes, it can have any of the 3 forms, with analogous semantics:

       *
       color
       [color [, ...]]

   Each color should be a an identifier listed in the color map file
4. positions describes a set of positions (cells) on the board. Similarly to the lists of shapes or colors, it can have any of the 3 forms:

       *
       positionSpecifier
       [positionSpecifier [, ...]]

   Each positionSpecifier must be either an integer number in the range 1..N2, representing a sequential number of a cell, or the name of one of the built-in or custom orders.
5. positions describes a set of positions (cells) on the board. Similarly to the list of shapes or colors, it can have any of the 3 forms:

       *
       positionSpecifier
       [positionSpecifier [, ...]]

   Each position specifier must be either an integer number in the range 1..N2, representing a sequential number of a cell, or the name of one of the built-in or custom orders.
6. buckets describes a set of buckets into which a game piece can be put. Similarly to the lists of shapes or colors, it can have any of the 3 forms:

       *
       bucket
       [bucket [, ...]]

   Each bucket must be either an integer number in the range 0..3, or an arithmetic expression (possibly set-valued). An arithmetic expression may include the special variable names `p`, `pc`, and `ps`, integers (literals), arithmetic operators (`+, -, *, /, %`), the equality operator (`==`), bracket lists (`[....]`), and round parentheses. The 5 special variables are interpreted during the evaluation of the expression as follows:
   - `p` = most recent bucket that has accepted anything
   - `pc` = most recent bucket that has accepted this color
   - `ps` = most recent bucket that has accepted this shape
   - `Nearby` = the bucket nearest to the location of the piece being picked
   - `Remotest` = the bucket farthest from the location of the piece being picked

Note: variable names are case-sensitive!

The variables `p`, `pc`, `ps` are nor defined before the first relevant actions have occured (i.e. `p` is not defined before the first successful move; `pc` is not defined in the context of an attempt to move a triangle, unless a triangle has been previously successfully moved; etc). When any variable occurring in an expression is not defined during a move attempt, the expression is simply ignored, i.e. does not provide any destination.

The values of the variables `Nearby` and `Remotest` are based on the position of the piece the player attempts to move. E.g. for a piece located in the bottom left corner of our 6x6 board (1 ≤ row ≤ 3, 1 ≤ column ≤ 3), `Nearby`=3 and `Remotest`=1. If we had a board with an even number of rows or columns (e.g. N=7), then for a piece located on the "central meridian" or the "equator" of the board (column=(N+1)/2, or row=(N+1)/2) these variables would be set-valued, with 2 values each; for a piece located in the central cell of the board, each of these variables would have the entire set [0,1,2,3] as its value.

For more details on expressions, please see Bucket expression arithmetic.

#### Atoms in property-based format

The alternative, property-based, format for atoms was introduced in GS 3.*, primarily because it became necessary to describe predicates that made use of arbitrary designer-defined properties of objects, and not just on the two "legacy" properties (color and shape).

If you just use the two legacy properties (color and shape), then property-based atoms are completely equivalent to the position-based ones. The difference between the two formats is that in the position-based format it was the position of the component in the atom which determined its meaning, while in a property-based atom the meaning of each component is explicitly labled. For example, if an atom is written in the position-based format as follows:

    	  (2,triangle,[red,black],L1,p+1)

then it can be written in the property-based format as follows:

    	  (count:2, shape:triangle, color:[red,black], pos:L1, bucket:[p+1])

If the property-based format is used, the order of components within an atom does not matter, so the same atom can also be written e.g. as

    	  (count:2, pos:L1, color:[red,black], shape:triangle,  bucket:[p+1])

with no difference in semantics.

When the property-based format is used, compoments with the value "*" (i.e., "any value is allowd") can be omitted. Thus, a non-metered atom does not need the "count:" component, an atom that allows picking game pieces from anywhere on the board does not need the "pos:" component, an atom that allows one to put objects to any bucket does not need the "bucket:" component, and so on. No component is mandatory. Thus, the position-based atom

    	  (*,*,*,*,*)

which means "you can pick any pieces and put them to any bucket, and the rule will be never exhausted" can be written in the property-based format simply as

    	  ()

The rule

    	  (*,square,*,*,Nearby)

which means "pick any square and put it to the nearest bucket; you can do it as many times as you want" can be written as

    	  (shape:square,pos:Nearby)

Since ver 6.041, we also have `postpos:`, which is like `pos:`, but is applied to the set of game pieces that have been selected by the other clauses. Thus

    	  (postpos:T, color:red)

will select the topmost red pieces (regardless of whether there are pieces of other colors in higher rows).

### Semantics of the rules

At any time during the episode, one of the rule lines is currently active (AKA "in control"). When the episode starts, the first rule line is made active; as the episode progresses, the control may be passed from the first rule line to the second, and so on, cyclically, under the rules discussed below.

Whenever a rule line becomes active (the first time, or any subsequent time), all applicable counters associated with the rule line and its atoms are set to their initial values given in the rule file. (This includes the counters for all metered atoms, and, if the rule line itself is metered, its global counter).

#### Acceptance of a move by a rule line

When the player attempts to move a piece G from position a to bucket b, the move attempt is accepted by the currently active rule line R if both clauses below are true:

- The rule line is unmetered, or the current value of its global counter is positive;
- At least one atom of the rule line currently accepts the move.

An atom accepts a move of game piece G from position a to bucket b, if everything below is true:

- The atom is unmetered, or the current value of the atom's counter is positive.
- The atom accepts the shape of the piece being moved, i.e. either the atom's shape list is `*`, or the atom's shape list includes the shape of the piece.
- The atom accepts the color of the piece being moved, i.e. either the atom's color list is `*`, or the atom's color list includes the color of the piece.
- The atom accepts the position a of the piece being moved. This means that either the atom's position list is `*`; or the position list contains the literal integer value of the piece's cell number; or the position list contains some order Q such that no presently-occupied cell of the board precedes a in this ranking. (E.g. if Q=T and all board cells above G's row are currently empty).
- The atom accepts the destination bucket b. This means that either the atom's bucket list is `*`, or at list one of the constants the list contains is equal to b, or one of the expression contained in the list currently evaluates to b. For example, if the last time when a red piece was successfully moved off the board it went to bucket No. 3, and the atom's bucket list contains the expression `(pc+1)%4`, then b=0 will be an accepted destination.

If the rule line has accepted a move, all applicable counters are decremented by 1. This includes:

- the counters of all metered atoms in this line that have accepted the move,
- the global counter of the rule line itself, if it is metered.

#### Transfer of control between rule lines

If the currently active rule line has accepted a move, the control stays with this line, and the Game Engine removes the game piece from the board, placing it into the requested destination bucket. If no pieces remain on the board, the episode is finished; if there are still pieces, the Game Engine will be ready to process the player's next move attempt.

If a rule line has refused to accept a move, the Game Engine checks whether the control should be transferred to the next rule line (or, if we are at the last line, to the first rule line again). This is done in any of the following cases:

1. The rule line is metered, and its global counter has been reduced to 0.
2. All atoms of the rule line are metered, and the counter of each of them has been reduced to 0 (i.e. every atom has been "exhausted").
3. At present (i.e. only using the atoms that are either unmetered, or metered but not yet exhausted), no move of any piece presently still on the board to any bucket could be accepted by this rule line.

(We will say that a rule line is "exhausted" if either (1) or (2) above is the case).

If any of the above criteria applies, the control is tranferred to the next rule line, its counters are reset to the initial values, and the Rule Engine checks whether to apply that rule line to the move. This process may be repeated until either of the following happens:

- The now-current rule line has accepted the move. The Game Engine then reports the acceptance of the move.
- The now-current rule line has rejected the move, but there is no need to transfer control to the next line. (I.e. the rule line is not exhausted, and it is capable of accepting some other move). The Game Engine then reports the rejection of the move.
- While processing a given move, the control has shifted through every rule line, came back to the line that was active when the Game Engine started to test this move for acceptance, and then passed to the next line again, even though all of the counters in all rule lines have been reset. This indicates that no rule line can accept any move with any of the pieces currently on the board. The Game Engine then reports the rejection of the current move and a stalemate, i.e. the end of the game due to the impossibility of any moves.

## Examples

#### Example 1

Position-based format | Property-based format
    1 (*,*,RED,T,0) (*,*,GREEN,T,1)
    1 (*,*,BLACK,T,2) (*,*,YELLOW,T,3)
    1 (color:RED,pos:T, bucket:0) (color:GREEN, pos:T, bucket:1)
    1 (color:BLACK, pos:T, bucket:2) (color:YELLOW,pos:T,bucket:3)

- (1) If there are any green or red pieces in the topmost non-empty row of the board, this rule set will allow you to pick any of these pieces, and put it to bucket 0 if red or to bucket 1 if green.
- (2) After that one piece is picked, or if there are no green or red pieces in the topmost non-empty row of the board to begin with, then, if there are any black or yellow piec in the topmost non-empty row, you can pick that piece and put it to bucket 2 (if black) or to bucket 3 (if yellow).
- (3)Go back to (1) and repeat.

This rule set is guaranteed to complete if the board has no pieces of colors other than red, green, black, or yellow. But if any such piece exists, a stalemate will occurr as soon as all pieces above, or in the same rows as, that piece, are removed.

#### Example 2

Position-based format | Property-based format
    * (*,CRANE,*,T,0)  (*,PELICAN,*,T,1)
    * (*,SHARK,*,B,2)  (*,CRAB,*,B,3)
    * (shape:CRANE,pos:T,bucket:0)  (shape:PELICAN,pos:T,bucket:1)
    * (shape:SHARK,pos:B,bucket:2)  (shape:CRAB,pos:B,bucket:3)

Here, the top rule and its atoms are unmetered. So one can first remove all cranes and pelicans as long as they are in the currently topmost occupied row. After that, one can remove all sharks and crabs as long as they are in the currently bottommost occupied row. After that, if any pieces still remain on the board (i.e. you have a shark or crab on top, and a crane or pelican below), stalemate will occur.

Naturally, if you create a rule like this, you need to include the appropriate `shapes` parameter in your parameter set (in the appropriate line of the trial list file), and to ensure that the SVG files for those shapes do exist.

#### Example 3

Position-based format | Property-based format
    * (2,*,YELLOW,*,[0,1]) (2,*,RED,*,[2,3])
    * (*,*,*,*,(p+2)%4)
    * (count:2, color:YELLOW, bucket:[0,1]) (count:2, color:RED, bucket:[2,3])
    * (bucket:[p+2])

The first rule line allows moving up to 2 yellow pieces to buckets 0 or 1, and up to two red pieces to buckets 2 or 3. The control shifts from this line once each atom is either exhausted or has no applicable pieces (i.e. after you have removed 2 yellow pieces, or no yellow pieces are left; AND you have removed and 2 red pieces, or no red pieces are left). After this point, the control will stay with the second line until the end of the game. It will allow you to pick any piece, and move it to the bucket opposite of the last bucket used while the first line was in control; after that, you will be able to put pieces one by one, alternatingly, into two opposite buckets (e.g. 1 and 3).

Note that the Game Server carries out `result := result modulo 4` operation with the result of the bucket expression, to ensure it always maps to a legal bucket number. Therefore, the `%4` part in the bucket expression is actually superfluous.

#### More examples

More rules set examples can be found in the directory `rules` in the GitHub repository RuleGame/Rule-Game-game-data, or on sapir in `/opt/tomcat/game-data/rules`.

You can also see various rules in operation at the MLC launch page.

## See also

- The original (GS 1.*) write-up on the rules: _0.Semantics2020.07.10.txt