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.
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.
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 rules used in a particular episode are defined by a rule file. The file consists of two sections:
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.
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 [,
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 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:
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 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:
If you use property-based objects with properties other than color and shape, you must use property-based atoms.
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:
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.
* 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").
* color [color [, ...]]Each color should be a an identifier listed in the color map file
* 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.
* 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.
* 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:
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.
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 propert-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 atome 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)
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).
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:
An atom accepts a move of game piece G from position a to bucket b, if everything below is true:
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:
(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:
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) |
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.
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.
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 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.