Filters
Every CQL file is built from filters.
A filter is a CQL construct which can either match the current position or fail to match the current position.
If a filter matches the current position, we say the filter is true in the current position. Otherwise, we say the filter is false in the current position.
We sometimes just say a filter matches as shorthand for "matches the current position". Or we might say a filter matches a position X if, when the current position is set to the value of X, the filter matches the current position.
A filter is evaluated at the current position. This evaluation determines two things about the filter:
- Whether the filter matches the current position
- If it does match the current position, what the value of the filter is
A filter is only ever applied to the current position. But
it can be applied to many different positions in a game as the current
position is updated by the CQL main loop. Some filters, like line
and find
can themselves temporarily modify the current position.
For example, one filter is the check
filter. This will match the current position if one
of the Kings is in check in that position. The name of the
check
filter is simply check
Many filters include other filters as constituents. For example, an
or
filter has two constituents: the filter to its left and the filter to its right.
check or Ra3is a single filter with two constituents. The filter matches a position when either there is a check or the White rook is on a3 (or both).
value of a filter
Some filters can have a value. This means that when the filter is evaluated in the current position, the filter computes an associated value. Sometimes we say the filter returns this value or that it represents this value.A value can be either:
- a set of squares (sometimes abbreviated to just set)
- a number (32-bit integer)
- a position
These are the only values a filter can represent. Some filters never have a value, even if they match a position. These filters are called logical filters. Examples are check, mate, hascomment.
Numeric Filters
A filter that represents a number is called a numeric filter.A numeric filter only has a value if it matches the current position.
Numeric filters include rank, file, sort, line and +. Examples of numeric filters are:
34 #R + #q rank R
Here, 34
is a numeric filter whose value is, not surprisingly, 34.
#R+#q
is a numeric filter whose value in the current position is the sum of the
number of white rooks and the number of black queens in the position. More exactly, R
is a filter whose value is the set of squares on which there is a white rook. #R
is a filter whose value is the cardinality of this set of squares, which is to say the number of white rooks. Likewise #q
is a filter whose value in the current position is the cardinality of the set of squares on which there is a black queen. +
is a filter whose value is the sum of the values of its left and right arguments, which in this case are the number of white rooks and the number of black queens respectively.
The last filter, rank R
is more complicated. It only matches the position if there is exactly one white rook in the position. In that case its value is the
rank of the square of that white rook. But if there are no white rooks, or more than one white rook, the rank
filter does not match.
Set Filters
A filter that represents a set of squares is called a set filter. Set filters include attacks, between, piece designators likePa-h2
, square, piece and the direction filters (like up or vertical).
Unlike numeric filters and position filters, a set filter has a value no matter what: if a set filter does not match a position, its value is the empty set.
Examples of set filters are
move from Q to n pin
The value of move from Q to n
is the set of squares on which there is a white queen which captures a black knight on the next move. The value of pin
is the set of squares on which there is a piece pinned against its own king by a piece of the opposing color. Each of these filters will only match a position
if the associated set is nonempty.
A piece variable is a set filter even though it stores the value of a piece. When a piece variable is used, its value is the square on which the piece it represents lies.
Position filters
A filter that represents a position is called a position filter. Position filters include child, parent, position, find and currentposition.A position filter only has a value if it matches the current position.
Examples of position filters are:
parent find move promote R
Here, parent
is a position filter whose value is the position that is the parent of the current position. The find
filter takes a single filter
as argument, and returns the next position in which that filter is true.
In this case the argument is move promote R
. The move
filter here matches a position if the next move is a rook underpromotion. Thus,
find move promote R
will have as value the position from which the next rook underpromotion occurs, if it exists. If there is no such next
rook underpromotion, then the find
filter will not match.
arguments to filters
Some filters take one or more arguments. An argument is another filter that the filter applies to.
For example, the power filter takes one argument a set filter. A set filter is a particular type of filter whose value is a set of squares. The power filter has a value equal to the total material of all the pieces on a square in the set given by its argument.
power R
Here, the argument to power
is the filter R
.
Here, R
is a set filter that is equal to the set of squares that have a white rook on them. If the start position of a chess game, for instance, its value
is the set of two squares: a1
and h1
. The power filter then computes the material on those squares (which would be 10 in that position).
When a filter takes only one argument, the argument just follows the filter:
sort R not Q #P
Some filters have multiple arguments.
In the case of prefix filters, where the name of the filter precedes the arguments, the arguments are enclosed in parentheses:
between (R n) ray (R between (N k) k) max (#R #Q)
The arguments to between (R n)
for example are the two filters R
and N
.
The ray
filter above, however, has three arguments: R
, between (N k)
and k
.
Some filters are infix filters, where the name of the filter is between the two arguments to the filter. The infix filters are mostly symbols (like >
, +
, ==
, :
and so on) with the exceptions of and
, or
, attacks
, and attackedby
. Thus in the example
A attacks k
the attacks
filter has two arguments: A
and k
. We call A
the left argument of the filter
attacks and we call
k the right argument
of the filter attacks
.
parameters to filters
Some filters also have parameters. A parameter is a keyword, or a keyword followed by a value, that controls how the filter acts on its arguments.
For example, the move
filter has many parameters (and no arguments). The filter:
move legal from R to _is a
move
filter with three parameters:
- The
legal
parameter, which has no value. Thelegal
parameter specifies that all legal moves are considered, rather than just the moves that occur in the PGN file - The
from
parameter, whose value is the filterR
. It specifies that only moves of a white rook are to be considered - The
to
parameter, whose value is the filter_
. It specifies the only moves to an empty square are to be considered
The filter will match the current position if there is a legal move of a white rook to an empty square.
Some filters have an optional unnamed parameter: an optional range. A range is specified by listing one or two numbers. It represents all integers
between its these values, inclusively. For example the range 1 3
represents the numbers 1,2,3
.
Precedence and Parsing
Sometimes it can seem difficult to parse an expression using filters. For example, what does this mean?not Ra3 or check
It might mean either of the two possibilities below:
{not Ra3} or check not {Ra3 or check}
The first possibility would match a position in which either there is no white rook on a3
or
one side is in check. The second possibility would match a position in which there is neither
a white rook on a3
nor is there a check.
CQL in this case chooses the first interpretation. The reason has to do with precedence: the not filter has a higher precedence than the or filter.
Precedence is discussed in more detail in the precedence section. See also the precedence table, concisely summarizing the precedence rules.
reading argument lists
Sometimes reading CQL code can be confusing because an argument list might contain more than one filter. An argument list is a series of one or more filters separated by white space and enclosed in parentheses.For example, the argument list
(Ra3 check)is an argument list with two filters in it . Similarly
(Ra3 or Nb2 check or stalemate)is another argument list with two filters. The first filter is the or filter
Ra3 or Nb2which matches a position if there is a white rook on a3 or a white knight on b2. The second filter is
check or stalematewhich is true if either the position is stalemate or check
This could be written equivalently as
({Ra3 or Nb2} {check or stalemate})If it's clearer to you, always enclose filters in braces.
You might be tempted to think that
(Ra3 or Nb2 check or stalemate)Might be parsed as
(Ra3 or {Nb2 check} or stalemate)but this would be incorrect, because
Nb2 checkis not the same as
{Nb2 check}
Note that there is no filter
Nb2 checkThe rule to remember here is this: Two filters next to one other separated by white space is never a single filter
More parsing examples
Likewise,{not Ra3 check}matches positions where the R is not on a3 and the position is a check. It is equivalent to
{ {not Ra3} check}
This is because there is no such single filter
Ra3 checkso that
Ra3 checkcannot be an argument to
not,
which takes only a single filter as its argument.
This rule is most commonly applied to transform filters (shift
,
flip
and so on). All of these take a single filter argument, but it is common to want to apply these to multiple filters at once. Remember to enclose their bodies in braces if you want to apply them to multiple filters at once. For instance, use
{rotate90 Ka1 kh8}would be parsed as
{{rotate90 Ka1} kh8}which would match a position with the white King on a corner and the black on h8.
By contrast,
{rotate90 {Ka1 kh8}}
would match positions with the kings on diagonally opposite corners.
However, since a transform filter is itself one filter, they can be chained:
flipcolor rotate90 {Ka1 kh8}would apply
flipcolor
to the result of applying rotate90
to {K1 kh8}
(which in point of fact would not change anything semantically here).
combining filters
One of the most powerful features of CQL is that filters can be combined. Any time a filter takes an argument, or a parameter takes a value, that argument or value can be another filter.
For example, #
is a filter whose value is the number of squares of its argument, which must be a set filter.
Thus, #R
is the number of white rooks in a position.
A attacks k
is a filter whose value is the set of squares on which there is a white piece giving check.
>
is a filter which is true if its left argument is greater than its right argument. Therefore,
#{A attacks k}>1will be true of a position where White is giving double check. In the above, we don't actually need the braces.
In fact, we can just write
A attacks k>1
here because >
converts a set argument to a cardinality in this case.