Transform filters
A transform filter takes a single argument filter and transforms it into a new filter. For example, the transform filterflipvertical Ph1-8is a transform filter whose argument filter is
Ph1-8
. The transform filter is equivalent to
Ph1-8 or Pa-8which is to say, it matches a white pawn on the edge of the board.
Each transform filter has an associate set of transforms. A transform is an operation that can change a board position to another (possibly illegal) board position. Transforms also transform filters into new filters. Examples of transforms are the transform that shifts all the pieces in the position up 2 squares; or the transform that reflects a position about the main diagonal. The set of transforms of transform filter always includes the identity transform, which leaves its argument unchanged. (Sometimes we conflate the name of a transform with the name of its associated filter: we might speak of a "shift transform" when we technically mean a "shift transform filter.")
In the above example, the set of transforms associated to flipvertical
has two elements:
- the identity transform
- the transform that reflects all pieces about the vertical bisector of the board
Ph1-8
, resulted in the new filter Ph1-8 or Pa1-8
. That is why
flipvertical Ph1-8
was equivalent to this or
filter, which matches white pawns on the edge of the board.
The possible transform filters and their associated sets of transforms are summarized in th following table:
Name of transform filter | category of transform | transform set (not including identity transform) |
---|---|---|
flipvertical |
dihedral transform | reflection about vertical bisector |
fliphorizontal |
dihedral transform | reflection about horizontal bisector |
flip |
dihedral transform | all 7 rotations and reflections of the board |
flipdihedral |
dihedral transform | (same as flip ) |
rotate90 |
dihedral transform | rotations by 90°, 180°, and 270° |
rotate45 |
rotate45 transform | rotations by multiples of 45° |
flipcolor |
color transform | swap colors followed by reflection about horizontal bisector |
shifthorizontal |
shift transform | shift horizontally |
shiftvertical |
shift transform | shift vertically |
shift |
shift transform | shift orthogonally |
Thus, flipvertical Ba4
is a dihedral transform filter (one which is equivalent to Ba4 or Bh4
. Similarly shift {Pd4 pd5}
is a
transform filter which matches a position in which there is a white pawn blocking a black pawn.
A transform is a function that transforms the board in some way. Example transforms includes reflections about an axis, rotations, and shifts. Each transform filter is associated with a particular set of transforms.
A transform filter matches the current position if Each transform filter is associated with a set of transforms. The set of transforms always includes the identity transform Transform filters can also take a range between the name of the transform filter and the the argument filter, as in:
flip 3 4 [Qq]a8. The range counts the number of transforms for which the filter is true, as explained in more detail below.
Dihedral transforms
Imagine a transparent chess board without any pieces placed at the origin of a coordinate plane, with its sides parallel to the coordinate axes. There are eight rigid transforms to that chessboard that can be performed and still leave it looking the same as the original:- The identity transform. This does nothing, leaves the chessboard as it is.
- The four reflections about the horizontal bisector of the chessboard (the x axis); the vertical bisector of the chessboard (the y axis); and the two diagonals.
- The three rotations: by 90 degree counterclockwise, by 180 degrees, and by 90 degrees clockwise.
Each dihedral transform transforms a square on the chessboard to a new square. For example, the square g6 is transformed into the square g3 by the reflection about the horizontal; into b6 by reflection about the vertical; into c7 by rotation 90 degrees counterclockwise; into b3 by rotation 180 degrees; into f2 by rotation 270 degrees; into f7 by rotation about the main diagonal, and into c2 by rotation about the off-diagonal. Of course, g6 is also transformed into itself by the identity transform.
To apply all dihedral transforms to a CQL filter, preface the filter with the word "flip":
fliphorizontal Pg6 |
flipvertical Pg6 |
flip Pg6 |
[Pg6,Pb6] | [Pg6,Pg3] | [Pg6,Pg3,Pb6,Pc7, Pb3,Pf2,Pf7,Pc2] |
rotate90
creates an or
filter with four clauses
corresponding to rotations of 0°, 90°, 180°, and 270°.
rotate90 Pg6 |
rotate90 Ra1 |
≡ [Pg6,Pc7,Pb3,Pf2] | ≡ [Ra1,Rh1,Rh8,Ra8] |
rotate90
does a 4-fold transform on a position. When combined with fliphorizontal
or flipvertical
it is in fact equivalent to flip
. For example:
flip g6 ≡ rotate90 fliphorizontal g6 ≡ rotate90 [g6,g3] ≡ [g6,g3,b6,b7,b3,f2,f7,c2]
Any transform can be applied recursively to the constituents of a filter. For example,
flipvertical attack (Rg6 K)is equivalent to
attack (Rg6 k) or attack (Rb6 k)Likewise,
rotate90 attack (Rg6 k)is equivalent to
attack (Rg6 k) or attack (Rc7 k) or attack (Rb3 k) or attack (Rf2 k)Directions are also transformed:
rotate90 ray up (R[g6,a1] k)is equivalent to
ray up (R[g6,a1] k) or ray left (R[c7,h1] k) or ray down (R[b3,h8] k) or ray right (R[f2,a8] k)
shift transforms
A shift transform shifts the squares in its single argument filter 0 or more times along some direction. There are three shift filters:- shiftvertical
- shifthorizontal
- shift
shifthorizontal {Kb1 kg6} |
shiftvertical {Kb1 kg6} |
shift {Kb1 kg6} |
Kb1 kg6 or Ka1 kf6 or Kc1 kh6 | Kb1 kg6 or Kb2 kg7 or Kb3 kg8 | Kb1 kg6 or Kb2 Kg7 or Kb3 Kg8 Ka1 kf6 or Ka2 Kf7 or Ka3 Kf8 Kc1 kh6 or Kc2 Kh7 or Kc3 Kh8 |
shiftvertical
shifts its argument 0 or more squares
vertically:
shiftvertical g6 ≡ g1 or g2 or... or g8 ≡ g1-8. Likewise,
shiftvertical [g2,g4]
≡ g1-8
When a square is shifted off the board it normally disappears. Piece
designators with empty square sets eliminate the entire transform:
shiftvertical {Kb1 kg6}
≡
{Kb1 Kg6}
or {Kb2 Kg7}
or {Kb3 Kg8}
There is no downward shift of kg6
because doing so would eliminate the b1
square for the K.
shifthorizontal
works likewise.
shift is equivalent to shifthorizontal shiftvertical
. Using the example above:
shift {Kb1 kg6} ≡ shifthorizontal shiftvertical {Kb1 kg6} ≡ shifthorizontal {Kb1 kg6} or shifthorizontal {Kb2 kg7} or shifthorizontal {Kb3 kg8} ≡ {Kb1 kg6} or {Ka1 kf6} or {Kc1 kh6} or {Kb2 kg7} or {Ka2 kf7} or {Kc2 kh7} or {Kb3 kg8} or {Ka3 kf8} or {Kc3 kh8}This also means that
shift Ka2 ≡ K
shiftvertical
does not alter a file of
8 squares:
shiftvertical {Kd1-8 Ba2}
≡
{Kd1-8 Ba2} or
{Kd1-8 Ba3} or
{Kd1-8 Ba4} or
...
{Kd1-8 Ba8} or
{Kd1-8 Ba1}
which turns out to be equivalent to
{Kd1-8 Ba1-8}. But
shiftvertical {Kd2-8 Ba2}
≡
{Kd2-8 Ba2} or
{Kd3-8 Ba3} or
{Kd4-8 Ba4} or
{Kd5-8 Ba5} or
{Kd6-8 Ba6} or
{Kd7-8 Ba7} or
{Kd8 Ba8} or
{Kd1-7 Ba1}
which is entirely different.
Similarly, shifthorizontal
does not change ranks with
8 squares:
shifthorizontal {Ka-h2 Ba4}
≡ {Ka-h2 Ba-h4}
The shift
transform doesn't change full ranks or files in
the direction of its shift:
shift {Ka-h2 Ba4}
≡
{Ka-h2 Ba-h4} or
{Ka-h3 Ba-h5} or
{Ka-h4 Ba-h6} or
{Ka-h5 Ba-h7} or
{Ka-h6 Ba-h8} or
{Ka-h1 Ba-h3}
Note that any transform applied to a piece designator without
an explicit square qualifier leaves the piece designator unchanged:
shift K
≡ K
and likewise
flip K
≡ K
The flipcolor transform
The flipcolor transform applied to filter is theor
of filter
with the new filter formed from the filter as follows:
- the colors of any piece
designators in
filter
are changed; -
wtm
is changed tobtm
and vice versa; player white
is changed toplayer black
and vice versa;elo white
is changed toelo black
and vice versaresult 0-1
is changed toresult 1-0
and vice versa- All piece designators in filter are reflected about the horizontal bisector of the board.
flipcolor Ba1
≡
Ba1 or ba8
Similarly,
flipcolor {wtm result 1-0 [Pk]a-h2}
≡
{wtm result 1-0 [Pk]a-h2}
or {btm result 0-1 [pK]a-h7}
The rotate45 transform and the cyclic group of order 8
rotate45
is an unusual transform in that it is not allowed to operate on individual squares.
Imagine an old-fashioned analogue clock. We will only focus on the minute hand of that clock.
Moving the minute-hand counterclockwise 45 degrees is the same as moving it back in time by 7.5 minutes. I will call this transform the unit counterclockwise transform. If we apply the unit counterclockwise transform twice, then the minute hand goes back by 15 minutes. If we apply it 8 times, then the minute hand stays unchanged.
Therefore, if we are only allowed to apply the unit counterclockwise transform to the minute hand, there are exactly 8 possible transformations of the minute hand, corresponding to moving the hand back 7.5 minutes, 15 minutes, 22.5 minutes, 30 minutes, 37.5 minutes, 45 minutes, 52.5 minutes, and leaving it unchanged.
These 8 transformations are the so-called cyclic group of order 8.
The transformations apply to a CQL filter as well, as long as the filter doesn't name any specific squares. All such a transformation does is change directions. A direction corresponds to a position of the minute hand: up is 12:00; down is 12:30; northeast is 12:07:30; and so on. So the unit counterclockwise transformation transforms up into northwest; northwest into left; left into southwest; and so on.
Thus, rotate45 filter
takes the or
of all the elements of the order 8 cyclic group applied to the filter.
For example,
rotate45 up 1 K
≡
up 1 K
or northwest 1 K
or left 1 K
or southwest 1 K
or down 1 K
or southeast 1 K
or right 1 K
or north east 1 K
Note that this is exactly the King's field: the set of squares adjacent to the King.
However, we cannot of course apply rotate45
to a filter that names any particular square, because a particular square cannot be
rotated 45 degrees. Thus,
rotate45 Kd3 ; ERROR
Transforms with ranges
If followed by a range, any transform becomes countable and counts the number of transforms of the argument filter that match a position. For example,shift 10 20 [Pp]a4would match positions with between 10 and 20 pawns on the board (although of course it would be much slower than simply writing
Pp 10 20
.
This gives us a compact way to check, say, if at some point in the game all 4 corners of the board are visited by a rook:
initial rotate90 4 next* Ra1Here, next* Ra1 is true if at the current position or in the future, a white rook is on a1. The 4 rotations of this filter are:
rotate90 next* Ra1
≡
{next* Ra1} or
{next* Rh1} or
{next* Rh8} or
{next* Ra8}
Here, however, the 4
following the rotate90
means that each of the 4 elements of the rotation group must match. Thus
rotate90 4 next* Ra1
≡
{next* Ra1
next* Rh1
next* Rh8
next* Ra8}
That is, each of the 4 clauses must be true. (Recall that in a compound filter enclosed in braces each filter must match for the filter to match).
This idea is used to give a compact statement of the problem of finding games in which the same rook visits all 4 corners of the board.
Using ranges to count occurrences of configurations
As another example, consider a configuration where a white queen and a black queen are separated by a single square, with a piece on that square. For example,
Qd4 ne4 qf4
is an example of such a configuration. There are eight possible orientations of the queens: the Q can be below the q, or northeast of the q and so on.
How can we find all games in which at least 5 of these configurations occur, sorted by the number of the configurations?
The simplest way is first to express one of valid configurations, say the one we listed above where the q is two squares to the right of the Q and a piece is between them. This configuration turns out to be:
q on right 1 [Aa] on right 1 QWritten with braces, this expression is:
q on {right 1 {[Aa] on {right 1 Q}}}Suppose the position is as in our example:
Qd4 ne4 qf4
. Will it match?
Well, Q is d4, so this is:
q on {right 1 {[Aa] on {right 1 d4}}}But
right 1 d4
is e4, so this is:
q on {right 1 {[Aa] on e4}There is a
[Aa]
on e4, namely a black knight, so the last expression is just e4
q on {right 1 e4} ≡ q on f4 ≡ qf4So this does in fact match our test position. And this should make clear why it would in fact match the positions where the Q is two squares right of the q separated by a piece. But the key thing to notice is that the
right
direction can be any of the eight compass directions.
Thus, to count configurations where 5 of the eight configurations occur, we stick a rotate45
in front and sort
sort rotate45 5 8 next* q on right 1 [Aa] on right 1 QThis example is taken from Qq-rotations.cql.
If you just want to test for rotations by multiples of 90 degrees, and not the 45 degree rotations, this code might be easier to follow, which tests for rotations of the theme configuration by multiples of 90 degrees: it names explicit squares and does not use direction operators.
rotate90 4 next* shift {Qd4 [Aa]e4 qf4}However, in general
shift
is much slower and less flexible than using the direction operators . This example code is in
Qq-rotations-90-degree.cql
Things to watch out for when using transforms
Using transforms can create code that is concise and easy to understand compared to usingsquare
and piece
. But there are some pitfalls:
- transforms create code that is less flexible and more
difficult to generalize than using
square
andpiece
. Counting and sorting can be more difficult. - transforms can make the automatically generated comments more difficult to understand
- transforms can be slower than using
square
orpiece
.