Read XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition Online
Authors: Michael Kay
Having established the list of possible moves, the function then calls
try-possible-moves
to select one of these moves and execute it.
Here is the function. Its parameters are the number of this move (starting at move 2, because the knight's initial position is numbered 1), the state of the board before this move, and the number of the square on which the knight is currently sitting.
select=“tour:list-possible-moves($board, $square)”/>
select=“tour:try-possible-moves($move,
$board,
$square,
$possible-move-list)”/>
Finding the Possible Moves
The next function to examine is
list-possible-moves
. This takes as input the current state of the board and the position of the knight, and it produces a list of squares that the knight can move to. For a knight in the center of the board there are eight possible squares it can move to (as shown in
Figure 20-4
): those squares that are either two columns and one row, or two rows and one column, removed from the current row.
However, we have to consider the case where some of these squares are unavailable because they are off the edge of the board, and we also have to eliminate any squares that have already been visited. The logic I have used is simple, if verbose; it simply examines each of the eight candidate squares in turn:
select=“$square idiv 8”/>
select=“$square mod 8”/>
(if ($row > 1 and $column > 0 and $board[($square - 17) + 1]=0)
then $square - 17 else (),
if ($row > 1 and $column < 7 and $board[($square - 15) + 1]=0)
then $square - 15 else (),
if ($row > 0 and $column > 1 and $board[($square - 10) + 1]=0)
then $square - 10 else (),
if ($row > 0 and $column < 6 and $board[($square - 6) + 1]=0)
then $square - 6 else (),
if ($row < 6 and $column > 0 and $board[($square + 15) + 1]=0)
then $square + 15 else (),
if ($row < 6 and $column < 7 and $board[($square + 17) + 1]=0)
then $square + 17 else (),
if ($row < 7 and $column > 1 and $board[($square + 6) + 1]=0)
then $square + 6 else (),
if ($row < 7 and $column < 6 and $board[($square + 10) + 1]=0)
then $square + 10 else () )”
/>
An observation: not everyone is happy with the idea of writing a single XPath expression that is 16 lines long in the middle of a stylesheet. Some would prefer to write this code using XSLT instructions, using
Another approach would be to try and capture all the logic in a single calculation, as follows:
for $r in (-2, -1, +1, +2),
$c in (-(3-abs($r)), +(3-abs($r)))
return
(($row+$r)*8 + ($column+$c) + 1)
[($row+$r = 0 to 7 and $column+$c = 0 to 7]
[$board[($row+$r)*8 + ($column+$c) + 1] eq 0]
So having found the possible moves we can make, we need to select one of them and make it. This is the job of the
try-possible-moves
function.
Trying the Possible Moves
In essence, this function is quite simple. As input it gets the current state of the board, the current position of the knight, the current move, and the list of moves that the knight can make from its current position. If there is at least one move that it can make, then it makes the best move it can find and returns the new state of the board; otherwise, it returns the special value
()
to indicate that it has failed, and that another path needs to be found.
select=“if (exists($possible-moves))
then tour:make-best-move($move,
$board,
$square,
$possible-moves)
else ()”/>