The echo filter lets you look for positions that have a specified relation to the current position. The normal use of the echo filter has the following syntax:
    echo (source_var target_var) body_filter

Here source_var and target_var are variable names, and body_filter is a filter.

The operation of echo is as follows:

  1. The variable source_var is set to the current position. This is called the source position
  2. For each position P other the source position in the game:
    • the current position is set to P, the target position
    • target_var is set to the current position
    • body_filter is evaluated

The echo filter matches the current position (which is the source position) if body_filter was true for some target position.

After completion, of course, the current position is restored to its original value, and source_var and target_var are reset.

including the source position as a target

If the echo filter includes the keywords in all just before the body filter, then the target position will be set to the source position as well as to the other positions

echo quiet

If they keyword echo is immediately followed by the keyword quiet then comments automatically generated by the echo filter are suppressed.

numeric value of echo

If body_filter is a numeric filter then the echo is also a numeric filter whose value at a position is the maximum value of body_filter attains over all target positions at which it is evaluated.

Therefore, you can use sort with echo just like with any other filter, so long as body_filter is a numeric filter, with one limitation: the sort must be in the max direction (the default). sort min is not supported with echo.

Therefore, when sort is used with echo, the sort filter finds the positions source and target that maximize the value of the body_filter . The default echo and sort generated PGN comments should indicate which source/target position pair attained this maximum. Example of this usage include zugzwang2.cql, fatamorgana.cql, movedblackpieceecho.cql, and underpromotionecho.cql .

comments in echo

echo will generate automatic comments indicating each matched source position and target position. echo will also generate a comment indicating the lca of the source position and target position whenever the LCA is different from each of these. These automatically generated comments can be removed by specifying the quiet option in the CQL header.

echo comments can quickly become unwieldy. It is often useful to sort an echo, since a sort only generates comments associated with its maximum value.

Useful filters in echo

You can use any filter you want in an echo, but some tend to be particularly useful:


The echo filter is used in castleecho.cql, chameleon.cql, enpassantecho.cql, fatamorgana.cql, flipverticalecho.cql, knightpawnforkecho.cql, movedblackpieceecho.cql, parallelpaths.cql, platzwechselecho.cql, queenpawnpinecho.cql, underpromotionecho.cql, wcct7.cql, zugzwang1.cql, and zugzwang2.cql.

We now will go through in a little detail how CQL finds some studies using the echo filter. First, we look at a simple zugzwang script.

A simple zugzwang example from Korolkov

For the simplest example, let's go through zugzwang1.cql.

Let's go through the CQL code in zugzwang1.cql, and specifically what happens when the CQL file reaches the study Korolkov 1972, USSR team champ. VIII, 1/2 p..

The CQL file begins with the list of CQL parameters which here importantly tell CQL to look in the variations:


The next few filters limit our consideration to win studies and the current position to be in the mainline that are black to move:

    result 1-0 mainline  btm 

At this point, the current position is a black to move mainline position in a win study.

In particular, the current position will match the filters so far at the position after move 4.Ka1:

Korolkov 1972, after 4. Ka1

This will be a zugzwang position of the kind we are looking for if the same position recurs in the variations with white to move. That is what the echo filter looks for:

echo(source target){ 
 wtm  variation  

The echo(source target) sets the user variable source to the current position, which is our mainline black to move position, that is, the diagrammed position above.

The echo filter then runs through all the other positions in the study, setting target to each of them, and setting the current position to target. For each such position, the filter checks if the target is a position that is wtm variation that is if it is a white-to-move position in a variation. Since target equals the current position, a filter like wtm is true of the current position (i.e., is true) if it is true of target.

Now for the key part of the echo body, we compare the positions source and target:


The source&target here represents a positional &, a comparison of two positions, and represent the squares on which the two positions source and target are the same. The . on the right is simply the set of all 64 squares.

And indeed this filter will be true, and thus the entire echo filter will be true, when target is set to the position in the variation after 4...Ka8:

Korolkov 1972, after 4...Ka8

Note that by default the echo filter outputs several automatic comments.

The matching source position, here named source occurs after move 4.Ka1 in the mainline:

  4.Ka1! source <-- move 5(wtm)[47] CQL

The CQL at the end is the default comment output by CQL when the body of the CQL file matches a position. The word source is taken from the first argument, source, to the echo filter, and indicates that the echo body matched a position when the source equalled this value. The move 5(wtm)[47] represents the position using the standard textual representation of a position. Here, it means the position with positionid equal to 47, which occurs in a white to move position at move 5. At that position, a corresponding comment is seen:

4...Ka8! target <-- move 4(btm) id:47
The id:47 means that the positionid of that position (the position after 4...Ka8) is 47. The target means that this position matches a value of target in the echo filter. The word target is taken from the variable that is the second argument to echo, in echo(source target). The move 4(btm) indicates the corresponding source position, namely the btm mainline position after move 4. (It must be a mainline position because there is no bracketed number following it, unlike for the source position's comment.

One final comment is worth mentioning. The mainline position after 1...Rd4,

Korolkov 1972, after 1...d4
(found from CQL file: zugzwang1.cql)

is annotated with the comment LCA. This simply means that this position is the lca or "critical position". If white plays 2. d:c4! here, then white wins, reaching the source position above with black to move. But if white instead plays 2. a7+ then black avoids loss, as he can in some variation reach the target position above, with white to move.

A logical study by Salai

Now we look at a slightly more complex example. We are looking for games such that there isa mainline position source and a variation target which only differ in that white has an extra piece in the target. Normally, the extra piece would help white, but here, because it occurs only in the variation, we expect it to hurt white.

We also want the game to illustrate why the missing piece hurts white by a sequence of moves that fails only in the variation. What this means will be clearer a bit later.

The following study by Salai is an example:

Salai 2011
(found from CQL file: missingwhitepiece.cql)

In this position, the natural 1.Kd7? turns out to lose, after the sequence of moves given in the variation.

The winning move is 1.f4!. After 1...B:f4 only now does white play Kd7. So the natural question: why does inserting the moves 1.f4 B:f4 turn the drawing 1.Kd7 into the winning 2.Kd7?

The answer lies in the position near the end of the mainline:

Salai 2011, after 10...Kh5
(found from CQL file: missingwhitepiece.cql)

The above position is reached after 1.f4! B:f4 2. Kd7 .... 10...Kh5. White now wins after 11. f3!. But 11. f3! is possible only because 1.f4 vacated the f3 square.

If white had instead of 1. f4 played the immediate 1.Kd7 and tried the same plan, the following position would have been reached.

Salai 2011, after 9...Kh5 (variation)
(found from CQL file: missingwhitepiece.cql)

In this position, white cannot save a tempo with f3, since there is now a pawn on f3, so white only draws.

The CQL that finds this position (and positions like it) is in the file missingwhitepiece.cql.

It begins with some boilerplate

cql(result 1-0 variations matchstring "")
This limits the result to white wins, searches in variations, and inhibits printing of CQL at each match, which clutters up the output somewhat.

Now, we want include and echo filter. We want the echo filter to match when source equals the winning mainline position above after 10...Kh5, and target equals the drawing variation after 9...Kh5.

The mainline. The

  sort "number of identical consecutive moves"
   echo (source target){
indicates that the games are to be sorted by the value of the echo filter. Here, the body of the echo is a compound filter which ends with
consecutivemoves 15 100 (source target)

Now, let us assume in the following that the variable source has value equal to the mainline the position after 10...Kh5, and the variable target has value equal to the variation position after 9...Kh5.

Next, the remainder of the echo body just checks that source and target are the same except for some extra pieces in target, which is the current position inside the echo body.

As in the zugzwang example earlier, we first check that the sidetomove is the same in source and target:


Next, we set the variable mismatches to the set of squares in which the positions source and target differ, and check that this is nonempty:

mismatches= ~(source&target)

In this case, mismatches has the value f3, since that that is the only square on which source and target have different contents. In the source, the mainline, the square f3 is empty; but in the target the square f3 has a white pawn on it.

The next line actually checks that the only difference between source and target is that target has some extra white pieces:

 mismatches== A&source:_

Here, A is the set of squares on which there is a white piece in the current position, that is on target. source:_ is the set of empty squares in source. The & takes the intersection of these two sets. The only square in both sets is f3: there is a white piece on f3 in target, but f3 is empty in source.

Hence, the right hand side of the == above is simply f3, which is also the value of the left side, mismatches.

Finally, the consecutivemoves filter has value equal to the length of the longest sequence of consecutive moves between the LCA and source and the LCA and target. Here, for example, this length is 16: the 16 moves that are annotated source-move[1] through source-move[16] (or target-move[1] through target-move[16]). The LCA of source and target in this case is simply the initial position: white can choose to go into the mainline with 1.f4! or the variation with 1.Kd7?.

Therefore, the filter matches.