Documentation

Position Relationship Filters

Find and compare related positions with relationship filters including find, echo, and consecutive move constraints.

The find filter is used to search for positions appearing either previous or subsequent to the current position that match a provided filter. The echo filter is used to find relationships between positions within a game. The consecutivemoves filter is used to determine the longest series of consecutive moves in common between two variations.

The find Filter

find [quiet] [<--] [all | range] target-filter

The find filter evaluates the target-filter first at the current position and then at every position that is a descendant of the current position until the result of target-filter matches the position in which it is evaluated. The result of the find filter is the first position for which the target-filter matched or None if no position matches. If the token <-- appears between find and target-filter, the target-filter will be evaluated for each ancestor position instead of each descendant. If the all keyword appears before the target-filter, the search will not stop at the first matching position and the result of the find filter will be the number of matching positions or None if no searched position matches. If a range is provided in place of all, the result will be the number of matching positions if the number of matching positions is within the provided range (which may include zero) and None otherwise. The quiet, <--, and all keywords may appear in any order but may not follow the optional range.

Positions are searched in position ID order (ascending order for descendants, descending order for ancestors) which is the same order that CQLi uses to process positions in the game evaluation loop.

Auxiliary Comments

When a find filter matches, automatic comments are applied to the positions for which target-filter matched by default. If neither all nor a range is provided, the comment Found will be applied to the single matching position, otherwise each matching position will have a comment of the form Found i of n where n is the total number of matching positions that were found and i is the index (1in) of the match based on the search order. The automatic comments generated by the find filter are suppressed if the quiet parameter is specified or the –quiet or –silent commandline options are used.

Use Cases

The find filter is particularly useful in situations where it is more natural to search all positions for each iteration over a set of pieces or squares than it is to construct the search over positions in the main loop. In such cases the find filter is typically preceded by initial to ensure the query is only executed once per game instead of once per position. One such common theme is when searching for the “greatest” or “least” of some situation across games. For example, to find the greatest number of captures by a single piece in a game, something that would be quite awkward without using the find filter, the following query may be used:

// Games with 10 or more captures by a single piece
initial
piece $p in [Aa] {
    sort "Number captures by single piece"
    { find all move previous capture . from $p } >= 10
}

In the initial position, the piece filter is used to iterate over all black and white pieces. For each piece, the find filter is used to count the number of positions where the move that led to the position was a capture by the current piece. If the total number of matching positions is greater than or equal to 10 then the game matches. Because the find filter is enclosed by a sort filter, only the largest value returned by find across all iterations will be reported for the game. Matching games will also be sorted in the output PGN file with games with the largest number of single-piece captures appearing first. The Smart Comments facility ensures that the comments automatically added by find are discarded unless they correspond to a piece that simultaneously has more captures than any other piece and has 10 or more captures. Captures by a pawn before and after promotion will both be counted toward the total number of captures for that piece by virtue of the Piece Tracking mechanism. The find filter will insert comments enumerating each capture by the piece with the greatest number of captures for each matching game. An example of a matching game with the find and sort comments inserted is:

{Number captures by single piece: 13} 1.e4 c5 2.Qh5 d6 3.Bc4 e6 4.Qh4
Qxh4 {Found 1 of 13} 5.g3 Qxe4+ {Found 2 of 13} 6.Ne2 Qxc4 {Found 3 of 13}
7.Nec3 Nc6 8.Na3 Qd4 9.Ne2 Qe5 10.O-O Nb4 11.Nc4 Qxe2 {Found 4 of 13}
12.Re1 Qxe1+ {Found 5 of 13} 13.Kg2 Qe4+ 14.Kg1 Qxc4 {Found 6 of 13} 15.b3
Qxc2 {Found 7 of 13} 16.Ba3 Nxa2 17.Rxa2 Qxa2 {Found 8 of 13} 18.Bb4 Qxb3
{Found 9 of 13} 19.Kg2 Qxb4 {Found 10 of 13} 20.h4 Qe4+ 21.f3 Qe2+ 22.Kh3
Qxf3 {Found 11 of 13} 23.d3 Qxd3 {Found 12 of 13} 24.Kg4 e5+ 25.Kg5 Qf3
26.g4 Qxg4# {Found 13 of 13} 0-1

The following query uses find inside a square iterator to find games with the largest number of captures (by both sides) on a single square:

// Games with 10 or more captures on a single square
initial
square sq in . { 
    sort "Most captures on a single square"
    { find all move previous capture sq } >= 10
}

Note that this does not limit captures to consecutive captures. See the line filter for an example that will find the longest series of consecutive captures.

The following query will find games with more than 5 promotions, sorted by the number of promotions in the game:

// Games with more than 5 promotions
initial
sort "Number of promotions" find all move promote A previous > 5

For games containing variations, promotions occurring in separate lines will all contribute to the total which likely is not desired. To limit the total to those occurring within a single line, it is necessary to consider the moves of each line separately. This can be done by using find <-- to search backwards from each terminal position:

// Games with more than 5 promotions in a single line
terminal
sort "Number of promotions" find <-- all move promote A > 5

The same method could be applied to the previous examples as well. In most cases it probably will not be desired to consider variations at all in which case variations can simply not be enabled or the mainline filter can be used to limit positions searched by the find filter to mainline positions.

The backwards searching find filter is often combined with terminal in the same way that the forward searching find filter is combined with initial. For example, to find games where neither side castled, the following query may be used:

// Games where neither side castled
terminal
not find <-- move castle

The echo Filter

echo [quiet] ( source target ) [in all] target-filter

The echo filter is used to search for arbitrary relationships between positions in a game. The echo filter takes a parenthesized list of two identifier names and creates a new variable scope in which Position variables with the names source and target are created. The target-filter is then evaluated once for every position in the game except the current position. For each evaluation of target-filter, the source variable is set to the original current position, and the target variable is set to the new position. If in all appears immediately after the parenthesized list, the original current position is included in the list of target positions. Target positions are processed in position ID order.

If target-filter has Numeric type, the echo filter is also Numeric with the result being the largest value of target-filter during evaluation of the echo filter. Otherwise the type of echo is Boolean and matches the position if any evaluation of target-filter matches the position.

For example, to find pairs of positions that are identical except for the side to move, the following query may be used:

echo (source target) {
    source & target == .
    sidetomove != source:sidetomove
}

Note that while it is conventional to name the source and target variables source and target, they may have any valid variable names. The following example will find pairs of positions that differ only in that en passant capture is available in the one case and not in the another:

move legal enpassant
echo (source target) {
    not move legal enpassant
    source & target == .
    sidetomove == source:sidetomove
    source:move legal enpassant:
        comment("enpassant capture " currentmutation " legal here")
    comment("enpassant capture not legal here")
}

Below is a matching study found in the HHdbVI endgame study database:

{Schach/3 (EG#21134).} {Game number 6319} 1.Bf2+ $1 1...Kh1 2.Kf3 f4 {LCA}
(2...g5 3.Bg3) 3.c5 $1 (3.g4 $2 3...g5 4.c5 {enpassant capture not legal here}
{target -->move 4(btm)} {id:14}) 3...g5 4.g4 {CQL} {enpassant capture 4...fxg3
legal here} {source <--move 4(btm)[14]} 4...fxg3 5.Bxg3 Kg1 6.Bxh2+ 1-0 

Auxiliary Comments

As can be seen in the above example, the echo filter adds several types of comments for matching positions:

  • For each matching pair identified by echo, the source position is annotated with a comment of the form source <--target-position and the target position is annotated with a comment of the form target -->source-position where source and target correspond to the variable names used with echo.
  • A Position ID comment is added to variation positions referenced by a matching pair annotation.
  • If the LCA of source and target is neither source nor target, the LCA position will be annotated with the comment LCA.

The automatic comments generated by the echo filter are suppressed if the quiet parameter is specified or the –quiet or –silent commandline options are used.

Using echo with sort

If the target-filter of echo is a Numeric filter, the echo filter itself is a Numeric filter that may be used as the target of a sort filter. As a special case, when the target of a sort filter is an echo filter, Smart Comments will suppress all comments emitted from the evaluation of the echo filter except those that correspond to the evaluation producing the largest numeric value. This is particularly useful in situations where there are many matching position pairs that would otherwise generate a large number of comments. Note that because a Numeric echo filter maximizes the value of its target positions, an echo filter may not be used as the target of a sort min filter.

Use Cases

The echo filter is particularly well-suited to situations in which specific relationships between two positions are sought and either the nature of the relationship does not imply an ordering between such positions or position pairs may span variations. When a relationship implies a strong order (e.g. two positions in the same line in which one must have preceded the other), the find filter may be more appropriate.

Aside from the comments that echo adds and the special behavior when combined with sort, a query of the form:

echo (source target) {
    ...
}

can be approximated using the find filter:

source = currentposition
initialposition : find quiet all {
    target = currentposition
    source != target    // Remove to obtain the 'in all' behavior
    ...
}

The type and result of this query may also be different than the corresponding echo filter. The echo query is of course more compact and introduces useful comments but the point is that the basic functionality can be realized using the find filter and potentially tailored to specific needs.

Performance Considerations

The use of echo can result in relatively slow queries as the target-filter is evaluated for every position in the game every time the echo filter is reached. To mitigate the performance impact, place gating checks before the echo filter when possible so that the echo filter is only evaluated for positions that match some prerequisite criteria. An example of this is checking that en passant capture is legal before entering the echo filter in the previous example.

The consecutivemoves Filter

consecutivemoves [quiet] [range] ( position1 position2 )

The consecutivemoves filter takes two Position arguments and determines the longest common sequence of identical moves in the lines bound by the provided positions and their LCA. The result of the consecutivemoves filter is the length of the longest common move sequence. The positions corresponding to the longest common move sequence pair found by a consecutivemoves filter across all evaluations for the current game are annotated with correlating comments unless the quiet parameter is specified. This means that a single consecutivemoves filter will annotate at most one sequence pair per game, regardless of how many times the filter is evaluated during the game.

For the purposes of this filter, two moves are considered to be identical if the to square and from square are the same and the color and type of piece moved is the same. This means that promotion of a pawn to different pieces are considered the same, as are moves that differ only in whether a capture occurred. Additionally, there is no distinguishing between a normal pawn capture and an enpassant capture (both involve a pawn moving from and to identical squares).

If either of the two arguments provided to consecutivemoves is None, the result of the filter is None. If both provided positions are part of the same line, then one of them will be the LCA of the two positions and the result of consecutivemoves will be None.

The consecutivemoves filter is typically used to identify key sequences in chess endgame studies and is often accompanied by the echo to provide the positions from which to analyze.

Auxiliary Comments

The positions of the corresponding matched sequence pairs are annotated comments having the form:

name-move[ index ]

The index starts at 1 and represents the position of the move within the matching sequence. By default, name corresponds to the names of the variables used as arguments to consecutivemoves. For example, in the following query x and y are position variables:

consecutivemoves(x y)

and the PGN output of a game where this filter matched might look like:

1.Bd5+ (1.Rd1 1...Nd3+ 2.Kd2 b2 3.h7 a1=Q) 1...Kxd5 2.O-O-O+ 2...Nd3+
(2...Kc4 3.Kb2 Nd3+ 4.Ka1) 3.Rxd3+ Kc4 4.Rd4+ (4.Kb2 {y-move[1]}
4...Kxd3 {y-move[2]} 5.h7 {y-move[3]} 5...a1=Q+ {y-move[4]} 6.Kxa1
{y-move[5]} 6...Kc2 7.h8=Q b2+ 8.Ka2 b1=Q+ 9.Ka3 Qb3#) 4...Kxc3 5.Rc4+
5...Kxc4 6.Kb2 {x-move[1]} 6...Kd3 {x-move[2]} 7.h7 {x-move[3]} 7...a1=Q+
{x-move[4]} 8.Kxa1 {x-move[5]} 8...Kc2 9.h8=Q 1-0

The name values of <source> and <target> are used for non-variable arguments corresponding to the position1 and position2 parameters, respectively.

The automatic comments generated by the consecutivemoves filter are suppressed if the quiet parameter is specified or the –quiet or –silent commandline options are used.