Structure of the game tree
The game tree represented by a game in a PGN file conceptually consists of a tree of positions. Each position has a unique location: two positions with the same board position and side to move are distinct if they occur at different places in the game tree.
The first position in game is called the initial position. Each position P other than the initial position was reached by a single move from another unique position Q in the game tree.
That single position Q which reaches P by a single move is called the parent of Q. Each position other than the initial position has a unique parent. The initial position has no parent.
A given position P in the game tree can have 0 or more children. Each of its children are reached by a single move from P, and each such child has P as its parent. Thus, the children of P are the positions whose parent is P.
In the PGN file, there can be several moves that follow a particular position P. The first of these moves is called the primary move.
Any remaining moves are called secondary moves. In the PGN file, the secondary moves are enclosed in parentheses:
1. e4 (1. d4 Nf6 (1...d5 2. c4)) (1. Nf3 d5) 1...e5 2. d4In the above PGN fragment, the primary move from the initial position is
1.e4
.
The secondary moves from the initial position are 1. d4
and 1. Nf3
.
The primary move from the position after 1.d4
is 1...Nf6
and the secondary move is 1...d5
.
Each position P can be reached from the initial position by a unique sequence of 0 or more moves (it is 0 moves if P is the initial position).
The number of moves in the unique sequence of moves from the initial position to P is called the ply of P. Thus, the initial position has a ply of 0, and the ply of any other position is one greater than the ply of its parent.
The depth of a position P is the number of secondary moves which occur in the sequence of moves from the initial position to P. Thus, the depth of the initial position is 0. If P is reached by a primary move from its parent, then the depth of P is equal to the depth of its parent. Otherwise the depth of P is one greater than the depth of its parent. In the above PGN fragment, the depth of the position after 1. d4
is 1
. The depth of the position after 1. e4 e5 2. d4
is 0.
A position whose depth is 0 is a mainline position. A position that is not a mainline position, and thus whose depth is greater than 0, is a variation position. Each position is either a mainline or a variation position, but not both.
A position with no children is called terminal. Each move from a specific position P has a unique move index. The primary move from P has move index 0. If a move is the i'th move from P occuring in the PGN file, then its move index is i (where we start the count from 0). Thus, a move is secondary if and only if its move index is greater than 0.
The number of children of P equals the number of moves from P. Each child of P has a unique child index which is equal to the move index of the move from P that reaches that child.
The child of P with index 0 is called the primary child of P.
the game tree and the variations parameter
By default, CQL only considers mainline positions, that is, positions whose variation depth is 0. That is, when CQL reads a PGN file, it conceptually prunes all positions that are not mainline positions from the game tree (when the game is output, the pruned positions are restored however).
This default behavior can be changed by specifying variations
in the CQL header:
(cql input foo.pgn variations) ...
It can also be changed by invoking cql with the -variations
option:
cql -variations foo.cql
Note that variations
as a CQL header parameter is not the same as variation, which is a filter that tests if the current position is in a variation.
Note as well that when variations
is not set, so the default situation arises, then:
- Every position is a mainline position:
mainline
is always true. - Every child is primary child. Thus, there is no reason to use the more complex
child ( i )
form to get the i'th child of the current position. Instead, just usechild
. - If X and Y are positions, then
X<Y
is true if and only if X is an ancestor of Y. - There is at most one move from a position.
- The position id of a position is equal to its ply, so that the following filter is always true:
ply==positionid
Much of the complexity in CQL comes from dealing with variations. Users interested in full chess don't need to worry about it.
List of game tree filters
CQL has several filters for getting information about the location of the current position in the game tree and for traversing the tree.
In the table below, any filter that would return a position that does not exist does not match the current position. Also, as usual in CQL, if any of the arguments of a filter do not match, neither does the filter.
For example, if the current position is the initial position, then parent
does not match. Similarly, if the current position
is a terminal position, then child
does not match.
In the table below, i always represents a numeric argument, that is to say, any numeric filter. For example, because the syntax of the
position
filter is given as:
position ithat means that we can write
position 0
which is the initial position.
Also in the table below, the symbols x and y represent filters whose values are positions. Thus, because the syntax of the ancestor
filter is given as
ancestor (x y)
That means we can write something like
ancestor (parent child)
This will be true whenever the parent of the current position is an ancestor of the primary child of the current position, which is true whenever they exist. Thus, the filter will be true whenever the current position is neither initial nor terminal.
filter name | example | description |
---|---|---|
initial | initial |
current position is first position of game |
mainline | mainline |
current position is in the mainline |
movenumber |
movenumber |
move number of current position |
terminal |
terminal |
current position has no successors |
ply |
ply |
the ply of the current position |
variation |
variation |
current position is in a variation |
virtualmainline |
virtualmainline |
current position is in a virtual mainline |
positionid |
positionid |
position id of current position |
position |
position i |
position whose position id is equal to the i |
depth |
depth |
variation depth of current position |
child |
child child (i) |
primary child of current position; the i'th child of the current position |
parent |
parent |
parent of current position |
lca |
lca(x y) |
latest common ancestor of its two arguments |
ancestor |
ancestor(x y) |
position x is an ancestor of position y |
descendant |
descendant(x y) |
position x is a descendant of position y |
distance |
distance(x y) |
The number of moves distance between positions x and y |
relop |
x<y |
for relop a relational operator, whether the position ids of its argument have the specified relation |
sidetomove |
sidetomove==white |
the side to move of the current position |