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 ♖a3
is 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 at most one of:
- a set of squares (sometimes abbreviated to just set)
- a number (32-bit integer)
- a position
- a string
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, in.
Sometimes the type of a value depends on the way the filter is used. For example the + filter can be a string or a number:
"zug" + "zwang" // string filter 50 + 1 // numeric filter
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, positionid, line and abs.
Examples of numeric filters are:
34 #♖ + #♛ rank ♖
Here, 34
is a numeric filter whose value is, not surprisingly, 34.
#♖+#♛
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, ♖
is a filter whose value is the set of squares on which there is a white rook. #♖
is a filter whose value is the cardinality of this set of squares, which is to say the number of white rooks. Likewise #♛
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 ♖
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 →, between, piece designators like♙a-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
♕――♞ pin
The value of ♕――♞
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 ――=♖
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 ――=♖
. The ――
filter here matches a position if the next move is a rook underpromotion. Thus,
find ――=♖
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.
string filters
A string filter is a filter whose value is a string, that is, a sequence of characters. Strings and string filters are discussed in more detail here.
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 ♖
Here, the argument to power
is the filter ♖
.
Here, ♖
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 ♖ not ♕ #♙
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 (♖ ♞) ray (♖ between (♘ ♚) ♚) max (#♖ #♕)
The arguments to between (♖ ♞)
for example are the two filters ♖
and ♘
.
The ray
filter above, however, has three arguments: ♖
, between (♘ ♚)
and ♚
.
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
, →
, and ←
. Thus in the example
△→♚
the →
filter has two arguments: △
and ♚
. We call △
the left argument of the filter
→ and we call
♚ 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.
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 ♖a3 or check
It might mean either of the two possibilities below:
{not ♖a3} or check not {♖a3 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
(♖a3 check)
is an argument list with two filters in it .
Similarly
(♖a3 or ♘b2 check or stalemate)is another argument list with two filters. The first filter is the or filter
♖a3 or ♘b2which 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
({♖a3 or ♘b2} {check or stalemate})If it's clearer to you, always enclose filters in braces.
You might be tempted to think that
(♖a3 or ♘b2 check or stalemate)Might be parsed as
(♖a3 or {♘b2 check} or stalemate)but this would be incorrect, because
♘b2 check
is not the same as
{♘b2 check}
Note that there is no filter
♘b2 check
The 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 ♖a3 check}
matches positions where the ♖ is not on a3
and the position is a check.
It is equivalent to
{ {not ♖a3} check}
This is because there is no such single filter
♖a3 check
so that ♖a3 check
cannot be an argument to not,
which takes only a single filter as its argument.
This rule is most commonly applied to transform filters (shift
,
✵
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 ♔a1 ♚h8}would be parsed as
{{rotate90 ♔a1} ♚h8}which would match a position with the white King on a corner and the black on h8.
By contrast,
{rotate90 {♔a1 ♚h8}}
would match positions with the kings on diagonally opposite corners.
However, since a transform filter is itself one filter, they can be chained:
⬓ rotate90 {♔a1 ♚h8}would apply
⬓
to the result of applying rotate90
to {♔1 ♚h8}
(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, #♖
is the number of white rooks in a position.
△→♚
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,
#{△→♚}>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
△→♚>1
here because >
converts a set argument to a cardinality in this case.