This is the second part of the main line of the CQL by Example document suite.
Let us state up front that we haven't a clue what we're doing. We're just groping around hoping to get lucky. When we go looking for patterns in solution trees with scores of variations with their threats and blocks and duals and cooks and patterns expressed across phases, we're in unexplored territory. According to the online reference, the language was never intended to go where we're going. So there you have it. Beyond this line there be dragons.
What we can promise is an entertaining ride, whether we ultimately arrive at our destination or not. What destination? CQL in the problem domain. The grand experiment. On the other side of that broad and treacherous sea.1
Each of the examples to follow will match at least a handful of problems in one or more of the problem databases found on the project site. The full solution trees have been generated using the Scid++ solving facility, translating Popeye solutions to native format.
We should mention that we've extended the CQL header to include two pseudo-parameters:
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")} }
Cat description...
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
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
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
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
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. 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
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. 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
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. 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} }
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#
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")} }
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. 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
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#
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 } }
// 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}
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
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#
A block in which the key changes one or more of the set mates for a given defense. The following query matches a total mutate, where all of the set mates are changed. Matching games are sorted by the number of mutates.
Nested interphase traversal...
// The mutate. cql(quiet variations alwayscomment) initial wtm result 1-0 player black "#2" // 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} } line --> move primary // actual play --> { hascomment "block" and Actual = currentposition not find {terminal and depth > parent:depth} // no duals Lines = find all {terminal} // count the lines currentposition:{Defenders = move from a} // the defending pieces } Defenders > Lines / 2 comment("Defenders:" Defenders) Abort = 0 line --> move null // set play --> 4 <= sort "Mutate lines" { not find {terminal and depth > parent:depth} // no duals // For every line in set play... find all { terminal comment("id:" positionid) T = currentposition // ... find a mutate in actual play. if not Actual:find { terminal // For a given defense... SameMove(parent T:parent) // ... the mating moves differ. not SameMove(currentposition T) // The terminal positions differ. currentposition & T != . comment("Mutate:" T:positionid) } then Abort = 1 } // find all } == Lines <= 8 // the compound returns the last evaluated filter Abort == 0
The Abort flag gives us an indication that some line(s) in set play did not have corresponding mutate(s) in actual play. Since we can't actually abort the outer traversal, we set an artificial switch to indicate that we would have if we could have. While there are other ways of achieving the same end, this may be the clearest.
Every line in actaul play represents a mutate. Range is given on the compound filter, which retuurns the result given by the last filter of the compound. Could as easily have given the range as parameters to the find all filter, but as given we receive a bit of an insight into the language.
Finally, 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.
The matching composition by Fleck (Magyar Sakkvilag - 1943) was awarded an Honorable Mention (not for the total mutate). All eight set mates are changed in actual play. The full solution reveals a Stocchi theme expressed across three phases.
┌ Nxh2 ─ Nfg3# Mutate:20 ├ Ng3 ── Nfxg3# Mutate:22 ├ Ne3 ── Nfg3# Mutate:24 ├ Nd2 ── Nfg3# Mutate:26 ┌ Nf5! block ┤ │ ├ d2 ─── Qc2# Mutate:28 │ ├ g3 ─── f3# Mutate:30 │ ├ exd4 ─ Qxd4# Mutate:32 │ └ e6 ─── Nxd6# Mutate:34 ⁓ │ Start ┤ │ ⁓ │ ┌ Nxh2 ─ Ng3# id:20 │ ├ Ng3 ── Nxg3# id:22 │ ├ Nxe3 ─ Ng3# id:24 │ ├ Nd2 ── Ng3# id:26 └ -- ────────┤ ├ d2 ─── Bc2# id:28 ├ g3 ─── Bf3# id:30 ├ exd4 ─ Rxe7# id:32 └ e6 ─── Nf6# id:34
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
cql (variations alwayscomment quiet) initial Purity = 0 player black ~~ "^#(\d).*" and StipDepth = int \1 Key=--(primary currentposition) //Key:{path focus p (--=▲) {3,}} local dictionary TTries path keepallbest -- secondary and not nullmove //and originalcomment "block" Try=∙ Key:{path keepallbest -- * -- Try:{pieceid to} == pieceid to and Try:{type to} == type to and Try:{to} == to and parent:{currentmove --=▲} IdP=∙ IdL=IdP Try:{ --=▲(originalcomment "$1") path keepallbest (-- not originalcomment "$1" IdC=∙ IdP:{Found=find{ IdC:{pieceid to} == pieceid to and IdC:(to) == to and distance(IdP ∙) == distance(Try IdC) comment("(match)") } ancestor(IdL Found) IdL=Found } if mate or Found:mate Try:{comment("(thematic)")} ) {2,} TTries[IdP] = Try //message(IdP ":" Try) } // Try } // Key #TTries >= 1 Key:{ LCount = 0 find all { terminal FCount = find all <-- TTries[∙] if LCount < FCount then LCount = FCount } find all { terminal if LCount == 1 then find <-- { TTries[∙] parent:{piece T = to TS = to TT = typename to} TTries[∙]:{ T--=▲(originalcomment "$1") not T--TS=TT(originalcomment "$1") if Purity > 0 countmoves --=▲(originalcomment "$1") == countmoves --(originalcomment "$1") } } if LCount == find all <-- TTries[∙] then find all <-- TTries[∙] and comment("(thematic)") } } //LCount == 1
The matching composition by
┌ Nxh2 ─ Nfg3# Mutate:20 ├ Ng3 ── Nfxg3# Mutate:22 ├ Ne3 ── Nfg3# Mutate:24 ├ Nd2 ── Nfg3# Mutate:26 ┌ Nf5! block ┤ │ ├ d2 ─── Qc2# Mutate:28 │ ├ g3 ─── f3# Mutate:30 │ ├ exd4 ─ Qxd4# Mutate:32 │ └ e6 ─── Nxd6# Mutate:34 ⁓ │ Start ┤ │ ⁓ │ ┌ Nxh2 ─ Ng3# id:20 │ ├ Ng3 ── Nxg3# id:22 │ ├ Nxe3 ─ Ng3# id:24 │ ├ Nd2 ── Ng3# id:26 └ -- ────────┤ ├ d2 ─── Bc2# id:28 ├ g3 ─── Bf3# id:30 ├ exd4 ─ Rxe7# id:32 └ e6 ─── Nf6# id:34
Cat description...
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
Cat description...
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
cql() true
This document is Copyright (c) 2019-2024 Lionel Hampton. The Chess Query Language was originally developed by Gady Costeff and Lewis Stiller.
The diagrams appearing in this document were created with the Scid vs PC application's board screenshot facility. The piece set is included with that application and is credited on the project's site.
1 We're having alot of fun with more than just the one language.