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:

  1. Whether the filter matches the current position
  2. 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 Ra3
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
  #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 like Pa-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.

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 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:
  1. The legal parameter, which has no value. The legal parameter specifies that all legal moves are considered, rather than just the moves that occur in the PGN file
  2. The from parameter, whose value is the filter R. It specifies that only moves of a white rook are to be considered
  3. 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 Nb2
which matches a position if there is a white rook on a3 or a white knight on b2. The second filter is
check or stalemate
which 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 check
is not the same as
{Nb2 check}

Note that there is no filter

Nb2 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 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 check
so that
Ra3 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, 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}>1
will 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.