next and previous filters

next and previous let CQL look ahead and look backwards to determine what follows or precedes the current position. Each of these modifies the value of the current position as CQL traverses the positions following or preceding in the PGN file.

next and previous syntax

  next-filter := 'next' opt-range '(' constituent+ ')'
  previous-filter := 'previous' opt-range '(' constituent+ ')'
  constituent := filter
    | filter '*'
    | filter '+'
    | filter '?'
    | '(' filter-list ')'
next and previous each take an optional range parameter, - either 1 or 2 integers denoting a range - and at least 1 constituent.

A constituent can be either a normal filter, or it can be a regular expression using wildcards. (Regular expressions are commonly used to search text, but we extend them to search for chess positions.) The constituents, called the arguments, are enclosed in parentheses and separated by whitespace.

next filter semantics

We begin by discussing semantics when all the arguments are filters.
next (filter1 filter2 ... filtern)
matches the current position if:
  • filter1 matches the current position;
  • filter2 matches the next position following the current position
  • filter3 matches the next position after the next position following the current position
  • ... and so on
Before an argument filter is evaluated to determine whether it matches a position, the current position is set to that position.

If variations is set, then next will also consider positions that move into variations. Otherwise, only mainline positions are considered.

For example, D. Gurgenidze
1.p Polish Chess Federation ty
1985

Left diagram: 8.Qe3+! (current position) 8..Kxe3+ (position 2) 9.Rg3+ (position 3) 9..K-e4 stalemate (position 4 and right diagram)

next (check check check stalemate)
The above study matches following 8.Qe3+! because the current position is check, positions 2 and 3 are also checks, and position 4 is stalemate.

using regular expressions with next

The symbols '*', '?', and '+' have similar meaning, for readers who are programmers, to their use in regular expressions. The rule to remember is this: A regular expression always matches the longest possible sequence of positions

The '*' symbol

'*' means "repeat 0 or more times".

Thus, consider the filter

  next (not check
        check*)
The last 'check' is modified by the '*' so it is repeated 0 or more times. Thus, the expression is equivalent to:
  next (not check) ; repeat 0 times
  or next (not check check) ; repeat 1 times
  or next (not check check check) ; repeat 2 times
  or next (not check check check check) ; repeat 3 times
  ....; and so on forever
Therefore, the next filter matches a position if either:
  • The current position is not a check, or
  • The current position is not a check and the next position is a check, or
  • The current position is not a check and the next position is a check and the following position is a check... and so on.

Because of the rule above about matching longest sequence of position, the actual filter will match the longest possible sequence of checks that it can starting from a not check in the current position.

The '+' symbol

The '+' symbol following a constituent means "repeat 1 or more times". Thus,
	next (move from Q
	      move from [Kk]+
              mate
	     )
	
will match any position from which the next move is a move by the White queen; following which is a sequence of one or more positions from which a King moves; following which there is a position that is mate.

The '?' symbol

The '?' following a constituent means "repeat 0 or 1 times". For example,
	next (move from Q
	      move from k?
	      mate)
means
	next (move from Q
	      mate)
	or
	next (move from Q
	      move from k
	      mate)
That is, either White delivers mate with the Queen, or after White's Queen move, black delivers mate by a King move.

The '()' wildcard symbol

A sequence of filters inside parentheses matches a sequence of consecutive positions that match the filters respectively. This construct is used exclusively with wildcards.

For example, suppose you want to match a white queen move followed by a black move followed by a sequence of White checks by a pawn followed by black king moves followed by mate. You can use this:

  next (move from Q
        btm
        (move from P ; white moves, giving check
         check       ; black to move and is in check
        )+           ; repeat
       wtm           ; white delivers...
       mate)         ; mate to black

counting with next

next is countable when it has a range parameter. It counts the number of matched positions. For example,
next 5 1000 (check*)
will match positions from which, starting at the current position, at least 5 consecutive checks occur.

Because next with a range is countable, it may be sorted on:

   sort next 5 1000 (check*)
The output games can then be sorted based on the longest matched sequence of at least 5 which is just the longest sequence of checks.

Boundary conditions in counting sequences

There is one problem with the code
   sort next 5 1000 (check*)
If there is a sequence of say 10 consecutive checks, then there will be a match on 5 of these. This will make the output difficult to read, with excessive "MATCH" being printed or annotations. It is clearer to require the sequence to start with at the earliest possible move. This can be done using previous, which is like next but backwards:
  not previous (any check)
  sort next 5 1000 (check*)
You can see this technique used in many of the examples of longest sequence searching.

previous

previous is just like next except that it searches backwards. One good thing about previous is that it is the same whether variations is set or not in the CQL parameters: any position has at most one parent, although it can have multiple children.

Using wildcards to look for maneuvers

Wildcards are useful in isolating the particular pieces you're interested in in a maneuver. For example, in turton.cql, there is a next filter with these subfilters:
  next (...
        {not move from $front or $side}*
        move from $side to $criticalsquare
        ...
       )
In this particular theme, without going into details, we are particularly interested in the movements of the pieces $front and $side. We want to track where they go. So the first wildcard operator ensures that that we ignore movements of pieces other than those, and just focus on those pieces.

pitfalls in using previous and next

There are several issues to keep in mind when using these filters.

  1. Always remember that the first filter in the next or previous filter's arguments matches the current position. This can be confusing, since the words 'next' and 'previous' might suggest that the first filter would match the next or previous positions, but they don't.
  2. because next and previous each take arguments that are list of filters separated by whitespace, and because these constituent filters can be fairly complex, it is important to be careful as to which filters are matching a position and which are part of a larger filter. It is easy to write say "mainline check" thinking that this is one filter, when in fact it is two filters that will matched to two successive positions inside an argument list.

    We recommend liberal use of braces to make the code clearer if there is any question. Always begin by testing the simplest possible next or previous filter, then adding in more complex arguments as necessary.

  3. It is important to keep in mind that the current position changes as the next or previous filter matches positions. When that changes, so do all the piece variables.
  4. When variations is set, be extremely cautious using move inside next. That is because the position after the move filter inside the next is not necessarily the position that would have resulted from the move. The move might be in another variation. Use either move previous or just previous to verify where necessary, or try and check the positions of the pieces.
  5. next and previous by default print a lot of annotations. These annotations can be confusing or overwhelming with wildcards, because an annotation will be printed for every matched position. Use silent to silence the annotations.

    Examples