CQL by Example

This document (still under development as part of the Scid++ project) is a humble attempt at composing a tutorial introducing Costeff's and Stiller's Chess Query Language. CQL is a novel and powerful language, the invention of which has attracted little attention outside of the chess composition domain. The author of this tutorial believes that CQL's low profile is an enormous loss to the general chess-playing population. The first step in rectifying that loss was the integration of the CQL engine with the Scid vs PC chess database application. The next step is this tutorial.

The CQL reference includes a large number of example scripts. Many of those examples can be rather complex as they tend to be oriented toward compositional themes, and so we present here some [mostly] simpler examples that might be useful in searching a database of ordinary over-the-board games. Mastering the simple stuff first will prepare one for the more complex language constructs.

As the authors of CQL like to point out, CQL was developed specifically for searching for themes and patterns in endgame studies. This author would argue that the language is so powerful that it is more generally applicable to conventional games. We hope that the examples in this tutorial will go some distance in demonstrating that potential, searching for patterns and themes that a non-composer could appreciate. Eventually we intend to expand the tutorial to include the more daunting chess problem domain, but first things first.

Attempting to demonstrate a language as complex as CQL is a dubious affair. Any given solution for a stated objective will inevitably have a better solution, or at least an alternative solution and probably many alternatives. Some of the examples below will demonstrate multiple means toward achieving the same end, but we make no claim that we have stumbled upon the optimal.

The application of a query language to the game of chess should necessarily be as elegant as the game itself, and the authors of the Chess Query Language have shown an appreciation for that aspiration. Elegance and aspiration aside, the author of this tutorial has little more than a hammer in his CQL toolbelt and apologizes for the brute force approach to the application of the language.

A brief review of the language

This tutorial is no substitute for the reference documentation. We assume that the reader has gone through most if not all of the reference at least once, and has the reference in hand while working through the examples. The general concepts (not necessarily every last detail) introduced in that documentation are critical to an understanding of the material herein. This review will cover aspects of the language that are especially relevant to the tutorial.


The language defines a suite of filters and operators, each of which yields a result. That result either matches a position or it does not. For example, a set filter which yields a non-empty set matches a position.

Iterating over every position in the main line of the game (and in variations if so directed), the engine evaluates every filter in the body of the query to see if it matches the position. If every filter matches, then the position matches the query and, by extension, the game matches the query. If at any point a filter fails to match, evaluation is terminated for that position (this is a key point).

At any given point in iterating over the game tree, there is the notion of the current position. Some filters can look backward or forward in the game tree, but at the end of filter evaluation the current position is restored.

If one were to take an accounting of all the filters in the CQL lexicon, one would probably find that the majority operate on and return a set of squares. We dedicate one entire section of this tutorial to sets and set operations, and for good reason. Operations on sets are ubiquitous in CQL. They are essential. Nearly every example below employs at least one operation on sets.

The v5.2 reference has a page dedicated to sets, and though the language has changed with v6.0 that page is still worth a read. The v6 reference's readout on sets is dispersed, but we give a few links to get the reader started:

Also available on this site is a companion tutorial titled Understanding Sets in a CQL Universe, which will assist the uninitiated in visualizing many of the complex set operations of this tutorial.

A number of the set filters are flexible in the determination of which set the filter will yield. The move and pin filters return a set determined by whichever parameter appears first in a list of parameters. The attack filters (taken together) yield a set determined by the left argument to the filter. And while the xray filter has no such inherent flexibility, that flexibility may be realized by constructing an equivalent ray filter with a direction parameter and with arguments reversed in order.

While most filters are oriented toward matching a static position, CQL also defines a filter that will match a line of play — appropriately, the line filter. Also, the move filter with the previous parameter given will look back one move, allowing one to inspect the move that results in the current position. That can be especially useful in identifying the sqaure to which or from which a move was made leading to the current position and/or the piece making the move.

The find filter can look forward or backward through the game tree for matching positions. For example, one could look forward from a position to determine whether a particular passed pawn ever promotes. Or one could look backward to determine whether a king on a long walk had been drawn out by a sacrifice.

CQL also provides a number of constructs which will be familiar to the software developer, including a conditional and a looping construct and a facility for defining functions. With the square and piece filters one can iterate over a set of squares testing a position against the body of the filter. And new to version 6 of the language, the arbitrary assignment to a variable of the result of a filter or of an operation — a real game changer.


The utility of the result of any given language construct may manifest in a variety of forms. Standing on its own, for example, a set filter evaluated in the context of a particular position will yield a set which is either empty or non-empty.

A attacks k
If the set is empty, i.e., there is no white piece attacking the black king, the filter fails to match the position.

But one could also construct a relational expression with the filter as an operand, testing the cardinality of the resulting set against an upper or lower bound.

A attacks k > 1
In this case, if the attacks filter yields a set with more than one member, the expression matches the position.

Equivalently, one could assign the set given by the filter to a set variable for later use.

Attackers = A attacks k
Attackers > 1
One could make a similar observation with regard to numeric and position filters. Most filters yield a result which may stand on its own in matching a position, or may be used as an argument in a larger expression, or may be assigned to a variable. Context — and perhaps a matter of taste — will determine which is the most appropriate.


While looking over the examples below, one could be forgiven one's confusion over the distinction between a piece and a square. Hoping to keep the lying and misinformation to a minimum, we'll simply state that — for the most part — they are interchangeable and the CQL engine will translate between the two as a function of context. That is, indicating a piece may yield the square that is occupied by that piece. Conversely, indicating a square may yield the piece occupying the square.

For example, the nested filter construct piece Piece in A attackedby a square Square in Piece comment(Piece Square) has a piece filter iterating over a set of squares given by the attack filter, and yet it is not the square that is bound to the Piece variable but the piece occupying the square. Likewise, the Square variable is bound not to the piece but to the square occupied by Piece.

Query design strategies

For even moderately complex queries in the conventional domain, many of the objectives of the query may be vague and difficult to define. What does it mean for a piece to be "trapped"? How is a prophylactic move to be distinguished from a simple defensive move? What is mobility? Or a spatial advantage? We know it when we see it, but just try to define it.

The intent of a particular maneuver is difficult to account for without reading the mind of the player. Is the intent of a sacrifice that of a decoy, a deflection, a positional advantage, a temporal gain (in the case, say, of a sacrificial clearance), or none of the above? At what point does a king out for a stroll become a king hunt? What is the threshold? The answers to such questions must often be realized implicitly.

Many of the examples presented below — especially in the latter half of the section on conventional games — do not even make an attempt at precision or completeness or clairvoyance. Some of the examples stipulate virtually none of the constraints or objectives of the query explicitly, but rely on what we might refer to as emergent properties at the intersection of the constraints given. The degree to which one can achieve the desired result — without giving the criteria explicitly — is remarkable. And yet, the signal to noise ratio in the results of such a query can be extremely high.

When approaching the design of a complex CQL query, a common mistake made by many is to get bogged down in the detail. A lesson in evolutionary design from the open source software community applies equally well in this context. Begin with the obvious, evaluate the result, adjust and refine, repeat. In constructing a query for conventional chess, one will often find that the detail is irrelevant to the outcome. The minutiae melts away implicitly.

The Anderssen's mate example is a typical case where half the stated criteria are satisfied implicitly. That this might be the case is hardly intuitive, and does not become apparent until one begins inspecting the matching positions given by a few obvious piece designations. An explicit stipulation that the pawn must be guarded or that the flight must be obstructed is completely unnecessary.

The example demonstrating Interference by a hostile piece is more subtle in achieving its ends. How are we to determine that the interfering piece actually presents an imminent and compelling threat? How does one even define such a threat in terms which might be implemented economically? Instead, we infer that if the threat were not imminent and compelling, the target of the interference would simply move out of harm's way or find some avenue for counterplay. And so we eliminate, with a single filter, positions in which the target does just that.

The Clearance example (of a friendly piece) may be the most pointed demonstration of achieving a result obliquely. The stated objective of the query is to match a line of play in which a sacrifice with tempo clears a square of a friendly that can then be occupied in an attack on the king. But nowhere is a sacrifice (never mind with tempo) or an attack on the king — or even a check — explicity stipulated. In fact, even attempting to correlate the line of play given by the query with the stated objective is at best a dubious undertaking. And yet the matching lines of play are remarkably consistent with the objective.

In none of these examples did we set out to stipulate the conditions of the objective by design. Rather, we began with a couple of obvious constraints, observed the results, and incrementally applied further constraints. How did we come upon the solution to the determination that a threat is "compelling"? By observation. The most reliable indication that a threat is not compelling is that it allows counterplay. Such was the observation in game after game after game.


CQL allows for the expression of some very complex questions, but that complexity can come at the expense of considerable compute time. There are, however, some techniques that can be employed to significantly reduce the search time.

  • Since evaluation stops when any filter fails to match a position, the relative placement of filters within the body of a query can have a marked impact on search time. Place the simplest, least compute-intensive queries up front where they can eliminate a position from further consideration before suffering the penalty of the following filters.
  • Often one will find that a query as a whole is constructed from the perspective of one side or the other, but that the enforcing filter may be at some depth into the query. Stipulating e.g. wtm up front can save a great deal of compute time, even though it is redundant within the larger context.
  • Numerous constraints may be applied on a query to limit the number of positions considered. One might limit the range of ply considered, the total value of the pieces on the board, the number of pawns on the board or some other property that is consistent with the objective. Perhaps one is interested only in those positions which occur after both sides have castled or after both queens are off the board. There is almost always some constraint on the position that can be explicitly stipulated to reduce the number of positions subject to evaluation.

Examples for conventional games

Many of the examples in this section are inspired by Wikipedia's glossary of chess terms and index of mate patterns. A more detailed explanation of the themes and patterns demonstrated may be found on that site.

Some simple one-liners

We begin with a few simple queries which will convey a general impression of the language. But even a one-liner can produce some interesting results.

  • Answering check with mate
  • The simplest of queries, but it turns up some interesting results. Of necessity, the mating move must simultaneously cover the check. The query employs the line filter, which matches a line of play beginning with the current position.
  • cql() line --> check --> mate

  • The royal check
  • Essentially a cross-check where the king moves out of check to reveal a discovered check. Again we employ the line filter to make the match. We also introduce the flipcolor transform, widely used throughout the tutorial, to match positions from either the white or the black perspective.
  • cql() line --> flipcolor {check  move from k} --> check

  • Castling opposite sides
  • White castles king side before black castles queen side. The regular expression in line between the castling moves allows zero or more intervening moves (actually positions) of any kind. And the braces are necessary on the line filter's first constituent because it is a compound filter.
  • cql() line --> {wtm  move o-o} --> .* --> move o-o-o

  • Alekhine's gun (battery)
  • A form of battery in which a queen backs up two rooks on the same file, oriented toward the opponent's field. The ray filter finds patterns of pieces arranged along a line, either adjacent to one another or separated only by empty squares. The up parameter to the filter establishes the direction of the ray, the sense of which is reversed by the flipcolor transform when evaluated from black's perspective.
  • cql() flipcolor ray up (Q R R)

  • Double check with capture.
  • We're looking for a double check by bishop and knight. We also wish for the move that leads to the double check to be a capture.

    Note that there are a couple of constraints on the matching position that are not given explicitly. Of necessity, it is the knight that makes the move resulting in double check. The double check must be the consequence of a discovery and discovered check cannot be given by a knight. Secondly, the [BN] piece designator need not be any more restrictive since it is impossible for two bishops or two knights to give a double check. Implicit conditions are common when constructing CQL queries and will sometimes simplify the query, but at the cost of clarity.

    For example, the move previous filter is in reference to the move that results in the current position. As given in this example, that is, only stipulating that the move is a capture of any opposing piece, the query is not as clear as if it had been explicitly given that the move is by the knight. There is no harm in being explicit even when it is redundant. But neither is it necessary.

  • cql() flipcolor {[BN] attacks k == 2  move previous capture a}
    

  • A pawn forks both rooks
  • A pattern that is fairly common in over-the-board games. The query matches the pattern with a compound filter of piece designators, then flips and shifts that pattern vertically and horizontally across the board.
  • cql() flipcolor shift {ra8 rc8 Pb7}

  • A three-way knight fork
  • The knight is poised to do some serious damage. The fork may be of any combination of queens and rooks. We limit the search to positions in which the knight is not subsequently captured.

    The piece filter iterates over every white knight on the board, matching a position if the number of rooks and queens attacked by the knight is greater than two, and if the move from that position is not a capture of the knight.

    Note the absence of braces around the body of the piece filter. Normally, the braces would be required around a compound filter, with an implied logical and operation between filters. Here we give the and operation explicitly and can drop the braces.

  • cql() piece Knight in N  [qr] attackedby Knight > 2 and not move capture Knight
    

  • Rook pinned to queen
  • A rook is pinned against the queen by the opposing QB pair. Another ray filter example, this time with a direction parameter restricting the ray to diagonal lines. Without that qualification, the ray would also match the pieces arranged orthogonally even though there would be no pin of the rook by the bishop in that case.
  • cql() flipcolor ray diagonal (Q B r q)

  • A skewered king
  • A so-called absolute skewer. The xray filter is a generalized pin. The compound piece designators match a skewer by either a bishop or a rook. Note that in the case of attack style filters (unlike the ray filter), a direction parameter is not necessary as any given line piece can only attack along specific lines.
  • cql() flipcolor xray ([BR] k q)
  • cql() flipcolor pin from [BR] through k to q

  • Fianchettoed bishops
  • We're searching for variations on the theme of opposing bishops on the long diagonals. This example demonstrates the process of starting out with a general condition and then refining the query with additional criteria. That process is probably the most effective in constructing complex CQL queries — begin with the essence of the idea and then extend the query one little bit at a time.

    The basic query places opposing bishops on a long diagonal, explicitly giving the designators for one diagonal and then flipping about the vertical axis.

  • cql() flipvertical {Bb2 bg7}
  • We next extend the query to stipulate that one bishop attacks the other, thus implicitly removing any intervening pieces from between the bishops. One could explicitly achieve the same outcome with the between filter, but that expression would be somewhat more complicated.
  • cql() flipvertical {Bb2 attacks bg7}
  • Finally, we insist that the black bishop is pinned against the black king.
  • cql() flipvertical {pin from Bb2 through bg7 to k}

  • The unbalanced exchange
  • An exchange of a queen for some combination of pieces and pawns can lead to a very interesting game. The outcome will often depend on how well the pieces are coordinated and the resulting play can be quite instructive.

    The first query looks for positions with equal material valuation but with only one queen on the board. Here we give no constraint on the composition of the pieces exchanged.

  • cql() power A == power a and [Qq] == 1
    
  • However, it is very easy to add further criteria specifying the material makeup of the exchange for the side without a queen. For example, the following query matches a position where a queen has been exchanged for three minor pieces.
  • cql() power A == power a and [Qq] == 1 and abs(#[BN] - #[bn]) == 3
    

Sets and set operations

Many CQL filters operate on sets of squares and may yield a set as the result of the operation. Even the lowly piece designator denotes a set of squares. When strategizing about the best approach in constructing a query, it's important to condition one's mind to envision operations on sets. This is probably the single most crucial concept encountered in all of CQL.

When dealing with set filters, it's important to pay attention to the semantics of the filter. One of the most common head scratchers is the question of what is the difference between A attacks k and k attackedby A? The answer is that the two filters represent very different sets. The former represents the set (which may be empty) of all squares on which there is a white piece attacking the black king, and the latter represents a set of one square on which the black king resides if it is attacked by any white piece.

Other set filters allow great flexibility in determining the set represented by the filter. The move and pin filters returns sets that are determined by whichever is the first parameter given in a list of filter parameters. Such flexibility allows for a wide latitude in the construction of a query and is one of the many improvements in the language with the release of version 6.

The examples of this section demonstrate some simple but still useful operations on sets of squares. Nearly all of the examples in this tutorial will likewise include operations on sets. Mastering the practice will yield huge dividends, and first reviewing the companion tutorial on understanding sets might assist in that pursuit.

  • A pinned rook attacks the king
  • This example is from the v6 online reference and is an excellent demonstration of the intersection of two sets. From the reference:
    The left argument to the & filter is pin whose value is the set of squares on which there is a pinned piece. The right argument to the & filter is {R attacks k} , whose value is the set of squares on which there is a white rook that attacks the black king. The value of the & filter is the intersection of these two sets.
    And so we have the set theoretic intersection operation applied to two sets to achieve the desired result. Those two sets are generated by the set filters pin and attacks. The set filters operate on their own sets: the pin filter has a default arrangement of parameters and the piece designators given as arguments to the attacks filter each denote a set of squares. Such is the mind-set conducive to constructing a CQL query.

  • cql() pin & {R attacks k}

  • Pawns on the attack
  • We want to search for positions in which some minumum number of pieces is attacked by pawns of the opposing side. In this case, we're looking for pawn attacks against pieces that cannot fight back (rooks and knights), and we're interested in positions with a large number of such simultaneous attacks.

    We give three distinctive approaches to the construction of the query in order to reinforce the notion that there's always more than one way to solve the problem. The first query employs the obvious lone attack filter operating on the two sets of interest, and would be the more efficient of the three. The second query takes the intersection of the set of squares occupied by any rook or knight with the set of squares attacked by any opposing pawn.

    By contrast, the third query employs the not so obvious direction filters in determining the set of squares that are attacked by the pawns; i.e., one square up from each pawn and then on either side of each of those squares. Note that these examples utilize numerous unnecessary groupings to help make the syntax more readable.

  • cql() flipcolor {([rn] attackedby P) >= 3}
    
  • cql() flipcolor {([rn] & (. attackedby P)) >= 3}
    
  • cql() flipcolor {([rn] & (horizontal 1 (up 1 P))) >= 3}
    

  • Every white piece is attacked
  • ... by a black piece. This query is also from the online reference and demonstrates a filter that is new to CQLv6. The in filter operates on two sets and matches a position if the left argument is a subset of the right argument. Therefore, if the set of all white pieces on the board is a subset of the set of all squares attacked by a black piece, we have a match.
  • cql() A in {. attackedby a}

  • A passed pawn on the h-file
  • In short, a passed pawn at the intersection of all the squares of the h-file (given by the square designator h1-8). Version 6 of the CQL language adds filters for identifying sets of connected pawns, doubled pawns, isolated pawns and passed pawns. The second query below expands the passedpawns filter to its raw equivalent. A breakdown and visualization of a similar construction can be found in the companion tutorial on sets.
  • cql() passedpawns & h1-8
    
  • cql() flipcolor{P & ~down horizontal 0 1 p} & h1-8
    

  • King on the line
  • We're looking for positions in which all of the line pieces for one side have the opposing king on their line, and with no intervening friendly pawns. Again, we're working with the intersection of sets but with a special case in which all of the members of one set intersect with the other.

    The query employs the in filter, which is true (matches) if the set given by the left argument is a subset of the set given by the right argument. Each piece of each line piece type must intersect with its respective directional set (line) emanating from the opposing king. Note that it is not sufficient to stipulate [QRB] in anydirection k as that would allow, for example, a rook to lie on the king's diagonal and still qualify.

    The last line of the query employs the intersection of sets to ensure that there are no friendly pawns obstructing the line. The between filter operates on two sets and yields a set of all the squares between any square in the first set and any square in the second set. If there are no friendly pawns on any of those squares, we need not worry about friendly fire.

  • cql() flipcolor {
      [QRB] > 3 
      B in diagonal k
      R in orthogonal k
      Q in anydirection k
      not P & between([QRB] k)
    }
    

  • A blockaded IQP
  • A hypermodern theme of particular interest when a knight is the blockading piece. The pawn protects the blockading piece from some forms of attack, while the blockade prevents the advancement of the pawn.

    This example employs the intersection of sets on steroids. The isolatedpawns filter yields the set of all isolated pawns of either color, and we take the intersection of that set with the set of all white pawns on the board to give us the set of all isolated white pawns. We take the intersection of that set with the set of all squares on the d-file to give us the set of all isolated white pawns on the d-file. The up filter gives us the set of squares immediately in front of those pawns, and the intersection of that set with the set of all black knights gives us the set of all black knights blockading an isolated queen pawn. See the companion tutorial on sets for a visualization of this example.

  • cql()  flipcolor {n & (up 1 ((P & isolatedpawns) & d1-8))}
    

Mate patterns

The Chess Query Language is all about searching for patterns. This section focuses on queries for static positions representing mate patterns, some of which are common place and a few of which are rare but are of interest for their esthetic value.

Most mate patterns are easily laid out in terms of piece placement, but then any number of conditions - implicit or explicit - might apply to further qualify the position. Perhaps the mate must be a double check or each of the king's escape squares must be guarded by only one piece or perhaps all obstructing pieces must be friendly. CQL is particularly adept at ferreting out such positions.

  • Anastasia's mate
  • There are a variety of competing definitions for this mate pattern lurking about the nethersphere, some of them self-contradicting. We'll begin with a knight and rook team up to trap the opposing king between the side of the board on one side and a friendly pawn on the other. But we're interested in a very specific pattern that goes beyond that definition: 1) the king must be in check by the rook and by only the rook, and 2) the knight must be guarding exactly two empty squares against the king's escape and must be the only guard.

    The square filter iterates over a set of empty squares each of which is attacked by both the knight and the mated king. The knight is explicitly placed relative to the king such that those squares are on the rank adjacent to the king's rank. The cardinality of the set returned by the filter is given as exactly two. The body of the filter stipulates that only one white piece attacks the escape square bound to the variable Guarded, and we know that that piece must be the knight.

    Note that, in placing the king and pawn in their relative positions, we could as easily have shifted a pair of designators over the length of the ranks with a transform. Also worth noting is that there is no need to explicitly place the rook on the edge of the board since that is the only vantage from which a rook could attack the king.

  • // Anastasia's mate.
    // The rotate90 transform matches the pattern on all four edges.
    cql() mate flipcolor rotate90 {
      // Place the king and pawn in their relative positions.
      ka-h8  p & (down 1 k)
      // Establish that the king is attacked by a rook and by no other piece.
      R attacks k and A attacks k == 1
      // Only the knight guards the two empty escape squares.
      {square Guarded in (_ attackedby (N & down 3 k) & _ attackedby k)
          A attacks Guarded == 1} == 2
    }
    

  • Anderssen's mate
  • The king is mated on the back rank by a rook or a queen on the corner adjacent to the king. The mating piece is protected by a pawn. The pawn is protected by another piece. The lone flight square is either guarded or obstructed.

    The query is of a simple pattern given by a compound of piece designators, where the complement of the stated criteria are satisfied implicitly; i.e., the pawn must be protected and the escape square must be guarded or obstructed else the position is not mate.

  • cql() mate flipcolor flipvertical { kg8 [RQ]h8 Pg7 }
    

  • Arabian mate
  • The knight and the rook team up to trap the opposing king on a corner of the board. The rook must be adjacent to the king, guarding one of the flight squares. The knight protects the rook and guards the other flight square. To make this example more interesting, we'll add the condition that the mate is made possible by the deflection of an interposing piece.

    The line filter looks back two positions to determine whether the mated side had a piece interposed between the rook and the rook's target mating square, and whether that piece was deflected (presumably by the forced capture of a sacrificing piece). The last constituent of that filter asks the question: was there any opposing piece sitting between the mating rook and it's mating square, and did that piece move out of the line of attack with a capture? Note that the capture must have been made by the interposing piece else the mate would not have been possible on the next move.

  • // Arabian mate.
    cql() mate flipcolor flipvertical {
      // Place the pieces in their relative positions.
      kh8 R[h7,g8] Nf6
      // Both flight squares are empty and the king is checked by a lone piece.
      _ attackedby k == 2 and A attacks k == 1
      // Some opposing piece made a capture from a square in the mating rook's line,
      // making way for the mating move.
      line <-- Dest = R attacks k
           <-- Source = move from R
           <-- a & between(Source Dest) and move capture A
    }
    
  • This query only scored half a dozen hits out of a database of three million games, but those matching games had some interesting finishing combinations.

  • Bishop and knight mate
  • The king, bishop and knight join forces to drive the opposing king into a corner controlled by the bishop or to a square adjacent to the corner. The mate is arrived at with some difficulty and only with perfect play in the endgame, and is therefore somewhat rare. Mate may happen elsewhere on the edge of the board, or even on a corner not controlled by the bishop, but only with inaccurate play on the part of the defense. We'll limit our search to patterns of the mate that can be forced.

    The query is very simple, only explicitly giving the four pieces on the board and the limited placement of the mated king. The mate filter implicitly enforces all other criteria. As a final touch, we'll sort the matching games by the length of the mating maneuver once the board has been reduced to four pieces.

  • // Bishop and knight mate.
    // Flip the king to all four corners.
    cql() mate flipcolor flip {
      // Stipulate that the four pieces in question
      // are the only pieces on the board.
      [Aa] == 4  B == 1  N == 1
      // Place the mated king on a corner or adjacent square.
      k[h7-8,g8]
      // Sort by the length of the B&K mating maneuver and mark the starting position.
      sort "Maneuver length"
      distance(position 0:find [Aa] == 4 and comment("Start Position") currentposition)
    }
    
  • Note that the execution overhead of the find filter is only incurred if all other criteria of the query are satisfied.

  • Blackburne's mate
  • A knight and two bishops coordinate to mate the king in his castled position. The knight and the bishop on the long diagonal guard the king's flight squares while the other bishop gives mate from the h-file, protected by the knight.

    This mate pattern is so rare that we're going to construct a query that will also match games in which mate is imminently threatened at the terminal position. We'll consider games that have the result we expect while all the pieces are in position to conclude the mate on the opponent's resignation. Note that in order for the threatened move to be threatening mate, the king must be the only opposing piece defending against the move.

  • // Blackburne's mate.
    // Restrict consideration to a terminal position with the expected result.
    cql() terminal flipcolor { result 1-0 
      // Locate the castled king and rook and the cooperating knight.
      kg8 rf8 Ng5
      // Place one bishop on the long diagonal with a clear line to h8.
      {B & diagonal 0 5 a1} attacks h8
      // The other bishop is either on or threatening a move to h7 with mate.
      Bh7 or B attacks h7
      // Only the king defends h7.
      a attacks h7 == 1
    }
    

  • Boden's mate
  • Similar to the Blackburne pattern but with the king castled queen-side, this pattern is characterized by both bishops attacking the king along orthogonal diagonals from a distance (hence, no need for the knight), with the complement of the king's flight squares occupied by friendly obstructing pieces. While the typical pattern manifests after a sacrifice opens up the pawn structure, we'll also give a query covering a more general pattern with the king placed anywhere along the back rank.

    The first query matches a position in which the b-pawn is diverted from its defensive duties with a capture of a [presumably] sacrificing piece. The diversion opens up a window for the mate.

  • // Boden's mate.
    cql() mate flipcolor {
      // Locate the castled king and rook and the obstructing pieces.
      kc8 rd8  ad7-8 == 2
      // Place the mating bishop.
      Ba6 attacks k
      // Place the other bishop on the orthogonal diagonal with a clear line to b8.
      {B & diagonal 0 4 h2} attacks _b8
      // The b-pawn has just abandoned the defense of the king.
      line <-- . <-- move from pb7 capture A previous
    }
    

  • The general case, in which the king may occupy any square along the back rank, is a bit more involved. Because the king may occupy any square along the rank, the placement of the bishops and the orientation of their attacking lines must be relative to the king's location. As a consequence, there is no need to explicitly flip and shift a piece configuration along the back rank.

    Once we've established that both bishops are attacking the king's field, we ensure that their attack orientation is orthogonal by stipulating that the attack field with diagonal orientation is orthogonally adjacent to the attack field of the other bishop within the king's field.

  • // Boden's mate.
    cql() mate flipcolor {
      // Place the king anywhere along the back rank.
      ka-h8
      // Assign sets of empty squares in king's field attacked by the bishops.
      BD = {{diagonal 1 k} & _} attackedby B
      BO = {{orthogonal 1 k} & _} attackedby B
      // Stipulate cardinality of sets and orientation of lines of attack.
      BD == 1  BO == 2  (orthogonal 1 BD) & BO == 2
      // Only the bishops are guarding or attacking the king or his field.
      {_ attackedby k | k} attackedby [KQRNP] == 0
    }
    
  • Note that, rather than explicitly stipulate the presence of the obstructing pieces, we ensure that only the bishops attack the king or the king's field. Implicitly, some piece must be obstructing the flight squares and, frankly, what are the odds that they would be hostile.

  • Box mate
  • The mated king is on the edge of the board, the mating king is in [effective] opposition, and the rook delivers mate from any square along the edge. It's not so much the mate pattern that we're interested in as the mating technique that leads to the pattern. The mating side confines the opposing king to a shrinking rectangle to force the king to the edge of the board, boxing in the king. Once the kings are in opposition, the rook delivers the mate.

    To make things more interesting, we'll allow pawns on the board as potential obstacles or spoilers. Pawns of the mated side must all eventually be blocked in order to force the opposition. The opposition is implicit and need not be explicitly stipulated. The query also ensures that the king's field is completely empty, satisfying a condition of the pattern.

  • // Box mate.
    // Rotate the pattern to all edges of the board.
    cql() mate flipcolor rotate90 {
      // Place the king on the edge and attacked by the rook.
      ka-h8   R attacks k
      // Only the one rook and pawns allowed in the position.
      [QRBNqrbn] == 1
      // No pawns obstruct or guard against the king's escape.
      not ([Pp] attackedby k or P attacks (. attackedby k))
      // Sort by the length of the mating maneuver and mark the starting position.
      sort "Maneuver length"
      distance(position 0:find [QRBNqrbn] == 1 and comment("Start Position") currentposition)
    }
    

  • Cozio's mate
  • The king is mated by a queen that occupies a square adjacent to the kine on a diagonal. The queen guards all of the king's flights along three lines, the complement of the king's field being obstructed by friendly pieces.

    The query gives the relative positions of the king, queen and obstructing pieces. There is no need to explicitly stipulate that the queen is guarded, since the position would otherwise not be mate. It is necessary to ensure that all of the squares in the king's field that lie on the three lines projecting from the queen are empty, else the pattern fails. This pattern can be shifted and flipped to match all possible orientations.

  • // Cozio's mate.
    // Shift and flip the pattern across the board.
    cql() mate flipcolor shift flip {
      // Establish the pattern with two friendly obstructing pieces.
      Qh2 kg3 [a]f3 [a]g4
      // All other flight squares are empty and only the queen prevents flight.
      _ attackedby k == 5 and A attacks (_ attackedby k) == 1
    }
    

  • Damiano's mate
  • The castled king is mated by a queen and supporting pawn. Because this pattern very rarely goes to mate, we give the expected result at the terminal position and then place the queen on the h-file either on h7 or with no intervening pieces on her line of attack. A common mating pattern has a rook sac on h8 leading to the mate. The query matches a number of would-be Damiano's mates but for the early resignation.
  • // Damiano's mate.
    // Mate or resignation with imminent mate.
    cql() terminal flipcolor { result 1-0 
      // Establish the basic pattern.
      kg-h8 rf8 pg7 Pg6
      // Queen mates on h7 or threatens mate.
      Q attacks h8
      // A rook sacrifice at h8 preps mate with tempo.
      line <-- .{1 6} <-- move from k capture Rh8
    }
    

  • Double bishop mate
  • Unlike the Boden's mate, these bishops attack the king along adjacent diagonals. The bishops are the only pieces participating in the mate, with the mated king in a corner and with his escape obstructed by a friendly pawn.

    As was the case with Boden's mate, the matching query has the bishops attacking empty squares in the king's field. There is no need to explicitly stipulate that the bishops attack on parallel diagonals as the king is in a corner and there is a bishop attacking each of the two empty squares in the king's field. That the position is mate guarantees the parallel orientation of the lines of attack.

  • // Double bishop mate.
    cql() mate flipcolor flipvertical {
      // Place the king in a corner with a friendly obstructing pawn.
      kh8 ph7
      // Both bishops (and only the bishops) participate in the mate.
      B attacks _g7-8 == 2 and A attacks g7-8 == 2
    }
    

  • Epaulette mate
  • This mate occurs when the king is sandwiched between two rooks (the epaulettes) on the back rank with the queen delivering mate along the same file as the king. With this query we'll also allow for a double epaulette, with two pawns standing in front of the rooks and obstructing the king's escape.

    We're interested in finding the purest pattern of an epaulette, with the following constraints:

    • Only the queen participates in the mate.
    • Only the rooks (and the pawns in the case of the double) are obstructing the king's escape.

    Note that nowhere do we explicitly stipulate that the queen guards all of the king's flight squares. Rather, we stipulate that only the queen guards any of the flights and that the position is mate. Thus, the queen implicitly must be guarding all of the flights.

  • // Epaulette mate.
    // Shift the pattern along the back rank.
    cql() mate flipcolor shifthorizontal {
      // Locate the king and his epaulettes.
      ke8 rd8 rf8
      // King's field on adjacent rank are all empty or hold a double epaulette.
      _ attackedby k == 3  or  p attackedby k == 2
      // The queen (and only the queen) checks.
      Qe1-6 attacks k  and  A attacks k == 1
      // Only the queen guards any of the flight squares.
      A attacks (_ attackedby k) == 1
    }
    

  • Greco's mate
  • The king is mated along an edge-file of the board in or adjacent to the corner. A friendly pawn obstructs one avenue of escape, a hostile bishop guards the other(s), and the mate is delivered by a queen or rook along the edge. There are many different ways to arrive at a Greco but we'll first give a query that matches the basic pattern.
  • // Greco's mate - basic pattern.
    cql()  mate flipcolor flipvertical {
      // Place the king and friendly obstructing pawn.
      kh7-8  pg7
      // The bishop guards the empty flight square on the back rank.
      B attacks _g8
      // A queen or rook on the h-file effects the mate.
      [QR]h1-5 attacks k
      // If the king is on h7, the g6 flight is guarded by the Q or B.
      if k attacks _g6 then [QB] attacks g6
    }
    

  • The Greco often involves a sacrifice on h7 or h8 in order to open up the h-file or to draw the king away from g6 or f7. The line filter looks back four positions for a capture by the king drawing him away from one of those squares.
  • // Greco's mate - with sacrifice.
    cql()  mate flipcolor flipvertical {
      // Place the king and friendly obstructing pawn.
      kh7-8  pg7
      // The bishop guards the empty flight square on the back rank.
      B attacks _g8
      // A queen or rook on the h-file effects the mate.
      [QR]h1-5 attacks k
      // If the king is on h7, the g6 flight is guarded by the Q or B.
      if k attacks _g6 then [QB] attacks g6
      // King is deflected away from defense or flight.
      line <-- .{1 4} <-- move from k capture h7-8
    }
    

  • Another common theme with the Greco is mate by discovery as the bishop moves into position at f7. Occasionally, that discovered mate immediately follows the deflection of the king by sacrifice.
  • // Greco's mate - by discovery.
    cql()  mate flipcolor flipvertical {
      // Place the king and friendly obstructing pawn.
      kh7-8  pg7
      // The bishop guards the empty flight square on the back rank.
      B attacks _g8
      // A queen or rook on the h-file effects the mate.
      [QR]h1-5 attacks k
      // If the king is on h7, the g6 flight is guarded by the Q or B.
      if k attacks _g6 then [QB] attacks g6
      // King is deflected away from defense or flight.
      line <-- .{1 4} <-- move from k capture h7-8
      // Mate is by discovery as the bishop moves to f7.
      move previous from B to f7
    }
    

  • Hook mate
  • There are a number of definitions for this pattern with a variety of constraints on the piece types and configuration. The least constricting definition might be the more interesting, any notion of a hook being more or less irrelevant; i.e., that the rook sealing the mate is protected by a knight that is in turn protected by a pawn. Implicit in this definition is that it actually matters that the knight and rook are protected, lest the king capture one of them, and so we should assume that both pieces lie in the king's field.

    Looking first at the classic pattern (the so-called hook), one is tempted to simply shift and flip a collection of piece designators about the board.

  • cql() mate flipcolor flipvertical shift { ke7 af7 Re8 Nf6 Pe5 }
    
  • That certainly works up to a point, but does not address the other conditions on the board affecting the pattern. For example, are there other pieces either guarding or obstructing the king's flight? Or are there other pieces also defending the pieces in the chain?

    Taking a cue from our more liberal definition (protected by protected by), we'll employ a chain of attacks to lay out a very general pattern as a foundation for further experimentation. Note that we no longer need to shift and flip the pattern about the board.

  • cql() mate flipcolor { (R attackedby (N attackedby P)) attacks k }
    
  • That query draws in a large number of hits not the least bit related to a hook, but we can incrementally lay on further constraints which ultimately gets us to the purest of patterns.
  • // Hook mate.
    cql() mate flipcolor {
      // Set up the attack chain.
      (R attackedby (N attackedby P)) attacks k
      // The king is adjacent to both the rook and the knight.
      [RN] attackedby k == 2
      // Only the three pieces in the chain attack the king or his field.
      A attacks (. attackedby k | k) == 3
      // The king has five empty squares in his field.
      _ attackedby k == 5
    }
    
  • Note the interaction between the constraints. We stipulate that the rook and knight are both in the king's field. We also limit the number of pieces guarding the king's field to those in the pattern. Combining those constraints, we limit protection of the pieces in the pattern to the pieces in the chain.

    The last of the filters enforces the archetype by limiting the number of friendly obstructing pieces, which in turn forces the pawn in the chain into the hook formation guarding the odd flight square. All of the constraints of the query are relative to the king, which may occupy any square on the board away from an edge.


  • King and two bishops mate
  • Two bishops and a king can force mate of a lone king so long as the bishops are on opposite colored squares. The query simply accounts for the pieces. The colored square qualification is not explicitly given since it is implicitly determined by the mate filter. We sort the matching games by the length of the mating maneuver.
  • // King and two bishops mate.
    cql() mate flipcolor {
      // Limit the pieces on the board to kings and bishops.
      [Aa] == 4 B == 2
      // Sort by the length of the mating maneuver and mark the starting position.
      sort "Maneuver length"
      distance(position 0:find [Aa] == 4 and comment("Start Position") currentposition)
    }
    

  • King and two knights mate
  • The king with two knights cannot force a mate against a lone king. However, a forced mate is possible if the lesser side has a pawn or two. A search of a large database of GM games can turn up some instructive struggles (and some very long games) as the mating GM tries to find the right pattern to get the king into the corner without stalemate.

    The query only requires a determination of the pieces that are on the board, including any number of pawns on the mated side. We sort the matching games by the length of the mating maneuver and mark the beginning of the maneuver.

  • // King and two knights mate.
    cql() mate flipcolor {
      // Limit the pieces on the board to kings and knights and
      // any number of pawns on the mated side.
      [Aa]&~p == 4  N == 2
      // Sort by the length of the mating maneuver and mark the starting position.
      sort "Maneuver length"
      distance(position 0:find [Aa]&~p == 4 and comment("Start Position") currentposition)
    }
    

  • Mayet's mate
  • This mate takes place on the back rank by a rook protected by a bishop along the long diagonal, with an assist by an obstructing pawn. The mate is often set up by a sacrifice either clearing the h-file or forcing the capture by a defending piece.
  • // Mayet's mate.
    cql() mate flipcolor {
      // Locate the key pieces.
      kg8 pf7  B attacks Rh8
      // A capture of the [likely] sacrificing piece.
      line <-- .{1 4} <-- move capture Ah7-8
    }
    

  • Morphy's/Pillsbury's mate
  • Sligtly different patterns, same query. The bishop and rook join forces to mate the king in or adjacent to the corner. One mates while the other guards the flight. The pattern has the rook on the g-file, the bishop on the long diagonal and a pawn obstructing the king on the h-file. The attack filters ensure that it is either the rook or the bishop delivering mate and that they are working in concert.
  • cql() mate flipcolor { ph7 kg-h8  Rg1-8 attacks g8  B attacks h8 }
    

  • Opera mate
  • A mate pattern named after the famous Morphy game with the Duke at the Paris opera. The mate takes place on the back rank and is realized with the aid of an obstructing piece friendly to the mated king.

    We give the piece designators for those two pieces and then we align the partnering bishop by stipulating that it simultaneously attacks both the mating rook and the king's escape. We also stipulate that the bishop is the only piece guarding the escape or defending the rook. The pattern is flipped and shifted across the back rank.

  • // Opera mate.
    // Flip and shift along the back rank.
    cql() mate flipcolor flipvertical shifthorizontal {
      // Locate the king and obstructing piece.
      ke8 [a]f7
      // The bishop and only the bishop protects the rook.
      B attacks Rd8 and A attacks d8 == 1
      // The bishop and only the bishop guards the flight.
      B attacks _e7 and A attacks e7 == 1
    }
    

  • Smothered mate
  • A smothered mate is a simple pattern where the king is completely surrounded by friendly pieces, unable to escape a knight check. It is most common with the king on a corner square, but can technically occur anyplace on the board.

    The query is a one liner checking that the position is a mate and that the king has no hostile piece on any adjacent square.

  • cql() mate flipcolor { btm  [_A] attackedby k == 0 }
    

  • Suffocation mate
  • Suffocation is similar to being smothered but the mate is carried out by a knight and a bishop. The knight mates while the king is confined by a combination of his own obstructing pieces and the bishop guarding his escape.

    Note that the last line of the query has some bishop guarding at least one of the squares in the kings escape field. Normally that would be insufficient, but as there is only one piece guarding the field and some piece must be guarding all of the field, that piece must be the bishop.

  • // Suffocation mate.
    cql() mate flipcolor {
      // The night and only the knight checks the king.
      N attacks k and A attacks k == 1
      // There are no hostile pieces in the king's field.
      A attackedby k == 0
      // There is at least one flight square...
      Escape =? _ attackedby k
      // ...and the bishop and only the bishop guards the flights.
      B attacks Escape and A attacks Escape == 1
    }
    

  • Swine's mate
  • The term swine refers to doubled rooks on their 7th rank. A swine's mate takes place on the back rank on the corner and the square adjacent to the corner, with the rooks teaming up in a series of checks that ultimately leave the king with no escape. The mate requires the assistance of an obstructing rook on the back rank.

    It's easy enough to give the final pattern as a compound of piece designators, but it is also necessary to ensure that the mating rooks are, indeed, swine. The line filter serves that very purpose. Note that the 7th rank pawns may be present or not.

    The query does not explicitly say anything about an obstructing piece, but that piece must be in place and it must be a rook, else we would not have mate.

  • // Swine's mate.
    cql() mate flipcolor flipvertical {
      // Place the rooks in the pattern.
      Rg-h7 == 2
      // Only the rooks participate in the mate.
      A attacks (_ attackedby k | k) == 2
      // The mating sequence ensures that the rooks are swine.
      line <-- (kg-h8 <-- move from Ra-h7 to g-h7) {2 3}
    }
    

Tactical themes

We move now from the static to the dynamic where it is a sequence of maneuvers that determines the pattern of interest. Most of the examples of this section employ the line and move filters in establishing that pattern. It's important to remember that with the line filter we're actually sequencing through a series of positions, but it is the move that transitions us through those positions.

A tactic is a move or sequence of moves that present a threat for which the opponent has no satisfactory answer. Generally, the key is that the move(s) present multiple threats, only one of which can be dealt with in due course. Forks, discovered attacks, pins and skewers are the obvious candidates. Removing of the guard covers a broad spectrum of threats. And then there are the tactical maneuvers that may not lead to short term material gain but to a blistering attack or to a subtle but decisive positional advantage.

Some of the simpler tactics have already been covered in earlier examples, but below are a few with a slightly more complicated theme or with numerous variations on a theme. In some cases we'll even endeavor to ascertain the immediate motive for the tactic.

  • The sacrifice
  • A move or capture resulting in loss of material with compensation in the form of an advantage in space or development, or with an imminent attack in the works.

    Of particular interest is the exchange sac, which may have been Petrosian's favorite flavor. In his hands it became a tactical device with deep positional implications. The exchange contrasts with other sacrifices in that during the early middle game the board is sufficiently congested that the rook is not as relatively effective as an active knight or a "good" bishop. From a Petrosian-esque perspective, pieces are assigned value as much for their long term strategic relevance as for their material significance.

    What we are not interested in is the so-called sham sacrifice, where the material is quickly recovered or the opposing king is quickly forced to mate. We'll first show how to avoid matching such positions, and then we'll look at queries that match an exchange that either improves a pawn structure or degrades a pawn structure. We'll also look at matching exchanges that are made with a motive of increasing the effectiveness (value) of some other piece or pieces.

    Before we dive into the complexities of the line-oriented queries, we'll give a simple example matching the capture-recapture pattern with a couple of additional constraints that will filter out some unwanted noise. The previous parameter of the move filter allows us to look back to the move that gives us the current position. In the query below, that would be the rook move capturing a minor piece. We capture the identification of that rook - with a piece assignment - for later use because we want to avoid matching captures or attacks on other rooks that might be on the board.

    The query is restricted to those positions in which the sacrificing side does not already have a material advantage. We further limit the query to only match games with a positive result for the side that is the exchange down, thinking that this ensures that the exchange is sound. The query is crude but relatively effective given its simplicity.

  • // The exchange sac - basic pattern.
    cql()  flipcolor { result 1-0
      // The presumed sacrifice on the move leading to the current position.
      // The assignment identifies the rook that occupies the square
      // on which the capture was made.
      piece Rook = move to [bn] from R previous
      // The sacrificing side did not have a material advantage.
      power A <= power a + 3
      // The rook is not protected.
      not A attacks Rook
      // The rook is recaptured with the next move.
      move from [a] capture Rook
    }
    
  • It's worth pointing out a fine subtlety of the piece assignment in the query above. While the set filter values of the various move filter parameters are evaluated in the context of the previous position, piece identification is evaluated in the context of the current position. Thus, while the assignment as given above does work as intended, the assignment piece Rook = move from R capture [bn] previous does not work. Quoting Lewis Stiller:
    Yes, the interaction between "piece" and "move previous" can seem counterintuitive.

    Suppose White moves 10 Ra3-a5, moving a rook from a3 to a5. Suppose we call the position before White's 10th move, X. Let us call the position after white's 10th move, Y.

    Now suppose the current position is X, the position before White moves the rook.

    Then
    move from R
    
    has the value "a3".

    Thus,
    piece z = move from R
    
    would be the same as
    piece z = a3
    
    which would set the piece variable z to the rook on a3.

    Now suppose the current position is Y.

    Then as before,
    move from R previous
    
    would have a value of a3.

    However, since the current position is Y,
    piece z = a3
    
    would not match, as there is no piece on a3 in Y. That piece just moved, it's now on a5.

    Thus,
    piece z = move from R previous
    
    has the same effect as
    piece z = a3
    
    which doesn't match.

  • In order to construct a more finely tuned query than that given above, we turn to the line filter with its greater inherent flexibility. The following query demonstrates one method of filtering out a sham sacrifice where, ultimately, there is no sacrifice at all. The query filters out positions in which a before and after measure of the relative material count for each side exposes the sham.

    We look out one to four arbitrary moves from the recapture to see if the "sacrificing" side has recovered the material. Note that we stipulate that the material loss must persist for at least two consecutive positions to account for oscillation.

  • // The exchange sac - filter out shams.
    cql()  flipcolor { result 1-0
      // The sacrificing side does not have a material advantage.
      power A <= power a
      // The sac is not a sham... the material loss persists.
      line --> {piece Rook = move from R capture [bn]
                PowerDiff = power A - power a}
           --> move from [a] capture Rook
           --> .{1 4}
           --> {power A - power a < PowerDiff}{2}
    }
    
  • Remember that the wildcard constituent of the line filter is greedy. That is, even though the following constituent might match for the first iteration of the range, the ultimate match might run all the way out to the maximum extent of the range. It's also important to understand that with the range set four positions out, the following constituent might succeed, then fail, then succeed again. The point at which one is satisfied that the sac is not a sham is somewhat arbitrary.

  • Likewise, we might wish to filter out games in which the exchange leads to a quick mate or resignation. The wildcard constituent of the line filter ensures that the sac is at least three moves removed from that result.

  • // The exchange sac - filter out shams.
    cql()  flipcolor { result 1-0
      // The sacrificing side does not have a material advantage.
      power A <= power a
      // The sac is not a sham... the end is not imminent.
      line --> piece Rook = move from R capture [bn]
           --> move from [a] capture Rook
           --> .{3}
           --> not terminal
    }
    

  • The exchange is often used to disrupt the opponent's pawn structure, compensating for the material loss by creating, for example, isolated doubled pawns. Without assigning motive to the sac, the last constituent of the line filter places the recapturing pawn at the intersection of all doubled and isolated pawns.
  • // The exchange sac - disrupting the pawn structure.
    cql()  flipcolor { result 1-0
      // The sacrificing side does not have a material advantage.
      power A <= power a
      // The rook is recaptured by a pawn, doubling and isolating.
      line --> piece Rook = move from R capture [bn]
           --> piece Pawn = move from [p] capture Rook
           --> doubledpawns & isolatedpawns & Pawn
    }
    

  • When the exchange occurs near the endgame, it is often to create and promote a passed pawn. The following query matches a position where the recapturing pawn is drawn away from a file to allow a hostile to pass. The passed pawn must have been on an adjacent file prior to the recapture, but not on the same file as the sac'd rook. The find filter will match any position in which the passed pawn later promotes. Note that this query will not match doubled passed pawns because the piece assignment fails if the cardinality of the assigned set is greater than one and because, well, we're lazy.
  • // The exchange sac - promote a passed pawn.
    cql()  flipcolor { result 1-0
      // The rook is recaptured by a pawn, making way for promotion.
      line --> piece Rook = move from R capture [bn]
           --> {piece Pawn = move from [p] capture Rook
               // The [potentially] passed pawn must be on a file adjacent to
               // the capturing pawn and opposite the captured rook.
               piece PassedPawn = (down horizontal 1 Pawn) & P & ~vertical Rook}
           --> connectedpawns & passedpawns & PassedPawn
      // Determine whether the passed pawn promotes.
      find move from PassedPawn promote A previous
           and comment("Promotion at " PassedPawn)
    }
    

  • An exchange intended to expose the opposing king is a fairly common maneuver. Here we stipulate that the king must be on the back rank and that the recapturing pawn is directly in front of the king. The last constituent ensures that the king's file is completely cleared of friendly pawns.
  • // The exchange sac - expose the opposing king.
    cql()  flipcolor { result 1-0
      // Pry open the file in front of the king.
      line --> piece Rook = move from R capture [bn]
           --> move from (p & down 1 (k & a-h8)) capture Rook
           --> not p & down k
    }
    

  • Now we fall into the realm of the speculative sacrifice. The motivation for a sac could derive from a desire to weaken the opponent's color complex or to strengthen one's own. Elimination of a hostile bishop that controls the same diagonals as our own bishop might be adequate compensation for the exchange. Note that the query takes the queens off the board.
  • // The exchange sac - eliminate a same-colored bishop.
    cql()  flipcolor { result 1-0
      // The sacrificing side does not have a material advantage.
      power A <= power a
      // Limit the pieces on the board.
      not [Qq]  [Bb] == 2
      // The captured bishop controls the same diagonals as the friendly.
      line --> {piece Rook = move from R capture b
                piece Bishop = move to b from R
               (light Bishop and light B) or (dark Bishop and dark B)}
           --> move from [a] capture Rook
    }
    

  • Another possible motive for the exchange might be to acquire the bishop pair in an open position. Note that a cheap but fairly reliable criteria for opening up the position is to put an upper bound on the number of pawns on the board.
  • // The exchange sac - acquire the bishop pair.
    cql()  flipcolor { result 1-0
      // The sacrificing side does not have a material advantage.
      power A <= power a
      // Limit the pieces on the board.
      not [Qq]  [Pp] <= 8
      // The exchange leaves the down side with the two bishops.
      line --> piece Rook = move from R capture b
           --> {move from [a] capture Rook   B == 2}
    }
    

  • We should not neglect the possibility that the exchange might be offered rather than initiated. This class of exchange is often intended either to create a passed pawn or to clear out the piece guarding the sac square, making way for an attack that follows immediately from that square.
  • // The exchange sac - offered.
    cql()  flipcolor { result 1-0
      // The sacrificing side does not have a material advantage.
      power A <= power a
      // Offer the exchange.
      line --> piece Rook = move from R to _
           --> piece Minor = move from [bn] capture Rook
           --> move from A capture Minor
    }
    

  • The block
  • A block is a defensive tactic in which a piece interposes between a hostile attacking piece and the piece being attacked. The query below matches the block of an attack on a queen by a rook or bishop, in which the opposing king is left in check either by the blocking piece or by discovery.

    The query is something of a shortcut, in that there could be a double attack on the queen or, for that matter, there could be multiple queens on the board. But, in practice, the query is sufficient for demonstration purposes.

  • // The block.
    cql()  check  flipcolor {
      // The move previous filter gives us check in the current position,
      // as an alternative to employing the line filter.
      move from [RBNP] to between([br] attacks Q  Q attackedby [br]) previous
    }
    

  • Interference
  • Sacrificially interposing a piece between an attacked piece and its defender, or forcing the opponent to interpose one of his own pieces. The interposition disrupts the cooperation between pieces and might even interfere with a pending attack. The defended object is most often more valuable than the interposing piece. The interposing piece must either pose or introduce a threat (directly or indirectly) or it must interrupt two lines simultaneously such that capturing the piece will necessarily break one of the lines of defense. The latter case will be dealt with in the section below with examples relevant to the chess problem domain.

    The following query matches a position in which the opposing king is forced into interfering between two among his forces, leaving one of them en prise. The line filter comprises two compound constituents. The first ensures that the defender actually does guard the target of the interference, and that the king moves between them. The second constituent stipulates that there is no other piece defending the target of the interference, and that the target is captured on the next move.

  • // Interference - by a friendly.
    cql() check flipcolor
    // Iterate over potential targets of the interference.
    piece Target in [qr]
      // Iterate over potential defenders of the targets.
      piece Defender in [rb] {
        line --> // The defender defends the target and the king moves between them.
                 {Defender attacks Target
                 move from k to between(Defender Target)}
             --> // The target is defenseless and is captured on the next move.
                 not a attacks Target and move capture Target
      }
    

  • The next demonstration of interference is considerably more complicated. In this case the interfering piece is not a friendly, and we must take into consideration and eliminate instances of counterplay that represent unwanted noise in the pattern. The line filter employed is constructed of four constituents laying down the necessary constraints in a sequence of positions.

    The first constituent is as in the query above, plus the stipulation that the [sacrificial] interposing piece must be of less value than the target of the interference. Else what is to be gained by the sacrifice?

    The second constituent stipulates, in order, that the target is attacked by some piece other than the interposing piece, that the target is [implicitly] not able to move out of harms way, and that the interposing piece has indeed established a threat. The main point of the second constituent is to ensure that the interfering piece introduces a threat that cannot be ignored. That is, certain avenues of counterplay are filtered out.

    The third constituent stipulates that, once the threat has been dealt with, the target is yet undefended and is captured on the next move. A recapture scenario has been eliminated.

    The last constituent filters out any line of play in which: 1) the interposing piece is captured by piece X (this will have happened as the move from the second position), 2) the target of the interference is captured by the king (move from k previous), and 3) piece X moves out of the interference lane with discovered check (move from Line). Such a sequence is commonplace and represents a faux interference pattern. Note that it is possible that piece X moved into the line of interference without capture, but it is extremely unlikely given the complement of criteria stipulated throughout the query.

  • // Interference - by a hostile.
    cql() flipcolor
    // Iterate over potential targets of the interference.
    piece Target in [qr]
      // Iterate over potential defenders of the targets.
      piece Defender in [rb] {
        Line = between(Defender Target)
        line --> // The defender defends the target and the value of the 
                 // interposing piece is less than the value of the target.
                 {Defender attacks Target
                 piece Interposer = move from [RBN] to Line
                 power Interposer < power Target}
             --> // Some piece other than the interposing piece attacks the target,
                 // the interposing piece introduces a threat leaving the target
                 // unable to flee.
                 {(A & ~Interposer) attacks Target
                 not move from Target
                 check or Interposer attacks ([qr] & ~Target)}
             --> // The target is undefended and is captured on the next move.
                 not a attacks Target and move capture Target
             --> // Eliminate lines where the target is captured by the king
                 // which is then left in check by discovery from the defending piece.
                 not (move from K previous and move from Line)
      }
    
  • To better grasp the nature of the noise eliminated by any of the filters in the query above, it can be useful to delete the filter and examine the matched positions that result. From Scid, execute the query sans filter, then execute the query with the filter on the games matched by that query, then negate the game filter to expose the games that are eliminated by the filter in question.

    Eliminating noise can be an effective way of arriving at a result implicitly where it may be very difficult to stipulate the criteria explicitly. For example, not move from Target reinforces an expectation - that the threat posed by the interfering piece is compelling - by eliminating counterplay from the target of the interference. Remove that one filter and the query will match many lines of play that hardly qualify as a legitimate interference pattern.


  • Clearance
  • There are two flavors of clearance that are of interest. The first is a sacrifice or a deflection that clears a line of attack of a hostile piece, making way for an attack on the opposing king. The second is a sacrifice with tempo that clears a square of a friendly that can then be occupied in an attack on the king.

    The following query matches a line of play in which a line of attack is cleared of a hostile piece. The line is cleared by a sacrifice and then immediately used for an attack.

  • // Clearance - of a hostile piece.
    cql() flipcolor {
      line --> // The capture of the sacrificing piece.
               move capture (move to _ from [QRBN] previous)
           --> // The square just cleared is in the line of the next move.
               move from . previous & between(move from . move to .)
           --> // That move results in an attack on the king.
               check and not move capture (move to . previous)
    }
    

  • The query below matches a line of play in which a square is first cleared with tempo and is then occupied by another piece initiating a fatal attack on the opposing king. Note that the concluding point of this narrative is inferred.

    Nowhere does the query explicitly stipulate that the piece is sacrificed (i.e., resulting in a loss of material). Nor does the query stipulate a series of checks or even an attack in general. We only stipulate that the cleared piece is immediately captured (likely by force given the overall circumstance), that another piece immediately occupies the cleared square, and that mate or resignation soon follows.

    And yet the games matched by this line of play are remarkably on target. One can often achieve very accurate results with a much simpler implicit query than with one that explicitly stipulates every last detail.

  • // Clearance - of a friendly piece.
    cql() flipcolor { result 1-0
      line --> // The capture of the "sacrificing" piece.
               {move to (move to _ from [QRBN] previous)
               Square = move from A previous}
           --> // The "attack" begins on the square just cleared.
               move to Square
           --> // Mate or resignation with an upper bound.
               .{0 5}
           --> terminal
    }
    

  • Discovered check
  • This example borrows from the online reference for the discovery mechanism and is an instructive application of set theoretic operations.

    The move filter gives us the target square of the move that produces the current position. That square is occupied by the white piece making the move. We take the intersection of the complement of that square with the set of squares on which any white piece currently resides. That intersection excludes the white piece that just made the last move. If any of the remaining white pieces have the black king in check, it must be by discovery.

  • cql() (A & ~move to . previous) attacks k

  • Demonstrating the use of the function facility, we next construct a query that matches a line of play where discovered check is answered by discovered check. The reversecolor transform frees us of the burden of manually editing the filter to reverse white/black roles. Note that the conjunction of flipcolor with reversecolor works as one would hope.
  • cql() flipcolor {
      function Discovery() { (A & ~move to . previous) attacks k }
      line --> Discovery()
           --> reversecolor Discovery()
    }
    

  • Overloading
  • When a piece has more defensive assignments than it can handle, it is overloaded or overworked. It lays open the overloaded piece to a deflection, which forces or coerces the piece into abandoning one or more of its original defensive assignments.

    The exploitation of an overloaded piece can occur in a variety of ways. Capturing one of the defended pieces might draw the overloaded piece away from defending another piece or a critical square. A check or threatened mate or a sacrifice might draw the piece away from its defending duties.

    It's easy enough to construct a query that looks for a piece that is overloaded defending other pieces, but it's a bit more of a challenge when deciding whether an overloaded piece is defending a critical square or a rank or a file. For our purposes, we'll infer one of two things as a consequence of the overload: 1) if one of the pieces defended by the overloaded piece is captured, that capture likely leads to some advantage to the capturing side, either materially or positionally, and 2) if some opposing piece moves into the attacking field of the overloaded piece, there's a good chance the move is intended to deflect the piece.

    For this example, we'll be searching for positions in which each of three or more defended pieces has only the overloaded piece as a defender and is being attacked by the opposing side. There may be more than one attacking piece threatening any one defended piece but, in order to ensure that the overloaded piece is, indeed, overworked, any given attacking piece may only be attacking one of the defended pieces.

    Enforcing the inferences noted above, we will also ensure that either the next move from any matching position is a capture of one of the pieces defended by the overload, or that some opposing piece is moving into the attack field of the overload. Finally - and we believe this is not too much of a stretch - we shall infer that if the side with the overload is the losing side, the reason for the loss is at least not too distantly a consequence of the overload. Therefore, we limit the matching games to those in which the result is in favor of the opposing side.

  • // Overloading.
    // For the identity transform, a white piece is overloaded
    // with black to move and black to win and black is not in check.
    cql() not check  flipcolor { btm  result 0-1
      Defenders=[QRBN] Defendeds=[QRBN] Attackers=[qrbn]
      // Assign the set of squares occupied by overloaded pieces.
      Overloads=
        piece Defender in Defenders
          // An overloaded piece is defending at least three pieces.
          (piece Defended in Defendeds
            // Only the overloaded piece defends.
            (A attacks Defended) == 1  and
            piece Attacker in Attackers
              // Establish the chain of attack.
              ((Defended attackedby Defender) attackedby Attacker)  and
              // The attacking piece only attacks one defended piece.
              ((Defendeds attackedby Defender) attackedby Attacker) == 1
          ) > 2
      // Iterate over the set of overloaded pieces.
      piece Overload in Overloads
        // Black captures a piece defended by an overloaded piece.
        (move capture Defendeds attackedby Overload  or
        // Or black moves into the field of attack of the overloaded piece.
        move to _ attackedby Overload)  and
        comment("Overload at "  Overload)
    }
    
  • The nested piece filters conduct the bulk of the work for the query. Pity anyone who is not a software developer in trying to figure out exactly what the nest of filters accomplishes, but we'll summarize. Iterating over their respective sets of pieces (actually squares, but never mind), the nested construct ensures (in order) that: 1) there is one and only one piece defending the defended piece, 2) the defended piece is attacked by a piece of the opposing side, and 3) the attacker attacks that one piece and no other piece.

    Recall that for a position to match a query, every filter in the body of the query must be true. In the case of a set filter, the filter must yield a non-empty set. In the case of the piece filter, for some piece occupying a square in the iterated set, the body of the filter must be true. Drilling down through the piece nest, all of the filters must be true for some set of three pieces where each of the sets given by Defenders, Defendeds and Attackers is represented by one of those pieces.

    The piece filter returns a set of squares representing (loosely stated) the pieces for which the body of the filter is true. Note that we require that, for the defended pieces, the count for that set must be three or more. Also note that we assign the set of overloaded defenders (yes, there could be more than one) to a variable for later use. We finish off this work of art with a final cryptic piece filter, dictating that the move from the position that has matched the query up to this point is either 1) a capture of one of the defended pieces or 2) a move into the attack field of the overloaded piece.


    Out of a database of four millions games, this query matched roughly one hundred. We encourage the reader to experiment by changing the values of the various parameters of the query, being careful to not loosen the constraints so much that the query matches every game of a mega-million base. Before embarking on that experiment, one might want to pare down the size of the database with a preliminary query that will run much faster on the larger base. A sample query that will match about one percent of a typical base is given below.

  • cql() not check  flipcolor { btm  result 0-1
      Defenders=[QRBN] Defendeds=[QRBN] Attackers=[qrbn]
      piece Defender in Defenders
        (piece Defended in Defendeds
          Defender attacks Defended and Attackers attacks Defended
        ) > 2
    }
    

  • A deflected king
  • A deflected piece is drawn away from defending a piece, a square or a critical rank or file. Deflections are somewhat related to overloading, but not all deflections are of overloaded pieces. Here we look for a king that is deflected by a piece of lesser value than the piece being defended by the king. The defended piece is captured immediately following the deflection.

    Survivors of the overloading example are already familiar with nested piece filters. In the query below, the body of the innermost filter first ensures that the deflection yields a material gain. The line filter then stipulates a specific move sequence of the deflecting piece moving within the field of the king (which may be a capture), followed by a capture of the deflecting piece by the king, and then by the capture of the piece that is the target of the deflection. We also ensure that the king cannot recapture.

  • // Deflection.
    cql() flipcolor {
      // Iterate over designated pieces of interest in the king's field.
      piece Defended in ([qrbn] attackedby k)
        // Iterate over all potential deflecting pieces.
        piece Deflector in A {
          // The value of the deflecting piece is less than the value of the target.
          power Deflector < power Defended
          // The move sequence.
          line --> move from Deflector
               --> move from k capture Deflector
               --> move from A capture Defended and
                     not Defended attackedby k
        }
    }
    
  • Note that there are more efficient ways of isolating the deflecting piece than by iterating over all potential deflecting pieces, but we seem to be focused on nested iterating filters at the moment.

  • A decoyed king
  • A decoyed piece (usually a king or a queen) is drawn to a poisoned square (usually by a sacrifice) where it becomes the object of a tactical maneuver such as a fork or of a mating sequence. Below we construct queries for both cases where the decoyed piece is a king.

    In the case of a decoy followed by a mating sequence, we set an upper bound on the number of moves once the king has captured the decoy. Anything beyond that and it is dubious at best to think that the decoy was the decisive factor in the result. Best results are obtained by mate or resignation in one, where it is very clear that the king was drawn to a poisoned square.

  • // Decoy - followed by mate.
    cql() flipcolor {
      // The move sequence, with the king capturing the decoy.
      line --> move from k capture (move to _ previous)
           --> .{1 3}
           --> { btm mate }
    }
    

  • Next is a decoy followed by a fork of king and queen. The decoy is of lesser value than the queen. The singlecolor parameter to the line filter evaluates positions with the same side to move.
  • // Decoy - followed by fork.
    cql() flipcolor {
      // The move sequence.
      line singlecolor
        --> // The king captures the decoy.
            move from k capture (move to . from [RBN] previous)
        --> // King and queen are attacked and the forking piece is not captured.
            piece Fork in [BN]
              [qk] attackedby Fork > 1 and not move capture Fork
    }
    

  • Desperado
  • The term means different things to different people. For some it descibes a generally desperate circumstance on the board and for others it is in reference to a specific piece that is in a desperate situation. We'll address a couple of such scenarios in the following queries.

    The first query matches positions in which both sides have one or more pieces en prise and a sequence of captures ensues in which the desperate side either equalizes or [presumably] minimizes the material loss. In order to enforce the sense of "desperation", the side initiating the flury of captures must have at least two more pieces en prise than the opposition.

  • // Desperado - en prise.
    // The wtm filter is redundant but significantly improves search time.
    cql() flipcolor { wtm
      // Return the set of squares occupied by pieces en prise.
      function EnPrise() {
        // Iterate over the en prise candidates.
        piece enPrise in [QRBN]
          // Iterate over the attacking pieces.
          piece Attacker in [qrbnp]
            enPrise attackedby Attacker and
            // Either the value of the attacker is less than the attacked
            // or the attacked is undefended directly or indirectly (hanging).
            (power Attacker < power enPrise  or
              (not (A attacks enPrise or xray(A Attacker enPrise))))
      }
    
      PowerDiff = power A - power a
      // Return the last position in the line of play.
      { line nestban lastposition
          --> {not (check or pin) and abs PowerDiff < 2
               wEnPrise =? EnPrise()
               bEnPrise =? reversecolor EnPrise()
              // The desperate side has more pieces en prise.
               #wEnPrise > #bEnPrise + 1
              // No piece attacks more than a single piece en prise.
               piece all X in [qrbn] { wEnPrise attackedby X <= 1 }
              // The first capture is of a piece that is not en prise.  
               move from wEnPrise capture ~bEnPrise and
                 comment("(diff: " PowerDiff " en prise: " wEnPrise " " bEnPrise ")")}
          --> // The bloodbath — with a lower bound on the extent.
              move capture [Aa]{4 100}
          --> // Tag the end of the flurry.
              comment("(position: " currentposition " diff: " power A - power a ")")
      // The line of play results in a loss or equalization of material.
      } : {PowerDiff >= power A - power a}
    }
    
  • Minor adjustments in matching criteria can produce a matching set of games with significant differences in character. For example, setting a relational threshold on the sums of the values of the pieces en prise could intensify the sense of desperation on the board. Also note that in identifying pieces en prise, we could have included under-defended pieces but at some considerable cost in cpu cycles.


    Moving along, we'll look for positions in which a piece is desperately both en prise and trapped. The following query matches a line of play wherein the trapped piece is rescued in a flurry of captures initiated by a fellow comrade, leaving the trapped piece unscathed and the material balance undiminished.

  • // Desperado - trapped.
    // The wtm filter is redundant but significantly improves search time.
    cql() flipcolor { wtm
      // Return the set of squares occupied by pieces en prise.
      function EnPrise(S) {
        // Iterate over the en prise candidates.
        piece enPrise in S
          // Iterate over the attacking pieces.
          piece Attacker in [qrbnp]
            enPrise attackedby Attacker and
              // Either the value of the attacker is less than the attacked
              // or the attacked is undefended directly or indirectly (hanging).
              (power Attacker < power enPrise  or
                (not (A attacks enPrise or xray(A Attacker enPrise))))
      }
    
      // Return the set of squares occupied by pieces trapped.
      function Trapped() {
        // Iterate over the trapped candidates.
        piece enTrapped in [QRBN] {
          // All flight squares are guarded.
          square all Flight in move to _ from enTrapped legal
            piece Guard in [a] attacks Flight
              power Guard < power enTrapped  or
                not A attacks Flight
          // Iterate over the pieces attacked by the entrapped piece.
          not piece Attacked in [qrbnp] attackedby enTrapped
              // Either the value of the attacked is greater than the trapped
              // or the attacked piece is en prise.
              power Attacked >= power enTrapped or
                reversecolor EnPrise(Attacked)
        }
      }
    
      PowerDiff = power A - power a
      // Return the last position in the line of play.
      { line nestban lastposition
          --> {not (check or pin or move capture A previous)
               piece Piece = Trapped() & EnPrise([QRBN])
              // A friendly makes the first capture.
               move from ~Piece capture (~move to . previous) and
                 not move enpassant and
                 comment("(diff: " PowerDiff " trapped: " Piece ")")}
          --> // The bloodbath — with a lower bound on the extent.
              // The trapped piece does not participate in the bath.
              move from ~Piece capture [Aa]{4 100}
          --> comment("(position: " currentposition " diff: " power A - power a ")")
      // The balance of material is undiminished and the piece survived...
      } : {PowerDiff <= power A - power a and Piece and
             // ... and the piece has been freed (--> btm? --> does not work) .
             if btm
             then line --> btm --> not (Piece & Trapped())
             else line --> not (Piece & Trapped())}
    }
    
  • The query will not match positions in which there is more than one piece trapped (what are the odds?), as the piece assignment will then fail. Also note that the conditional if btm accounts for the fact that the Trapped() function is side-to-move dependent and side-to-move coming out of the bloodbath is unknown.

  • The windmill
  • The king is stuck in a trap of sorts, alternately being placed in check by a piece that is gobbling up material and a cohort that is patiently slapping the king around by discovery. The latter is usually a bishop (though it could be a rook) while the former is commonly a rook or a knight.

    The following query, though imprecise, is remarkably effective and is much faster in the execution of a search than the alternative. We give a lower bound on the ply of the positions evaluated to further save on search time.

  • // Windmill - quick'n'dirty.
    // Imprecise but effective.
    cql() ply > 40
      flipcolor
        line singlecolor
          --> ([RN] attacks k and move from k
          --> [B] attacks k and move from k and move from [RN] capture . previous){2 100}
    
  • The alternative (and more precise) query iterates over the potential participants, substituting piece variables for the catch-all piece designators employed above. We also allow for a windmilling bishop, though they are very rare. The query is far more compute intensive than the less precise version above.
  • // Windmill - nested piece filters.
    // Ensure that there are only two pieces employed in the tactic.
    cql() ply > 40
      flipcolor
        // Iterate over the windmilling pieces.
        piece Mill in [RBN]
          // Iterate over the stationary pieces giving discovery.
          piece Disc in [RB] {
            Disc != Mill
            line singlecolor
              --> (Mill attacks k and move from k
              --> Disc attacks k and move from k and move from Mill capture . previous){2 10}
          }
    

  • Zwischenzug
  • A move interposed during an exchange or a series of exchanges, which poses an immediate threat that must be answered. The interposed (and unexpected) move is often a check of the hostile king and commonly leads to material gain or positional advantage which would not otherwise obtain. The delayed recapture may occur many moves post-zwischenzug.

    The following query does a decent job picking up on the pattern without going too deeply into the weeds. Note that the query stipulates a material gain for the side playing the zwischenzug, but we could pick up many more positional gains by stipulating only that there is no material loss.

  • // Zwischenzug.
    cql() flipcolor {
      PowerDiff = power A - power a
      line
        --> // A capture that is not by a pawn and is not a recapture.
            {Square =? move capture a
            not move from P
            not move capture Square previous}
        --> // The recapture is delayed at least one move.
            {not check
            // The next move is not a recapture.
            move to ~Square
            // Assign the [attacked] piece that is to be recaptured.
            piece Piece = Square attackedby a
            // That piece has fewer guards than attackers.
            #(Square attackedby a) > #(Square attackedby A)
            // The piece is eventually recaptured, resulting in material gain.
            {find move to Square capture Piece}:child:
                 { PowerDiff > power A - power a and not move capture . }}
    }
    

Hypermodern themes

Aron Nimzowitsch, considered the founder and leading practitioner of hypermodernism, was probably more accurately the one person who contributed most to formalizing hypermodern theory, which was a challenge to the existing orthodoxy. Many have challenged the notion that it was a theory at all or that there was anything new about it. According to Edward Winter, Frank Marshall expressed a heartfelt disdain for hypermodernist practice when he described it as the tendency on the part of many of the grand masters to adopt an extremely close style of play, which involves a keen desire on the part of both players to avoid incurring the slightest risk.

  • The hypermodern center
  • After the first move 1.e4 white's game is in the last throes. — Gyula Breyer

    Departing from the classical view that the center must be occupied by pawns, Nimzowitsch advocated controlling the center with distant pieces. Inviting the opponent to occupy the void, the overextended center pawns can become objects of attack and the center can be gradually broken down.

    The query identifies positions in which the center is controlled from a distance and the opponent has been drawn into it. Furthermore, we isolate those positions in which the side in control has decided the time is right to begin the assault on the center. It should come as no surprse that the vast majority of matching games derive from the hypermodern openings.

  • // The hypermodern center.
    cql() flipcolor {
      Center = d-e4-5
      // The center score simply tallies the number of times a center
      // square is attacked by a piece.
      function CenterCalc() {
        Score = 0
        piece Piece in [QRBN]
          Score += #{Center attackedby Piece}
        Score
      }
    
      // The side in control is about to advance a pawn to challenge the center.
      move to c-f4 from P
      // A lower bound on the opponent's pawns in the center, our pawns have
      // never advanced into the center, and our control of the center is
      // from a distance.
      pc-f4-5 >= 2 and not find <-- Pc-f4 and not [QRBN] & Center
      // A lower bound on attention to the center and control is established.
      6 < CenterCalc() >= (reversecolor CenterCalc())*2
    }
    

  • An overprotected center pawn
  • A theme about which Nimzowitsch was quite adamant and that he actually practiced in competitive play. An important element of overprotection is that the defenders of the resource should outnumber the attackers, theoretically giving the defenders greater mobility.

    We look for positions in which the number of defenders of the pawn is at least five, an indication that there's a good chance that overprotection is intentional and not incidental. We also limit the search up to the early middle game, where overprotection is most likely to occur.

    The only serious question is that of what comprises a defender or an attacker. Obviously, we include any piece that directly defends or attacks. We also want to include both pieces in a battery and even probably an xray through a friendly piece. But what about an xray through a friendly pawn? That could be in support of an attack or defense of the center pawn, or the intervening pawn might be an obstruction to the defense or attack. Likely if it's along a diagonal it's in support, else an obstruction. We could go on in this vein but we believe we've chosen a reasonable set of criteria without getting too deeply into the weeds.

  • // Overprotected center pawn.
    // Limit the search to the first 30 ply.
    cql() ply <= 30
    // Count the number of pieces attacking a given pawn (directly or xrayed).
    function Countem(Pawn) {
       A attacks Pawn | 
       ray orthogonal (Pawn [QRBN] [QR]) |
       ray diagonal (Pawn [QRBNP] [QB])
    }
    
    // Iterate over all the center pawns.
    piece Protected in Pd-e4-5 {
      Defenders = Countem(Protected)
      Attackers = reversecolor Countem(Protected)
      (Defenders > 4) > Attackers  and
        comment("Attackers: " Attackers " Defenders: " Defenders)
    }
    
  • The Countem function employs the ray filter rather than the xray filter because we need to reverse the order of the set arguments to the ray to yield the square set of interest (i.e., the set of pieces attacking the pawn rather than the attacked pawn).

    The last line of the body of the piece filter establishes the relationship of defenders to attackers and a lower bound on the number of defenders. Note that (Defenders > 4) > Attackers is different from Defenders > 4 > Attackers. The former syntax stipulates that the number of defenders is greater than 4 and that the number of defenders is greater than the number of attackers. The latter stipulates that the number of defenders is greater than 4 and that the number of attackers is less than 4.


  • A knight outpost
  • An outpost is a square that is in or near the opponent's domain, usually in a "hole" created by the pawn structure where there is no pawn that can be advanced to attack and clear the outpost. Particularly in a closed position, a knight on an outpost can seriously cramp the enemy position.

    We'll match positions with a three-sided outpost on the fifth rank. If there is an immediate direct or indirect attack on the post then there must also be two pawns protecting the post.

  • // A knight outpost.
    cql() flipcolor
      // Iterate over eligible knights.
      piece Post in Na-h5 {
        // The post is three-sided.
        p & (up 1 Post | horizontal 1 Post) == 3
        // The post cannot be cleared by an advancing pawn.
        not p & up (horizontal 1 Post)
        // If the post is attacked then it must be defended by two pawns.
        if a attacks Post or xray([qb] [rn] Post) then P attacks Post == 2
        comment("Post at " Post)
      }
    

  • Undermining
  • Also known as removal of the guard. A defending piece is captured, leaving one of the opponent's pieces undefended or under-defended and opening the way for a net material gain. The initial capturing piece is commonly recaptured and is often of greater value than the piece captured. The material gain is realized in the capture of the undermined piece.

    Note the use of the =? assignment operator in filtering out empty sets from consideration. We also remove pawns from consideration as capturing pieces.

  • // Undermining.
    cql() flipcolor {
      // Return the set of squares occupied by pieces en prise.
      function EnPrise(EPC) {
        // Iterate over the en prise candidates.
        piece enPrise in EPC
          // Iterate over the attacking pieces.
          {piece Attacker in [QRBN]
            enPrise attackedby Attacker and
            // Either the value of the attacker is less than the attacked
            // or the attacked is undefended directly or indirectly (hanging).
            (power Attacker < power enPrise  or
              (not (a attacks enPrise or xray(a Attacker enPrise))))
          // Or the piece is under-defended.
          } or #{A attacks enPrise} > #{a attacks enPrise}
      }
    
      PowerDiff = power A - power a
      {line lastposition
         --> // We have a capture...
             {piece Guard = move capture [qrbn]
             piece Capturing = move from [QRBN]
             // ... but not an exchange.
             not move capture . previous
             // The captured piece was guarding one or more friendlies.
             Guardeds =? [qrbn] attackedby Guard}
         --> // Are any of the guarded pieces now en prise?
             {Undermined =?  EnPrise(Guardeds)
                comment("Undermined " Undermined)}
         --> // The capture of an undermined piece.
             move capture Undermined from ~(Capturing | P)
      // We have a material gain with no immediate capture to follow.
      }:child:{PowerDiff < power A - power a and not move capture .}
    }
    
  • The lastposition parameter to the line filter returns the last position of the line of play. That is the position from which the concluding capture of the line takes place. We actually want to measure the material difference of the two sides from the position resulting from that last move, which is the reason for employing the child filter.

  • Prophylaxis
  • Prophylaxis in chess amounts to anticipation of an opponent's plan. A prophylactic move is a move that discourages the opponent from carrying out a plan for fear of some negative consequence. A typical prophylactic maneuver is the advance of the rook pawn near a castled king to counter the possibility of a back rank mate or to prevent pins. This we will refer to as shallow prophylaxis.

    The Nimzowitschian or hypermodern notion of prophylaxis is a deep positional consideration aimed at not just improving one's own position, but at denying the opponent the opportunity of improving theirs. Identifying a deeply prophylactic move is a highly subjective endeavor. We are not aware of an AI that can aspire to such an accomplishment and neither shall we. However, CQL is perfectly capable of ferreting out cases of shallow prophylaxis, as we shall demonstrate with a couple of examples.

    We should note here that we are looking for moves that are in response to an imminent/demonstrable threat, else we must get into the business of reading the minds of the players. There are some who would argue that those moves are simply defensive moves, not prophylactic at all. The nitpicking types will then have to explain just how many moves out the threat must be before a defensive move becomes prophylactic. But that misses the point of the exercise, which is to demonstrate a language technique.

    Moving right along, we should also note that the queries below are just a little bit mind-bending, in that they seek to match positions that do not exist (i.e., are anticipated). In achieving that end, we will employ three features that are new to CQL with the release of version 6: 1) the position operator, 2) arbitrary piece assignment, and 3) the legal parameter to the move filter.

    The first query anticipates a threatened knight fork of king and/or queen(s) which has been prevented (or at least discouraged) by a prophylactic move. The query iterates over each knight in the position and tests the body of the square filter for each square: 1) that is guarded by the opposing piece whose move resulted in the current position, and 2) to which the knight might make a legal move. In other words, was the move leading to the current position intended to guard against a move by the knight?

  • // Prophylaxis - an anticipated fork.
    cql()  flipcolor {
      // Iterate over every hostile knight on the board.
      piece Knight in n
        // For each square at the intersection of the knights field
        // and the field of the piece making the last move.
        square Square in {move to {. attackedby {move to . from A previous}} from Knight legal} {
          // Eliminate positions in which the critical square was already defended
          // or was occupied by the piece now defending the square.
          piece Defender = A attacks Square
          not parent:{Square attackedby Defender or Square & Defender}
          // The critical square has the king and queen in the attack field
          // of a hypothetical knight.
          rotate90 {horizontal 1 up 2 Square} & [KQ] >= 2
          // The knight does not move to the critical square.
          not move from Knight to Square and
            comment("Critical square: " Square)
        }
    }
    
  • The expression yielding the square filter's iterating set is somewhat cryptic and so we'll translate to English. Working our way out from the innermost filter: 1) the move filter {move to . from A previous} gives us the square to which the last move was made, 2) the attackedby filter then gives us the set of squares that are guarded by the piece making that move (actually, the piece occupying the square to which the last move was made, which happens to be the piece in question), and finally 3) the outermost move filter then gives us the set of squares that are guarded by that piece and that can be legally moved to by the knight (effectively the intersection of two sets of squares).

    Iterating over each of the squares given above, we ask three questions: 1) is the piece that made the move producing the current position the only piece defending the square (the piece assignment will fail if the attack filter yields a set with cardinality greater than one), 2) did that piece already defend the square or occupy the square in the parent position (if so, we don't care about this square), and 3) if the knight were to move to occupy the square, would it fork a king and queen of the opposition? Note that we do not actually have an existing position from which to answer that question with an attack filter, so we must improvise by counting the squares at the intersection of the piece designator (king and queen) and the set of squares representing a knight's legal moves from the critical square.


    The next query matches a position in which a prophylactic pawn move counters a potential threat to the king on the back rank. The threat is anticipated to be by a hostile rook or queen against a king with no available escape.

  • // Prophylaxis - opening an avenue of escape.
    cql() flipcolor {
      line --> // The king has no escape and an adjacent pawn moves out.
               not ((_ attackedby K) & a-h2) and ((move from P) attackedby K)
           --> // Iterate over the potential threats.
               piece Threat in [qr]
                 // For each legal move by the threat to the king's rank.
                 square Square in move to a-h1 from Threat legal {
                   // The critical square is undefended.
                   not ((A attacks Square) or xray(A Threat Square))
                   // There are no interfering pieces between the critical
                   // square and the king.
                   ray horizontal (Square K)
                   comment("Critical square: " Square)
                 }
    }
    
  • The first constituent of the line filter establishes that the king has no escape off the back rank and that the next move is by a pawn adjacent to the king, presumably allowing the king an escape. The second constituent establishes that the pawn move is likely in response to an imminent threat on the back rank. Iterating over all legal moves by a queen or rook (the threat) to the back rank, we ask: 1) is there any opposing piece defending the critical square, and 2) is there a clear line of attack on the back rank between the critical square and the king?

    Note that we infer (impossible to explicitly confirm) that there is no practical or desirable defense to the threat other than the pawn move, else why the pawn move? In examining matching positions, that inference turns out to be sound far more often than not and is at times looking forward several moves.


  • TBD Sacrificial prophylaxis... Petrosian-style defensive positional exchange sacrifices.

  • In a recent e-mail exchange with Indian GM Ankit Rajpara, who just happens to subscribe to the judicious use of prophylactic thinking as part of a balanced middlegame and was thus drawn to this example, the discussion ranged from anticipating double attacks by line pieces (and how to craft that CQL query) to a whole new approach to recognizing prophylactic patterns that incorporates threat analysis by an analysis engine. (More on that later.)

    We thought that it would be useful to expand on the knight fork example above to show a similar approach for line pieces, and to include those examples in this section for the benefit of others and to compare and contrast considerations in the construction of the respective queries. Anticipating a double attack by line pieces raises a set of considerations that are a bit different from those of a knight fork. For example, in evaluating whether a defensive move is made in anticipation of the fork, we don't need to worry about one of the targets of a fork being mistaken for the piece making the prophylactic move. In the case of a line piece, we must take that possibility into consideration.

    For those who might be new to CQL, the material that follows amounts to a mini-tutorial on the process that one might follow in constructing this non-trivial CQL query: starting out with a set of general criteria, examining the results, further constraining the query, refining and repeating until we finally have what we want.


    We'll begin with a scaled down query that will simply filter out a set of games with at least one position in which a double attack by a bishop is anticipated. That is, if side-to-move is with the bishop, the critical square on which the attack might happen is within the range of the bishop's current set of legal moves.

  • // Prophylaxis - an anticipated double attack by bishop.
    cql()  flipcolor {
      // Iterate over every hostile bishop on the board.
      piece Bishop in b
        // For each square at the intersection of the bishop's field
        // and the field of the piece making the last move.
        square Square in {move to {. attackedby {move to . from A previous}}
                               from Bishop legal} {
          // The critical square has the targets in the line of attack
          // of a hypothetical bishop.
          Targets = [KQR]
          piece P1 in Targets
            piece P2 in Targets
              ray diagonal (P1 P2) and between(P1 P2) & Square
          comment("Critical square: " Square)
        }
    }
    
  • Since we don't have a shift operator in CQL, the nested piece filters iterating over the potential pairs of targets do a bit more work than is necessary, but they do get the job done. The criteria established by ray diagonal (P1 P2) and between(P1 P2) & Square assures us that the potential targets lie on the same diagonal and that the square that is under consideration lies between the two targets. Note that the ray filter, as given, allows for only unoccupied squares between the two pieces.

    Executing the query above on a mega-database will take a few minutes. Copy the matching games to a temporary database so that subsequent searches will take far less time. The query is general enough that it will match all games that we might be the least bit interested in.

    On examination of those matching games, it quickly becomes apparent that in many cases one of the targets of the double attack has also qualified as the piece making the prophylactic move. That's probably not what we want. We can filter out those games by eliminating positions in which one of the targets made the move that guards the critical square.

  • // Prophylaxis - an anticipated double attack by bishop.
    cql()  flipcolor {
      // Iterate over every hostile bishop on the board.
      piece Bishop in b
        // For each square at the intersection of the bishop's field
        // and the field of the piece making the last move.
        square Square in {move to {. attackedby {move to . from A previous}}
                               from Bishop legal} {
          // The critical square has the targets in the line of attack
          // of a hypothetical bishop.
          Targets = [KQR]
          piece P1 in Targets
            piece P2 in Targets {
              ray diagonal (P1 P2) and between(P1 P2) & Square
              Square attackedby (move to . from (A&~(P1|P2)) previous)
            }
          comment("Critical square: " Square)
        }
    }
    
  • The expression Square attackedby (move to . from (A&~(P1|P2)) previous) establishes that the piece making the last move does indeed guard the critical square and that the piece is not one of the targets. The construct (A&~(P1|P2)) gives us whatever is at the intersection of all white pieces and everything that is not under consideration as a target.


    We also wish to eliminate those positions in which the [line] piece making the last move was already guarding the critical square along the same line, or perhaps was occupying the critical square prior to the move.

  • // Prophylaxis - an anticipated double attack by bishop.
    cql()  flipcolor {
      // Iterate over every hostile bishop on the board.
      piece Bishop in b
        // For each square at the intersection of the bishop's field
        // and the field of the piece making the last move.
        square Square in {move to {. attackedby {move to . from A previous}}
                               from Bishop legal} {
          // The critical square has the targets in the line of attack
          // of a hypothetical bishop.
          Targets = [KQR]
          piece P1 in Targets
            piece P2 in Targets {
              ray diagonal (P1 P2) and between(P1 P2) & Square
              Square attackedby (move to . from (A&~(P1|P2)) previous)
              piece Guard = (A&~(P1|P2)) attacks Square
              not parent:{Square attackedby Guard or Square & Guard}
            }
          comment("Critical square: " Square)
        }
    }
    
  • The piece assignment accomplishes two things: 1) it captures the identity of the piece that attacks the critical square, and 2) it assures us that there is only one such attacking piece outside of the targets of the double attack (else the assignment fails to match the position).

    It's time, now, for one of those mind-bending exercises of which we are so eternally fond. We know that any given CQL filter will be evaluated in the context of a single position (technically and formally, it is always the "current" position but that technicality only muddies the water). In the case of our move previous filter, the expression (A&~(P1|P2)) is evaluated in the context of the parent position. In the case of our piece assignment, that same expression is evaluated in the context of the current position and will yield a slightly different set of squares.

    We very deliberately wish to employ the piece id of the guard — identified in the context of the current position — in the parent position where the piece will necessarily occupy a different square (the guard just made the last move). Remember that the id of a piece is a constant regardless of the square occupied by that piece from position to position. Of course, the attack filters and the intersection operator require set filters as arguments, and the piece variable yields a set of one square (the square occupied by the piece) when evaluated.


    One of the last measures we might take in weeding out faux anticipations is the elimination of positions in which the candidate prophylactic move did not actually deter the perceived threat. That is, the opponent made the move despite the defense of the critical square.

  • // Prophylaxis - an anticipated double attack by bishop.
    cql()  flipcolor {
      // Iterate over every hostile bishop on the board.
      piece Bishop in b
        // For each square at the intersection of the bishop's field
        // and the field of the piece making the last move.
        square Square in (move to (. attackedby (move to . from A previous))
                               from Bishop legal) {
          // The critical square has the targets in the line of attack
          // of a hypothetical bishop.
          Targets = [KQR]
          piece P1 in Targets
            piece P2 in Targets {
              ray diagonal (P1 P2) and between(P1 P2) & Square
              Square attackedby (move to . from (A&~(P1|P2)) previous)
              piece Guard = (A&~(P1|P2)) attacks Square
              not parent:{Square attackedby Guard or Square & Guard}
            }  // piece
          not move from Bishop to Square and
            comment("Critical square: " Square)
        }  // square
    }  // flipcolor
    
  • We could continue to further refine the query, eliminating what might amount to noise in the results, but at some point the improvements are no longer worth the effort. We'll leave such detail to the more industrious among us.

  • The construction of a query anticipating a double attack by that other line piece (the rook) requires just a few alterations to the query we've been laboring over. Most notably, a rook attacks along orthogonal lines rather than diagonal lines. We also adjust the candidate targets of the double attack to consider only pieces of greater value than the rook, namely the king and the queen(s).
  • // Prophylaxis - an anticipated double attack by rook.
    cql()  flipcolor {
      // Iterate over every hostile rook on the board.
      piece Rook in r
        // For each square at the intersection of the rook's field
        // and the field of the piece making the last move.
        square Square in (move to (. attackedby (move to . from A previous))
                               from Rook legal) {
          // The critical square has the targets in the line of attack
          // of a hypothetical rook.
          Targets = [KQ]
          piece P1 in Targets
            piece P2 in Targets {
              ray orthogonal (P1 P2) and between(P1 P2) & Square
              Square attackedby (move to . from (A&~(P1|P2)) previous)
              piece Guard = (A&~(P1|P2)) attacks Square
              not parent:{Square attackedby Guard or Square & Guard}
            }  // piece
          not move from Rook to Square and
            comment("Critical square: " Square)
        }  // square
    }  // flipcolor
    
  • We should point out that all of the examples above match positions from either a black or a white perspective. The flipcolor filter also evaluates the body of the transform with the roles of the white and black sides switched.


    And finally (speaking of anticipation)... as we noted above, our GM correspondent has proposed an idea that may have profound potential, not only in the exploration of what we are calling deep prophylaxis, but in the exploration of a wide range of ideas.

    The complications that arise in the recognition of prophylaxis all derive from the fact that we are having to anticipate a position or line of play that does not exist in the game tree. How to remedy that problem? In short, by incorporating an analysis engine (think Stockfish) into the process to give us the moves that are missing (perhaps 20-30 ply deep).

    The process will look something like the following, making use of existing facilties of the Scid++ application:

    1. Pre-process a database of games with a CQL query that tags positions of interest (perhaps a particular pawn structure, a well known tactical pattern, or simply a king on a back rank with no available flight) with a comment containing a string that triggers the engine's analysis.
    2. Put the analysis engine in annotate mode (analysis window), passing over the entire batch of games that matched item 1, and analyze each tagged position for threats. That is, analyze the position on the other side of a null move, and thus from the opponent's perspective, automatically attaching the multi-PV analysis to the position as a collection of the top N variations ordered by evaluation score.
    3. Post-process the results with a CQL query that picks out, for example, tell-tale patterns in the analysis or perhaps patterns in the mainline that correlate in some fashion with patterns in the threat analysis.

    The possibilities are difficult even to estimate. To some extent, the presence of threat analysis in the game tree does allow us to read the minds of the players in the flow of the game. Can we detect the patterns that reveal a deep strategic prophylaxis with CQL? Sacrificial prophylaxis? Maybe. We intend to find out.

    Development of the extended mechanics required for item 2 above will take place on an experimental branch of the Scid++ project. All the rest of the pieces are already in place. A tutorial on the use of this feature is quite likely in the offing.

Miscellaneous

Most of the examples of this section are what we might call advanced. A few of the examples enter into a rather vague realm; e.g., what exactly does it mean for a position to be cramped and how does one go about evaluating such a position? Or at what point does a king hunt make the transition into king harassment? We'll make our judgement. The reader may make his own.

  • Blind swine
  • Two rooks on the seventh rank are deemed to be pigs or swine due to their tendency to gobble up all the material on that rank. Chess lore has it that those swine are blind if they are not able to find mate despite their combined powers against the opposing king on the back rank. This seems like a bit of a bad rap on the pigs since without the aid of additional pieces a mate is very difficult unless the mated side assists in the mate by placing an obstructing rook on just the right square (see the swine's mate pattern above).

    Nonetheless, the gobbling up of material by the pigs is of some interest, especially if that material gain makes a difference in the outcome of the game. We set a lower bound on the number of captures made by the swine on the seventh rank. We also stipulate that the side with the marauding swine wins the game, likely as a consequence of significant material gain.

  • // Blind swine.
    cql() initial flipcolor { result 1-0
    sort "Match count"
      // Returns the number of matching positions found.
      (find all { ka-h8  Ra-h7 == 2
                  move from Ra-h7 capture [a]a-h7 and
                    comment("CQL")}
      ) > 3
    }
    
  • We tag each matching position with a "CQL" comment so Scid can set the current ply for matching games to the first match. Note that when employing the find all filter we limit the query to the initial position. There is no point in evaluating that filter for every position in the game.

  • The Greek gift
  • The so-called gift is a bishop sacrifice on h7. The sac can be considered successful from white's perspective if it leads to mate or to a material grab as a consequence of the threat of mate.

    With this query we're not interested in dictating the pattern so much as we are in learning what the pattern is. What are the conditions that lend themselves to a positive result after such a sacrifice?

    We set the stage with some very limited constraints. At a minimum, we're looking for a critical involvement of the white king's knight after the sac, and so we place a knight on f3. We also expect that there is an obstructing black pawn on g7, limiting the black king's response to the inevitable onslaught of checks. Beyond that, we only lay out the bishop sac followed by an upper bound on the number of moves before mate or resignation.

  • // Greek gift.
    cql() result 1-0  ply < 30
    Nf3 pg7
    line --> move from B capture ph7
         --> move from k capture h7
         --> .{2 12}
         --> terminal
    
  • The result of the query is telling. The white queen is almost always on the d1-h5 diagonal. The white king pawn is often advanced to e5, chasing away the black knight from the defense of h7. The white king's rook pawn is frequently advanced to h4, with the sac taking place before white has castled and with the pawn ready to recapture on g5 with discovered check. And white's queen's bishop is often in play along the c1-h6 diagonal.

    But perhaps most telling is the pattern on the black side of the board. The black queen is nearly always out of play, often hemmed in by her own knight. The king's rook is nearly always still on its castling square, and the king's wing is often woefully underdefended. And while the king's bishop may be in the fight, it can do more harm than good.

    We should point out a couple of things about the query. We limit the number of ply because the further into the game we get, the further away we get from the pattern we wish to study. Also note the use of the terminal filter. Many games are resigned before they arrive at a mate, so we give side to win with the result filter and indicate end of game as being the last node of the main line of the game tree.


  • The king hunt
  • The king hunt is typically characterized by [arguably] two or three defining elements:
    1. The side on the hunt will usually sacrifice material to flush the opposing king out into the open.
    2. The king is subject to a sustained attack, driving the piece far away from its initial position.
    3. The long walk typically ends in mate or resignation on the opposite side of the board.

    The following query stipulates all three of those elements looking backward from the terminal position, sorting the matching games by the length of the walk.

  • // The king hunt.
    // Placing the terminal filter up front reduces search time.
    cql() terminal
      // Board congestion has never dropped below some threshold.
      not find <-- power [Aa] < 30
      flipcolor {
        result 1-0  ka-h1-3   Q > 0
        // The king has been "drawn out" by sac's or decoys.
        find <-- 2 100  move from k capture (move to . from A previous)
                            and comment("Sac")
        // Consecutive checks with a lower bound.
        sort "Consecutive checks"
          line singlecolor <-- check {4 100} <-- check and comment("CQL")
      }
    
  • There are a couple of constraints enforced on the position that may be somewhat mysterious. First, with the power filter we establish a lower bound on board congestion, ensuring that congestion has never dropped below some arbitrary threshold. We do this for two reasons: 1) we believe that the greater the congestion the more interesting is the hunt, and 2) we avoid end games in which pawns have promoted and the kings may have gone on voluntary walks about the board prior to the promotion. Second, we ensure that for the side on the hunt the queen is still on the board, thus eliminating games in which a king has voluntarily come out of hiding because the queens have come off.

  • Railroaded
  • A mating technique that alternates between a kill box pattern and a triangle pattern. The queen and rook work in concert up the track in supporting maneuvers. The opposing king is driven in a forced walk to the edge of the board or up against an obstacle.

    We employ the line filter with wild card grouping to match repetitions of the mating pattern that ends at the terminal position (resignation or mate). The first constituent of the grouping stipulates that the king is in check and moves up the rail line between the rook and queen. The alternating constituent stipulates that either the rook makes a boxing maneuver or the queen makes a triangling maneuver, forcing the king up the rails.

  • // Railroaded.
    // Rotate the orientation of the pattern toward all four edges of the board.
    cql() flipcolor rotate90 { result 1-0
      // Walk the king up between the tracks.
      line --> ({check  move from k to vertical between(Q R)}
           --> // Alternate between rook and queen moves...
               move from R to diagonal 2 Q    or
               move from Q to horizontal 2 R) {3 100}
           --> // ... until mate or resignation.
               terminal 
    }
    
  • Note that the query does not distinguish between what might be multiple rooks or queens on the board, which would normally undermine one's intentions. The maneuver, in this case, apparently is so exacting over multiple repetitions that other pieces on the board are relegated to irrelevance. To verify this, one can reduce the lower bound on the repetition quantifier and, indeed, one will find the occasional match where a second rook interferes with the pattern on account of the between filter. We could correct this deficiency in the query but at the cost of some added compute time, and with nothing to gain.

  • Symmetric formations
  • Borrowing from one of the examples of the online reference, this basic query can be used as a starting point for any number on interesting searches. We look for positions in which some aspect of the position exhibits the property of symmetry between the opposing sides.

    By adjusting the various constraints on the upper or lower bound for the ply and power filters, and by tinkering with the in-filter of the square filter, one can look for a variety of symmetric patterns: symmetric endgames, symmetric openings, symmetric batteries and so forth. We give two such examples here.

    The first looks for positions with symmetric pawn formations along the files of the extended center. We stipulate up front that the designated pawns (squares) must amount to at least one, else the query will match positions with no pawns in the designated field. We also give a lower bound on the cumulative piece value of the position in order to avoid endgame positions. And the lower bound on ply gets us out of the early opening.

  • // Symmetric pawn formation.
    // Avoid positions in the opening and endgame.
    cql() ply > 20  power [Aa] > 20
      // Assign the designation of interest.
      Designator = [Pp]c-f1-8
      // The designated set must be non-empty.
      Designator > 0
      square all Square in Designator
        colortype Square ==
          -colortype
              makesquare (file Square
                    8 - rank Square + 1)
    

  • The second example searches for symmetric rook batteriess; i.e., batteries that face each other. To make the position a bit more interesting, we also throw in symmetry in the position of the queens if they are present. Note that the presence of promoted rooks on the board could eliminate otherwise qualifying positions from a match.
  • // Symmetric rook batteries.
    // Avoid positions in the opening and endgame.
    cql()  ply > 10  power [Aa] > 20
      // We have at least one battery accounted for.
      shifthorizontal {R & a1-4 == 2}
      square all Square in [RrQq]
        colortype Square ==
          -colortype
              makesquare (file Square
                   8 - rank Square + 1)
    

  • Pawn chains
  • We're interested in simple pawn chains consisting of pawns placed on the same diagonal, without interruption, so that only the base of the chain is undefended. Arbitrarily complex bifurcating chains and/or chains with multiple bases are beyond the scope of this tutorial and are likely beyond the capabilities of CQL, having neither recursion nor graph theoretic features for complex chain detection.

    We present below a number of approaches for detecting simple chains. First, we'll borrow a general idea from the serialpin example of the online reference and convert it to search for a chain of pawns rather than a chain of pins. The loop filter iterates over the body of the filter until the evaluation of the body is false. In our case, the loop repeats until the current pawn in the chain has no successor.

  • // Pawn chain - loop construct.
    cql() flipcolor {
      piece Pawn in P {
        piece CurrentPawn = Pawn
        Length = 1
        loop {
          // Is there a successor in the chain?
          piece CurrentPawn = P attackedby CurrentPawn
          Length += 1
        }  
        Length >= 5
      }
    }
    
  • The body of the piece filter matches the position if the length of the chain is five or greater. There is a disadvantage in this approach as the chain will not extend beyond a bifurcation. This is due to the fact that assignment to a piece variable is only valid and only matches the position if the set has cardinality of exactly one.


    The next query utilizes direction filters which yield a set of all the white pawns on the board that are supported by another pawn. That set does not include the base pawn in the chain because we need to be able to distinguish the base pawn. With this query, we will also match a bifurcated chain originating at the base. Other bifurcations in the chain will only be matched along the extended diagonal.

  • // Pawn chain - direction filters.
    cql(quiet)
      // The set of all pawns supported by another pawn.
      Chained = P & (northeast 1 P | northwest 1 P)
      // Iterate over all base pawns.
      piece Base in (P & ~Chained) attacks Chained {
        // Chained pawns relative to the base...
        Chain = Chained & (northeast Base | northwest Base)
        // ... and fully contiguous.
        not ((~P) & between(Base Chain))
        // Sort by a lower bound on the chain length (not including base).
        sort "Chain length" {
          comment("CQL Chain base at " Base)
          Chain
        } > 3
      }
    
  • With the variable Chained assigned the set of pawns that are guarded by another pawn, we iterate over a set of base pawns produced by determining which white pawns other than the Chained pawns are guarding a Chained pawn. For each of the base pawns, we generate a set containing the Chained pawns relative to that base, and then ensure that that chain is fully contiguous. (We do not bother to measure the length of a chain fragment nearest the Base because, well, we're lazy.)

    Note that we only tag the first matching position with a comment, though the chain may persist through many positions. See the online reference on smart comments as part of a sort filter for an explanation.


    Finally, the following one-liner matches an e5 chain that runs all the way from b2 to f6 along the long dark diagonal. If the count of squares occupied by pawns equals five, the chain must be complete.

  • cql() P & diagonal 1 5 a1 == 5 
    

  • Space and mobility
  • The side controlling more squares than the other is said to have a spatial advantage. That advantage should translate to a superior mobility of forces and that superior mobility can often determine the outcome of a game.

    Evaluation of the relative mobility of either side's pieces is a subjective undertaking, but we might look to the chess programming domain for some help with a number of related definitions. Drawing on the Chess Programming Wiki (CPW) as a resource:

    • Space: a measure of the amount of area of the chess board controlled by either side. Closely related to square control, in particular center control.
    • Square Control: refers to which and how many pieces attack or defend a particular square, independently from its occupancy state. Squares diagonally controlled by a pawn are usually taboo for opponent pieces. Square control is considered in evaluating piece mobility.
    • Center Control: the control or occupation of the center and extended center of the board, which gains space and allows pieces fast access to most of the board areas.
    • Mobility: a measure of the number of choices (legal or pseudo-legal moves) a player has in a given position. The more choices one has at one's disposal, the stronger one's position. Correlations have been established between a side's mobility — with the position in material balance — and the outcome of the game.

    We're interested in matching positions in which one side has a marked (even extreme) advantage in space and mobility over the opponent coming out of the opening phase of the game. In fact, by stipulating a ply range for the query one can narrow the phase very specifically. The ultimate end is toward understanding which opening lines lead consistently to a lopsided advantage in space and mobility.


    We'll begin by looking at center control. The following query utilizes a simple calculation which satisfies the definition given above; i.e., that the center is controlled by pieces either defending or occupying that space. We somewhat arbitrarily define control as one side having quadrupled the attention to the center as the opponent.

  • // Center control.
    cql() 
    // Calculate and return a score for center control.
    function CenterCalc() {
      Score = 0  Center = d-e4-5
      // For each piece, tally the number of center squares attacked by
      // that piece and add a bonus if the center is occupied by the piece.
      piece Piece in [QRBNP]
        Score += #{Center attackedby Piece} + #{Center & Piece} * 2
      Score
    }
    
    wCenterControl = CenterCalc()
    bCenterControl = reversecolor CenterCalc()
    
    // One side must exceed a lower bound and quadruple the score of the other side.
    function Qualify(SideA SideB) { 12 < SideA >= SideB*4 }
    if Qualify(wCenterControl bCenterControl)
      then comment("White controls:  " wCenterControl "  " bCenterControl)
      else
        if Qualify(bCenterControl wCenterControl)
          then comment("Black controls:  " bCenterControl "  " wCenterControl)
          else false
    

  • Square control is less about control of an area as of a specific square, and is less concerned about occupancy than about whether or not the square may be occupied. For the following query we'll expand the area of interest to include the extended center, and exclude from consideration those squares that are attacked by a hostile pawn.

    When we were looking at center control, we calculated a score based on the forces arrayed against an area of the board. Here we're interested in which side is in control of the greater number of squares within an area, a subtle distinction. The function calculating the score increments the score for each white piece attacking a given square and decrements the score for each black piece attacking the square. Either or both sides may be denied control if an opposing pawn is defending the square.

    Note that we are only interested in positions with a minimum amount of congestion (as measured by the piece count), since the relative scores become less interesting as the position opens up and both sides have virtually unlimited mobility.

  • // Square control.
    cql()  [Aa] > 24
    // Calculate and return the score for a given square, excluding xray's.
    // Every white piece attacking the square adds to the score, every black piece
    // subtracts from the score. An attack on a square by an opposing pawn nullifies
    // the score.
    function SquareCalc(Sq) {
      Score = 0
      if not (Sq attackedby p)
        then Score += #{[QRBNP] attacks Sq}
      if not (Sq attackedby P)
        then Score -= #{[qrbnp] attacks Sq}
      Score
    }
    
    eCenter = c-f3-6  wSquareControl = 0  bSquareControl = 0
    // Initialize the set variables to the empty set.
    wSquares = makesquare(0 0) bSquares = makesquare(0 0)
    // Tally the number of squares controlled by each side
    // and accumulate their respective sets of controlled squares.
    square Square in eCenter {
      Tally = SquareCalc(Square)
      Tally != 0
      if Tally > 0
        then wSquareControl += 1 and wSquares = wSquares | Square
        else bSquareControl += 1 and bSquares = bSquares | Square
    }
    
    // One side must exceed a lower bound and quadruple the score of the other side.
    function Qualify(SideA SideB) { 8 < SideA >= SideB*4 }
    if Qualify(wSquareControl bSquareControl)
      then comment("White controls:  " wSquareControl  " " bSquareControl) and
           comment(wSquares " " bSquares)
      else
        if Qualify(bSquareControl wSquareControl)
          then comment("Black controls:  " bSquareControl "  " wSquareControl) and
               comment(bSquares " " wSquares)
          else false
    

  • Most chess engines implement algorithms evaluating the spatial qualities of a position. According to CPW, Stockfish defines a space area bonus by the number of safe squares for minor pieces on the central four files on ranks 2 to 4, counting twice if on a rearspan of an own pawn. The space area bonus is multiplied by a weight, determined by the number of own pieces minus the number of open files. Two points: 1) a bonus for the number of safe squares (mobility), and 2) minus the number of open files (the more closed a position the more important a spatial advantage). We should borrow from that heuristic and run with it.

    The Stockfish Evaluation Guide (SEG) defines a safe square as having no opposing pawn attacking the square and having no friendly pawn occupying the square. Safe squares one, two or three squares behind a friendly pawn are counted twice. Additionally, the SEG indicates that the space bonus is to be multiplied by the number of our pieces minus two times the number of open files. Looking at the evaluation code, the weighting is heavily in favor of congested, closed positions. If more than a quarter of the non-pawn material is off the board, the space evaluation is completely ignored. That is, whatever spatial advantage one side might have over the other is far more relevant when space is generally scarce. Furthermore, the space under consideration is especially important in the latter phase of the opening.

    The following query implements the Stockfish notion of space evaluation. In calculating the bonus, the square filter yields the set of squares for which there is a bonus (i.e., for which the body of the filter is true). We tag each matching position with a comment that lists that square set. Note that right up front we limit consideration of positions to those with a lower bound on material (roughly 75%) and on pawn count (roughly translating to the potential for open files). This is in keeping with the SEG.

  • // Space evaluation.
    // A lower bound on congestion and pawns on the board.
    cql() power [QRBNqrbn] > 45  [Pp] >= 12
    
    // Calculate and return the space area bonus.
    // Count the number of safe squares within a critical
    // space that might be occupied by a piece.  Add a bonus
    // if that square is in the rear-span of a friendly pawn.
    function BonusCalc() {
      Bonus = 0
      Squares =
        // Iterate over each square in the bonus area.
        square Square in c-f2-4
          // Neither occupied by a friendly pawn nor
          // attacked by a hostile pawn.
          if not (Square & P or Square attackedby p)
            then {
              Bonus += 1
              // Double the bonus if behind a friendly pawn.
              if up 1 3 Square & P then Bonus += 1
            } else false
      comment("Bonus squares: " Squares)
      Bonus
    }
    
    // Calculate and return the space evaluation.
    // Weighted positively by the number of pieces on board
    // for a side and negatively for open files. Note that the
    // evaluation is non-linear by weight. That is, the more
    // congested and closed a position, the more relevant is
    // the bonus calculated above.
    function SpaceEval() {
      Weight = #A
      // Iterate over every file, docking the weight for open files.
      square Square in a-h1
        if not ((up Square) & [Pp]) then  Weight -= 2
      Score = BonusCalc()
      comment("Bonus " Score " with Weight " Weight)
      // Return the evaluation.
      Score * Weight * Weight
    }
    
    comment("WHITE")
    wSpaceEval = SpaceEval()
    comment("BLACK")
    bSpaceEval = reversecolor SpaceEval()
    
    // One side must quadruple the score of the other side.
    function Qualify(SideA SideB) { SideA >= SideB*4 }
    if Qualify(wSpaceEval bSpaceEval)
      then comment("||| White advantage:  " wSpaceEval  " " bSpaceEval)
      else
        if Qualify(bSpaceEval wSpaceEval)
          then comment("||| Black advantage:  " bSpaceEval "  " wSpaceEval)
          else false
    
  • There is some magic happening with this query that is worthy of explanation. There are two invocations of the SpaceEval() function, one for the white side and one for the black. That function, in turn, invokes the BonusCalc() function. The implementation of both of those functions is from the white perspective. When we invoke that nest of functions for space evaluation from the black perspective, we apply the reversecolor transform to the call.

    This all just happens to work because the CQL function facility behaves much like macro expansion. Therefore, the application of a transform will cascade through an arbitrary depth of nested function calls. Expanding the "macros" in cascading fashion effectively transforms the entire series of code blocks represented by the function nest. Though the authors of the language casually observe that the facility merely behaves as expected, the genius behind this semantic is difficult to overstate.


  • Our evaluation of mobility also will mirror that of the SEG, which is a bit more involved than simply summing up the number of legal or pseudo-legal moves. We'll not be evaluating so-called safe mobility — counting only those squares where a piece can move without being en prise. Nor is occupancy by a hostile piece a consideration. We yield to the judgement of the SEG in those determinations which are likely the consequence of long experience in the discipline.

    We'll also ignore the middle game vs endgame weighting, since we have no interest in measuring wide-open endgame mobility and our position-matching never reaches that phase of the game. As is the case in evaluation of space, differences in mobility between the two sides are most relevant when mobility is at a premium.

    Quoting from the SEG, the area under consideration for mobility excludes squares protected by enemy pawns, or occupied by our blocked pawns or king. Pawns blocked or on ranks 2 and 3 will be excluded from the mobility area. And mobility is measured by the number of attacked squares in the Mobility area. For a queen, the squares defended by an opponent's knight, bishop or rook are ignored. For minor pieces, squares occupied by our queen are ignored.

    The code for the SEG mobility evaluation also gives some special consideration for line pieces in that, if their line is behind a queen (or queen or rook), their attack will xray through the queen or rook to the square under consideration.

  • // Mobility evaluation.
    // Well into the opening and a lower bound on congestion
    // with rough material balance.
    cql()  ply > 16  power [QRBN] == power [qrbn] > 24
    // Decide whether a given square is eligible for evaluation.
    // Squares occupied by a friendly king or queen are not eligible.
    // Squares attacked by an opposing pawn are not eligible.
    // Squares occupied by a friendly pawn that is blocked or that
    // occupies the second or third ranks is not eligible.
    function MobilityArea(Sq) {
      // Occupied by a friendly king or queen or
      // attacked by a hostile pawn...
      if Sq & [KQ]  or  p attacks Sq  or
        // ... or occupied by a friendly pawn either
        // back of the fourth rank or blocked.
        (Sq & P and (Sq & a-h2-3 or not up 1 Sq & _))
        then false
        else true
    }
    
    // Calculate a mobility score that is the cumulation of the mobility of
    // each non-pawn piece for a side.  A piece's mobility is determined by the
    // number of eligible squares that it attacks, with a few special per-piece
    // considerations. Bishop mobility includes an xray through a friendly queen.
    // Rook mobility includes an xray through a friendly queen or rook. And the
    // queen's mobility excludes those eligible squares that are attacked by
    // a minor hostile piece.
    function Mobility() {
      Score = 0
      // Iterate over every piece.
      piece Piece in [QRBN] {
      Squares = 
        // Iterate over every square attacked by or xray'd by the piece.
        square Square in {. attackedby Piece | xray(Piece [QR] .)}
          if MobilityArea(Square)
            then {
              // No score for a queen if the square is attacked by...
              not (type Piece == 5 and Square attackedby [rbn]) // queen
              // ... else score if the square is attacked by the piece.
              Score += #{Square attackedby Piece}
              // Bishops score an xray through a queen.
              if type Piece == 3   // bishop
                then Score += #{xray (Piece Q Square)}
              // Rooks score an xray through a queen or rook.
              if type Piece == 4   // rook
                then Score += #{xray (Piece [QR] Square)}
            } else false
        comment("[" Piece " mobile on " Squares "]")
      }
      // Return the score.
      Score
    }
    
    comment("WHITE")
    wMobility = Mobility()
    comment("BLACK")
    bMobility = reversecolor Mobility()
    
    // One side must quadruple the score of the other side.
    function Qualify(SideA SideB) { SideA >= SideB*4 }
    if Qualify(wMobility bMobility)
      then comment("||| White advantage:  " wMobility  " " bMobility)
      else
        if Qualify(bMobility wMobility)
          then comment("||| Black advantage:  " bMobility "  " wMobility)
          else false
    

  • Finally, we'll give a weighted composite of all of our measures of space and mobility. Those measures are combined to conditionally match a position if the composite measure gives a fairly extreme advantage in space and mobility to one side or the other. Each of the evaluation sections of the query is more or less a copy-n-paste from the queries above.
  • // Space and mobility - composite evaluation.
    cql(quiet)
    power [QRBNqrbn] > 45  [Pp] >= 12
    Center = d-e4-5  eCenter = c-f3-6
    
    // Skip over positions that are a capture followed by recapture. Such positions
    // are prone to score oscillation and rarely represent an interesting match.
    // (The move from the current position would be the recapturing move.)
    not line <-- Recap = move capture .
             <-- move capture Recap
    
    // Evaluate center control.
    
    // Calculate and return a score for center control.
    function CenterCalc() {
      Score = 0
      // For each piece, tally the number of center squares attacked by
      // that piece and add a bonus if the center is occupied by the piece.
      piece Piece in [QRBNP]
        Score += #{Center attackedby Piece} + #{Center & Piece} * 2
      Score
    }
    
    wCenterControl = CenterCalc()
    bCenterControl = reversecolor CenterCalc()
    
    // Evaluate square control.
    
    function SquareCalc(Sq) {
      Score = 0
      if not (Sq attackedby p)
        then Score += #{[QRBNP] attacks Sq}
      if not (Sq attackedby P)
        then Score -= #{[qrbnp] attacks Sq}
      Score
    }
    
    wSquareControl = 0  bSquareControl = 0
    // Tally the number of squares controlled by each side.
    square Square in eCenter {
      Tally = SquareCalc(Square)
      Tally != 0
      if Tally > 0
        then wSquareControl += 1
        else bSquareControl += 1
    }
    
    // Evaluate space (SEG).
    
    // Evaluation is weighted in favor of a congested and somewhat closed positon.
    // (See also the constraints imposed at the top of this query.)
    // This evaluation of space can be considered a measure of mobility on the
    // front ranks which is especially important in the early stages of the game.
    
    // Calculate and return the space area bonus.
    // Count the number of safe squares within a critical
    // space that might be occupied by a piece.  Add a bonus
    // if that square is in the rear-span of a friendly pawn.
    function BonusCalc() {
      Bonus = 0
      // Iterate over each square in the bonus area.
      square Square in c-f2-4
        // Neither occupied by a friendly pawn nor
        // attacked by a hostile pawn.
        if not (Square & P or Square attackedby p)
          then {
            Bonus += 1
            // Double the bonus if behind a friendly pawn.
            if up 1 3 Square & P then Bonus += 1
          } else false
      Bonus
    }
    
    // Calculate and return the space evaluation.
    // Weighted positively by the number of pieces on board
    // for a side and negatively for open files. Note that the
    // evaluation is non-linear by weight. That is, the more
    // congested and closed a position, the more relevant is
    // the bonus calculated above.
    function SpaceEval() {
      Weight = #A
      // Iterate over every file, docking the weight for open files.
      square Square in a-h1
        if not ((up Square) & [Pp]) then  Weight -= 2
      Score = BonusCalc()
      // Return the evaluation.
      Score * Weight * Weight
    }
    
    wSpaceEval = SpaceEval()
    bSpaceEval = reversecolor SpaceEval()
    
    // Evaluate mobility (SEG).
    
    // Decide whether a given square is eligible for evaluation.
    // Squares occupied by a friendly king or queen are not eligible.
    // Squares attacked by an opposing pawn are not eligible.
    // Squares occupied by a friendly pawn that is blocked or that
    // occupies the second or third ranks is not eligible.
    function MobilityArea(Sq) {
      // Occupied by a friendly king or queen or
      // attacked by a hostile pawn...
      if Sq & [KQ]  or  p attacks Sq  or
        // ... or occupied by a friendly pawn either
        // back of the fourth rank or blocked.
        (Sq & P and (Sq & a-h2-3 or not up 1 Sq & _))
        then false
        else true
    }
    
    // Calculate a mobility score that is the cumulation of the mobility of
    // each non-pawn piece for a side.  A piece's mobility is determined by the
    // number of eligible squares that it attacks, with a few special per-piece
    // considerations. Bishop mobility includes an xray through a friendly queen.
    // Rook mobility includes an xray through a friendly queen or rook. And the
    // queen's mobility excludes those eligible squares that are attacked by
    // a minor hostile piece.
    function Mobility() {
      Score = 0
      // Iterate over every piece.
      piece Piece in [QRBN]
        // Iterate over every square attacked by or xray'd by the piece.
        square Square in {. attackedby Piece | xray(Piece [QR] .)}
          if MobilityArea(Square)
            then {
              // No score for a queen if the square is attacked by...
              not (type Piece == 5 and Square attackedby [rbn]) // queen
              // ... else score if the square is attacked by the piece.
              Score += #{Square attackedby Piece}
              // Bishops score an xray through a queen.
              if type Piece == 3   // bishop
                then Score += #{xray (Piece Q Square)}
              // Rooks score an xray through a queen or rook.
              if type Piece == 4   // rook
                then Score += #{xray (Piece [QR] Square)}
            } else false
      // Return the score.
      Score
    }
    
    wMobility = Mobility()
    bMobility = reversecolor Mobility()
    
    // Match the side with an overwhelming advantage.
    // The lower bounds and relative scores are somewhat arbitrary,
    // but the positions matched speak for themselves... the side
    // flagged with an advantage is immediately apparent even on
    // casual inspection.
    
    function Qualify(CenterControlA CenterControlB
                     SquareControlA SquareControlB
                     SpaceEvalA     SpaceEvalB
                     MobilityA      MobilityB) {
      12 < CenterControlA > CenterControlB*2  and
      8 < SquareControlA > SquareControlB*2  and
      2000 < SpaceEvalA > SpaceEvalB*3/2  and
      MobilityA > MobilityB*3/2
    }
    
    if Qualify(wCenterControl bCenterControl
               wSquareControl bSquareControl
               wSpaceEval     bSpaceEval
               wMobility      bMobility)
      then comment("CQL White advantage::  ")
      else
        if Qualify(bCenterControl wCenterControl
                   bSquareControl wSquareControl
                   bSpaceEval     wSpaceEval
                   bMobility      wMobility)
          then comment("CQL Black advantage::  ")
          else false
    
    // For matching positions, dump the scores and evaluations.
    
    comment("center control: " wCenterControl "  " bCenterControl)
    comment("square control: " wSquareControl "  " bSquareControl)
    comment("space: " wSpaceEval "  " bSpaceEval)
    comment("mobility: " wMobility "  " bMobility)
    

  • While this is obviously a compute-intensive query, there are ways of mitigating the downside of executing the search on a very large database.

    First trim the base down to a reasonable size with the following snippet. The resulting base will contain approximately 10% of the larger base, but will contain 100% of the games that would have matched the compute-intensive monster above. Queries run against the smaller base will obviously execute much more quickly.

  • cql()
    power [QRBNqrbn] > 45  [Pp] >= 12
    Center = d-e4-5
    
    function CenterCalc() {
      Score = 0
      // For each piece, tally the number of center squares attacked by
      // that piece and add a bonus if the center is occupied by the piece.
      piece Piece in [QRBNP]
        Score += #{Center attackedby Piece} + #{Center & Piece} * 2
      Score
    }
    
    wCenterControl = CenterCalc()
    bCenterControl = reversecolor CenterCalc()
    
    12 < wCenterControl > bCenterControl*2  or
    12 < bCenterControl > wCenterControl*2
    

Examples for the problem domain

The construction of queries for compositions in the problem domain is significantly more complex than anything we've covered thus far, mainly as a consequence of a game tree laden with variations. To assist in easing the transition, we offer a companion tutorial titled Game Tree Traversal in a CQL Universe.

The examples in this section are primarily aimed at the direct mate, with a wealth of material to cover in that category alone. Most of the examples are inspired by the various tutorials with links in the See also section below.

Each of the CQL queries given in this section will match a fair sample of the problems found in the databases that are included as a resource for this tutorial. Those databases include solutions generated by the Scid++ project's problem solving extension.

We should note that we've extended CQL's header to include two pseudo-parameters that may be employed by many of the examples of this section:

  • The nolinearize pseudo-parameter turns off linearization for the line filter. This is sometimes convenient if we wish to match moves traversing multiple variations from a single position.
  • The alwayscomment pseudo-parameter disables smart comments so that we can tag each variation matched by a line filter with a comment.
These pseudo-parameters mirror the functionality of existing CQL command line switches, each of which is currently unsupported but is nonetheless quite functional. The Scid++ CQL search facility strips the parameters out of the CQL header and converts the parameters to their respective command line switches before launching the CQL sub-process.

With regard to the alwayscomment parameter, Lewis Stiller points out one potential problem area and a solution:

One issue with using -alwayscomment is that it will not work if you wanted to comment every move in your line, with say different comments (as is done, say, in turton.cql).

The general way to handle the issue of commenting every variation, but also deriving the benefit of smart comments, is to wrap the line in an echo, and modify the final filter of the line only to match if its current position equals the target of the echo, e.g.:

echo (source target) {
  mate // limit target to mate here for efficiency
  source:line // evaluate the line at the source, making sure it ends at target
    --> move primary comment ("first ply")
    --> Checker = move to . from [qrb] secondary comment ("second ply")
    --> check and move to between(Checker K) primary comment ("third ply")
    --> {currentposition==target  mate  comment("CrossCheck")}
}

Direct play

Cat description...

  • The thematic cross-check
  • Generally speaking, the cross-check is a tactic in which one side blocks a check by interposition and gives check with the same move. In the case of a compositional theme, the cross-check typically gives mate by discovery.

    We can have any number of different flavors of this theme. We'll first demonstrate a query matching the generic pattern and then we'll give a couple of queries designed to match a very specific cross-check pattern.

  • // The thematic cross-check - generic.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    ccCount = 0
    line  // Count the number of cross-checks by interposition.
      --> move primary  // the key
      --> Checker =? move to . from [qrb]  // must be a line piece giving check
      --> check and move to between(Checker K)  // the interposition gives...
      --> {mate  ccCount += 1  comment("CrossCheck")}  // ... mate by discovery
    
    actualCount = 0
    line  // Count the total number of lines in actual play.
      --> move primary --> not move null --> move primary
      --> mate and actualCount+=1
    
    // A lower bound on thematic play and thematic play outweighs byplay.
    ccCount >= 4  and  actualCount < ccCount * 3 / 2
    
  • Note that the mate is necessarily by discovery, else the interposing [and presumably mating] piece could be captured.


    Next we'll consider a cross-check theme wherein Black gives check leaving a [previously guarded] mating candidate unguarded (either by abandoning the guard or by interfering with the guard). White answers by interposing a piece, mating by discovery with the [now] unguarded piece.

  • // The thematic cross-check - abandoning the guard.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    Count = 0
    line
      --> move primary  // the key
      --> {
            // The set of mating aspirants with a threatened discovery in place.
            Maters =? ray diagonal (k A B) | ray orthogonal (k A R)
            // The guards of the mating aspirants.
            Guards =? a attacks Maters
            // Every mating aspirant is guarded.
            Maters > 2 and square all Mater in Maters {Guards attacks Mater}
            // The line piece giving the first check.
            Checker =? move to . from [qrb]
          }
      --> {
            check
            // At least one mating candidate is no longer guarded.
            Unguardeds =? square Mater in Maters {not Guards attacks Mater}
            // The interposing piece moves to block the check.
            move to between(Checker K)
          }
      --> {
            mate
            // Mate is by discovery and is by our now unguarded aspirant.
            Unguardeds & ((A & ~move to . previous) attacks k)
            Count += 1  comment("CrossCheck")
          }
    Count > 2
    
  • We establish a lower bound on the number of matching lines and on the number of mating candidates, which are criteria for a more interesting composition.

    The query matches a problem by Davidenko (Shakhmaty v SSSR - 1986), where the guard of the mating piece is sometimes abandoned and sometimes interfered with. We give a slice of the solution tree showing the cross-checks in actual play following the key.

  •                       ┌ -- ──── Nb6#
                          ├ Qg7+ ── Be7# CrossCheck
                          ├ Qe5+ ── Bd6# CrossCheck
           ┌ Kc7! threat ─┤
           │              ├ R4g7+ ─ N6e7# CrossCheck
           │              ├ Bf4+ ── Ne5# CrossCheck
           │              └ R8g7+ ─ N8e7# CrossCheck
    

  • Finally, we'll search for a cross-check theme with a knight wheel at the crux. Certain aspects of thematic play get twisted about with this pattern. The initial check by black is necessarily by discovery (though it could be a double check), with the knight repeatedly clearing the same line. And the cross-check by white is not [necessarily] by interposition.

    It should surprise no one that a fully-wheeled cross-check theme is either very rare or perhaps has never been composed (we can't find one). Thus, we search for [a range of] partial wheels with each knight move leading to a cross-check with mate, and where every white response is unique.

  • // The thematic cross-check - with wheeling knight.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    line
      --> move primary  // the key
      --> { // actual play
            Actual = currentposition
            // Eliminate from consideration any actual play with duals.
            not find {terminal and (depth > parent:depth and
                        not parent:move null previous)}
            // Iterate over every black knight on the board.
            piece Knight in n {
              Moves = 0 // Count the total number of knight moves.
              // Match a range of wheels, where each knight move has a
              // unique white response.
              Found = find 4 8 {
                terminal
                // The previous position is given by a knight move with check.
                parent:{check
                        move from Knight previous
                        comment("Knight at " parent:Knight)}
                Moves += 1
                // Is the knight move followed by a unique white response?
                not echo(source target) {
                  terminal and ancestor(Actual currentposition)
                  parent:move from Knight previous
                  // Only the knight move is different... disqualifying.
                  ~(source & target) == 2
                } // echo
                comment("CrossCheck")
              } // find
              // All knight moves have a unique white response.
              Found == Moves
            } // piece
          } // compound
    
  • We eliminate solutions with duals just to make our counting job easier (threat lines with duals being irrelevant). And the use of the echo filter is, perhaps, not the most efficient allocation of CPU cycles, but it feels more natural in detecting changes in board position.

    The query matches a problem by Thorsson (Torneo Olímpico - 1936), in which promotion play also shows up in the thematic pattern. The diagram depicts the board after the key move.

  •                      ┌ -- ──────────────── Bd5#
                         ├ Kf7 ─────────────── Nfd6#
                         ├ Nb6+ Knight at d7 ─ cxd8=N# CrossCheck
           ┌ Nc8! threat ┼ Nc5+ Knight at d7 ─ Nfd6# CrossCheck
           │             ├ Ne5+ Knight at d7 ─ Nd4# CrossCheck
           │             ├ Nf6+ Knight at d7 ─ exd8=N# CrossCheck
           │             └ Rb6 ─────────────── cxd8=N#
           ├ Bd5+? ─────── Qxd5+!
           │             ┌ Qxd8 ────────────── Bd5#
           ├ cxd8=N+? ───┤
           │             └ Rxd8!
     Start ┤
           │             ┌ Be7 ─────────────── Qxe7#
           ├ e8=Q+? ─────┤
           │             └ Rxe8!
           │             ┌ Be7 ─────────────── Rxe7#
           ├ e8=R+? ─────┤
           │             └ Rxe8!
           │             ┌ Qxc7 ────────────── Bd5#
           │             ├ Qb6 ─────────────── Bd5#
           │             ├ Qxa6 ────────────── Bd5#
           └ -- ─────────┤
                         │                   ┌ Bxd5#
                         ├ Qd5+ ─────────────┤
                         │                   └ cxd5#
                         └ Rb6 ─────────────── cxd8=N#
    

  • The thematic self-block
  • The key move forces black to block flight squares in the king's field, perhaps eliminating the immediate threat but allowing an alternate or a set mate. The self-block may be of an outright flight square or of a square previously guarded by the mating piece.
  • // The thematic self-block.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    Count = 0
    line
        --> move primary  // the key
        --> {
            // Eliminate from consideration any actual play with duals.
            not find {terminal and (depth > parent:depth and
                        not parent:move null previous)}
            // A friendly moves adjacent to the king in actual play.
            Flight =? move to (. attackedby k) from ~k  comment("SelfBlock")
            }
        --> {
            piece Mater = move from .
            // Either the block is of an outright flight square or the
            // block is of a square previously guarded by the mating piece. 
            parent:{move from k to Flight legal or Mater attacks Flight}
            // The mating move is not a capture of the blocking piece.
            not move from Mater capture Flight
            Count += 1
            }
    Count >= 6
    
  • We narrow the matching problems to those with at least six self-blocks in the solution. Note that we need not terminate the line of play, as we've already established that the problem is mate-in-2.

    The query matches a composition by Fleck (Nepszava - 1939), which was awarded 1st prize in the competition.

    Inspecting the solution, we can see that the key's threat also covers both of the black king's flights, adding to the fun. And that even the other white queen on the board gets some action. Nice.

  •                       ┌ -- ────────────── Qxd5#
                          ├ Rxc5 SelfBlock ── Rxe4#
                          ├ Rdxe5 SelfBlock ─ Qc4#
                          ├ Rgxe5 SelfBlock ─ Be3#
           ┌ g8=Q! threat ┤
           │              ├ Rxg8 ──────────── Rxd5#
           │              ├ dxe5 SelfBlock ── Ne6#
           │              ├ dxc5 SelfBlock ── Nc6#
           │              └ Nxc5 SelfBlock ── Qb2#
           ├ Ne6+? ──────── Kxe5!
           ├ Rxe4+? ─────── Kxc5!
     Start ┼ Rxd5+? ─────── Rxd5!
           ├ Nc6+? ──────── Kxc5!
           │              ┌ Rxc5 ──────────── Rxe4#
           │              ├ Rgxe5 ─────────── Be3#
           │              ├ Rxg7 ──────────── Rxd5#
           │              ├ Rg6 ───────────── Rxd5#
           └ -- ──────────┼ dxe5 ──────────── Ne6#
                          ├ dxc5 ──────────── Nc6#
                          ├ f5 ────────────── Rxd5#
                          ├ fxe5 ──────────── Ne6#
                          └ Nxc5 ──────────── Qb2#
    

  • The thematic self-pin
  • It's not unusual to find a self-pin in a solution, but it is fairly rare to encounter a problem with the self-pin as the theme. The query is simple and to the point, with the king stepping into the pin followed by the mating piece moving into the attack field of the pinned piece.
  • // The thematic self-pin.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    Count = 0
    line
        --> move primary  // the key
        --> {
            // Eliminate from consideration any actual play with duals.
            not find {terminal and (depth > parent:depth and
                        not parent:move null previous)}
            // The king moves into the pin.
            move from k to ~k  comment("SelfPin")
            }
        --> {
            // The mating piece is attacked by the pinned piece.
            move to (. attackedby (pin through a))
            Count += 1
            }
    Count >= 4
    
  • The pin filter with a leading through parameter yields a set representing the square(s) occupied by the pinned piece(s). The mating piece must move onto a square that is attacked by one off those pieces.

    Also note that the expression — stipulating that the king moves into the pin — at the same time guards against matching a null move (which is technically a move by the king).

    The query matches a composition by Allison (Manchester Guardian - 1953) that is not exactly a great challenge to the solver. But it does have a certain charm and elegance about it, given the symmetry in the position and in the solution, which, by the way, also qualifies as a starflight.

  •                      ┌ Kxc4 SelfPin ─ Ne3#
                         ├ Kxe4 SelfPin ─ Nc3#
           ┌ Re4! block ─┤
           │             ├ Ke6 SelfPin ── Nf4#
           │             └ Kc6 SelfPin ── Nb4#
           │             ┌ Ke6 ────────── Nf4#
           ├ N1b2? block ┼ Kc6 ────────── Nb4#
           │             └ e4!
     Start ┤
           │             ┌ Kxc4 ───────── N1b2#
           ├ Rxe5+? ─────┼ Kc6 ────────── Nb4#
           │             └ dxe5!
           │             ┌ Ke6 ────────── Nf4#
           ├ Rxc5+? ─────┤
           │             └ dxc5!
           │             ┌ Ke6 ────────── Nf4#
           └ -- ─────────┤
                         └ Kc6 ────────── Nb4#
    

  • The starflight theme
  • The key to this pattern of play is typically a blocking move, forcing the opposing king to step into each of his diagonal flight squares (actual play), where mate is to follow. Being most interested in pure thematic play, we eliminate from consideration any solutions with byplay.
  • // The starflight theme.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    line
      --> move primary  // the key
      --> currentposition:{
            // The king moves to all four diagonal flights.
            (move to (diagonal 1 k) from k  comment("Flight")) == 4
            // The king makes only those four moves.
            (move to ~k from k) == 4
            // Only the king makes a move.
            not move from ~k
            // All mates are by the same knight.
            //N == 1 and not find {terminal  k attackedby ~N}
          }
    
  • We disable linearization to allow for a tallying of all the moves emanating from the one position.

    The composition by Hartong (The Observer - 1920) is fairly typical of the starflight. Again, we have a board with some appealing symmetry and a key that is within reach of the novice.

    The solution tree has been sliced to eliminate virtual play.

  •                     ┌ Kd4 Flight ─ Nf3#
                        ├ Kf4 Flight ─ Bc7#
           ┌ Ba5! block ┤
           │            ├ Kf6 Flight ─ Bc3#
           │            └ Kd6 Flight ─ e5#
           ⁓
           │
     Start ┼
           │
           ⁓
           │            ┌ Kd4 ──────── Nf3#
           │            ├ Kf4 ──────── Nd3#
           └ -- ────────┤
                        ├ Kf6 ──────── Bc3#
                        └ Kd6 ──────── Bg3#
    
  • Uncomment the last line of the second constituent of the line filter to match what might strike one as an unlikely pattern. And, in fact, the pattern is rare and the matching problems all derive of the same idea.

  • Unpinned and unleashed
  • We rarely see a composition with multiple serial pins in the starting position, but there are a few. The problem is usually a block, with black [usually] forced to interpose a piece in each of its own pins, allowing the unpinned white piece to mate. Of course, the serial pin prevents the black pinning piece from capturing the pinned white piece, which would be bad form in any event.

    We'll search for problems with at least two serial pins, each of which is broken, with the unpinned piece moving to mate. Matching problems are sorted by the number of serial pins.

  • // Unpinned and unleashed.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    Pinned =? pin through A
    serialPins =? pin through (pin from a)
    
    sort "Serial count" serialPins >= 2
    comment("White pinned at:" Pinned " Black pinned at:" serialPins)
    
    line
      --> move primary from ~(K|Pinned)  // the key
      --> {
          hascomment "block"
          // All of the pinned pieces get their shot at mate.
          piece all Piece in Pinned
            find all {terminal  Piece attacks k  comment("Unleashed")}
          }
    
  • We stipulate that the key may not be by the king (breaking the pins) or by one of the pinned white pieces. The latter stipulation is something of an artificial requirement imposed by the semantics of the piece filter. The set over which the piece filter iterates is a set of squares, not a set of pieces. If there is no piece on a square when the filter is evaluated, we'll get some unintended results.

    The composition by Carra (8th American Chess Congress - 1921) has three serial pins (White pinned at:[b5,c6,d7] Black pinned at:[b4,e4,f7]) and a blocking key.

  •                       ┌ Qxa4 ── Nxd6# Unleashed
                          ├ d5 ──── Qxc5# Unleashed
           ┌ a3! block ───┤
           │              ├ Nb6 ─── Nxd6# Unleashed
           │              └ Nc7 ─── Nb6# Unleashed
           ⁓
           │
     Start ┤
           │
           ⁓
           │              ┌ d5 ──── Qxc5#
           │              │       ┌ Na3#
           └ -- ──────────┼ Nb6 ──┤
                          │       └ Nxd6#
                          └ Nc7 ─── Nb6#
    

  • Thematic self-interference
  • The key presents a threat. In answer to that threat, every defense amounts to interference with a friendly's ability to interpose the mate. We look for solutions having the number of interfering lines within a given range, and then sort the matching problems by that number.
  • // Thematic self-interference.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    Start = currentposition
    Terms = 0
    line
      --> move primary // the key
      --> {
          hascomment "threat"
          sort "Interfering lines"
            {find 4 100 {
              // Ignore the threat line.
              terminal and not parent:move null previous
              Terms += 1
              Mater = A attacks k
              Interferer = parent:{move to . previous}
              // Check each line piece for interference with the defense
              // of the king.
              piece Piece in ([qrb] & ~Interferer) {
                // Get the square on which the piece might have interposed.
                Interpose = Start:{between(Mater k) attackedby Piece}
                // Has the piece been interfered with?
                ray(Piece Interferer Interpose)
                comment("Interference of " Piece " at " Interferer)
              } // piece
            }} == Terms
          } // compound
    
  • To better understand the query, it probably helps to work backwards from the ray filter. From the terminal position, we're looking for the alignment of three squares: 1) the square occupied by the piece that has been interfered with, 2) the square occupied by the interfering piece, and 3) the square on which the mate might have been interposed, but for the interference.

    We obtain these squares by examining three different positions. The squares for items one and two are trivial. They are the candidates for the interferee and the interferer. Item three requires some explanation. We cannot determine the would-be interposing square until we know the mating square. We can only know the mating square from the terminal position, but we can only know that a line piece might have interposed (but for the interference) from the starting position. The interferee must have had a clear line of attack intersecting with the mating line of attack from that position.

    So, from the terminal position we acquire the mating square (Mater = A attacks k) and determine that from the starting position the interference candidate (bound to the Piece variable) had a clear line of attack to an interposing square (Start:{between(Mater k) attackedby Piece}). Then, if the candidates for the Interferee, the Interferer, and the Interposition are all aligned (ray(Piece Interferer Interpose)), we have our match.

    Note that if we stipulate that the number of positions matched by the find filter is equal to the number of terminal positions, we have effectively eliminated solutions with byplay.

    The composition by Golubev (Moscú-Leningrad - 1933) depicted in the diagram takes the theme of interference well beyond the search criteria given above. We just stumbled onto a little bonus.

  •                      ┌ -- ──── Nf7#
                         ├ Ne4 ─── Qe3# Interference of rb4 at e4
           ┌ Qd4! threat ┼ Bxd4 ── Rg3# Interference of rb4 at d4
           │             ├ Rxd4 ── Be7# Interference of bc3 at d4
           │             └ e5 ──── Qd8# Interference of bc3 at e5
           │             ┌ Rf4 ─── Qxf4#
           ├ Qxd2+? ─────┤
           │             └ Bxd2!
           ├ Qd8+? ─────── Bf6!
     Start ┼ Qe7+? ─────── Bf6!
           │                     ┌ Qxe6#
           │             ┌ Kf6 ──┤
           ├ Nf7+? ──────┤       └ Qd8#
           │             └ Kf4!
           │             ┌ Bd4 ─── Rg3#
           │             │       ┌ Qe7#
           │             ├ Rd4 ──┤
           └ -- ─────────┤       └ Be7#
                         │       ┌ Qd8#
                         └ e5 ───┤
                                 └ Qe7#
    

  • The four corner block
  • Following a blocking key, at least four of the defenses in actual play are met by a mating move to a corner, and each of the four corners is covered. The mating piece is not necessarily the piece in the corner.

    We're tempted to take a brute force approach to the query, which leaves us something short of being esthetically pleased. Repetitive, unimaginative and ugly.

  • // The four corner block.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    line
      --> move primary // the key
      --> hascomment "block" and
          {
          find { terminal and move to a1 previous }
          find { terminal and move to a8 previous }
          find { terminal and move to h1 previous }
          find { terminal and move to h8 previous }
          }
    
  • Reducing the four find invocations to one, we're feeling better about things. But...
  • // The four corner block.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    line
      --> move primary // the key
      --> hascomment "block" and
          find 4 {terminal  move to [a1,a8,h1,h8] previous}
    
  • ... there's a problem. The move filter allows any number of matches to any or all of the squares given, which would only accidentally match a solution with moves to all four.

    What we want is to find four matches where each of the four moves is to a unique corner. We borrow an idea from an online example to achieve our ends, maintaining a record of corner squares found and ensuring that any tested square is unique.

  • // The four corner block.
    cql(quiet variations alwayscomment)
    initial  wtm  result 1-0  player black "#2"
    
    // From two-piece-cycle.cql.
    function Unique(Square Set) {
      not Square in Set  
      Set = Square | Set // insert Square into Set 
    } 
    
    Corners = ~.  // the empty set
    line
      --> move primary // the key
      --> hascomment "block" and
          find 4 {
            terminal
            Unique(move to [a1,a8,h1,h8] previous  Corners)
            comment("Corner")
          }
    Corners == 4  // superfluous test
    
  • Note that we can measure the number of unique corner squares by either of two methods: 1) as a range given for the find filter, or 2) as a measure of the cardinality of the Corners set. Either measure is sufficient by itself.

    The composition by Kuznetsov (64 Shakhmatnoe obozrenie - 1996) was awarded an special honorable mention, with a solution that realizes the four corner theme as a starflight.

  •                      ┌ Kc4 ─── Na1# Corner
                         ├ Kxe4 ── Qh1# Corner
           ┌ Rg5! block ─┼ Kxe6 ── Bh8# Corner
           │             │       ┌ a8=Q# Corner
           │             └ Kxc6 ─┤
           │                     └ a8=B#
           │                     ┌ a8=Q#
           │             ┌ Kxc6 ─┤
           ├ Na1+? ──────┤       └ a8=B#
           │             └ Kxe4!
           │             ┌ Kc4 ─── Na1#
           │             ├ Kxe4 ── Qh1#
     Start ┤             │
           ├ Rf6? block ─┤       ┌ a8=Q#
           │             ├ Kxc6 ─┤
           │             │       └ a8=B#
           │             └ g5!
           │             ┌ Kc4 ─── Na1#
           │             │       ┌ a8=Q#
           ├ Qxg6? block ┼ Kxc6 ─┤
           │             │       └ a8=B#
           │             └ Kxe4!
           │             ┌ Kc4 ─── Na1#
           └ -- ─────────┤       ┌ a8=Q#
                         └ Kxc6 ─┤
                                 └ a8=B#
    

  • The mutate
  • When the composition is a complete block, the key might change one or more of the set mates for a given defense, in which case the problem is referred to as a mutate. The following query matches a total mutate, where all of the set mates are changed. Matching games are sorted by the number of mutates.
  • // The mutate.
    cql(quiet variations alwayscomment)
    initial wtm
    
    // Compare two moves.
    function SameMove(X Y){
      X:{move from . previous} == Y:{move from . previous}
      X:{move to . previous} == Y:{move to . previous}
      // Account for promotions.
      X:{type move to . previous} == Y:{type move to . previous}
    }
    
    // Recover actual play and count the number of lines.
    line
      --> move primary  // the key
      --> {hascomment "block" and Actual = currentposition
           ActualCount = find all {mate and move primary previous}}
    
    // Set play with nested actual play traversal.
    line
      --> move null
      --> 3 < sort "Mutate total" {
           SetCount = 0
           // For every line in set play...
           find all { mate  ply == 3  // terminal and #2
             move primary previous  // eliminate 'move to any' secondaries
             SetCount += 1
             C = currentposition
             comment("id:" positionid)
             // ... find a mutate in actual play.
             Actual:find { mate
                      move primary previous
                      // The mating moves differ...
                      not SameMove(currentposition C)
                      // ...for a given defense.
                      SameMove(parent C:parent)
                      // The terminal positions differ.
                      currentposition & C != .
                      comment("Match " C:positionid)}
           }
          } == SetCount == ActualCount < 7
    
  • Stipulating that the terminal positions differ eliminates the following scenario. The problem is a complete block. The key move is by the mating piece and is a waiting move. For any given defense, the mating piece moves to the same square as in set play. Thus, the mating moves differ, but the terminal positions are identical. Technically, such changed play qualifies as a mutate. Esthetically, not so much.

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • The Babson (preliminary)
  • For decades the Babson Task was widely considered to be the Holy Grail of the chess problem domain. Master composers declared that the problem (a quadruple echo promotion) would never be satisfactorily solved as a direct mate. In 1983, Leonid Yarosh finally composed a Babson that was — at the time — considered to be one of the greatest and most elegant accomplishments in chess composition.

    Definitions of the Babson Task and expositions on the long history of the challenge abound on the net and we'll not repeat that information here, but Tim Krabbé presents a very nice review of the solution to Yarosh's composition that is well worthy of review.

    The following generic (and rather simple) query satisfies the minimal criteria for the Babson. Other thematic criteria may be imposed on top of the generic to ferret out specific lines in the main play or even in the byplay, as demonstrated further below.

  • cql(quiet variations alwayscomment)
    initial wtm
    
    line
      --> move primary // the key
      --> {
           line --> move promote Q --> move promote Q and not child(1)
           line --> move promote R --> move promote R and not child(1)
           line --> move promote B --> move promote B and not child(1)
           line --> move promote N --> move promote N and not child(1)
          }
    
  • Note that the trailing constraint given for each nested line — that there is no secondary child correlated with the echoed promotion — enforces the requirement that the given promotion is the only promotion (or line for that matter) that satisfies the stipulation. In other words, if black promotes to a bishop then white may only respond with promotion to a bishop, whereas promotion to a queen, for example, must fail to satisfy the stipulation (no small detail).

    We've executed the query against a problem database of #4 compositions with solutions provided by Popeye, and we've provided a selection of matching Babsons as a resource on the Scid++ SourceForge site. The solutions for those Babsons omit virtual play and set play since the game trees for the full solutions are quite large. For those interested [especially] in a solution's virtual play — which can be instructive — the full solution may be generated and imported to Scid through the problem solving dialog on the project's solver branch.

    The Babson problem database referred to above includes many of the compositions collected in Krabbé's article on the Sons of Babson. That article does a nice job of highlighting the important characteristics of the most notable Babsons on the historical timeline.

    Finally, Peter Hoffmann has produced a rather thorough treatise on the current state of Babson affairs. The paper is written in German but the solutions are accessible to the non-linguist and Google does a fair job of translating the commentary to English.


Help play

Cat description...

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

Self/Reflex play

Cat description...

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

  • Example template
  • Explanation...
  • cql() true

Contacting

Questions and comments relating to this tutorial, or to CQL in general, may be directed to the Scid++ project's CQL mailing list at SourceForge.

Credits

This tutorial is Copyright (c) 2019-2020 Lionel Hampton. The Chess Query Language was developed by Gady Costeff and Lewis Stiller. The Scid vs PC project, which has integrated the version 5 CQL engine, is managed by Steven Atkinson.

Thanks go to Costeff and Stiller for their innovative work on CQL, for their considerable assistance to the author of this tutorial and for their contributions to the success of the integration of the CQL engines with the Scid vs PC application.