76 A knight's tour is called \emph{closed}, if the last step in the tour |
76 A knight's tour is called \emph{closed}, if the last step in the tour |
77 is within a knight's move to the beginning of the tour. So the above |
77 is within a knight's move to the beginning of the tour. So the above |
78 knight's tour is \underline{not} closed (it is open) because the last |
78 knight's tour is \underline{not} closed (it is open) because the last |
79 step on field $(0, 4)$ is not within the reach of the first step on |
79 step on field $(0, 4)$ is not within the reach of the first step on |
80 $(4, 4)$. It turns out there is no closed knight's tour on a $5\times |
80 $(4, 4)$. It turns out there is no closed knight's tour on a $5\times |
81 5$ board. But there are on a $6\times 6$ board, for example |
81 5$ board. But there are on a $6\times 6$ board and bigger, for example |
82 |
82 |
83 \chessboard[maxfield=e5, |
83 \chessboard[maxfield=e5, |
84 pgfstyle= {[base,at={\pgfpoint{0pt}{-0.5ex}}]text}, |
84 pgfstyle= {[base,at={\pgfpoint{0pt}{-0.5ex}}]text}, |
85 text = \small 10, markfield=Z5, |
85 text = \small 10, markfield=Z5, |
86 text = \small 5, markfield=a5, |
86 text = \small 5, markfield=a5, |
146 markfields={f5, e6}, |
146 markfields={f5, e6}, |
147 setpieces={Ng7, Nb2}] |
147 setpieces={Ng7, Nb2}] |
148 |
148 |
149 \subsection*{Part 1 (6 Marks)} |
149 \subsection*{Part 1 (6 Marks)} |
150 |
150 |
151 We will implement the knight's tour problem such that we can change |
151 You are asked to implement the knight's tour problem such that the |
152 quickly the dimension of the chessboard. The fun with this problem is |
152 dimension of the board can be changed. Therefore most functions will |
153 that even for small chessbord dimensions it has already an incredably |
153 take the dimension as an argument. The fun with this problem is that |
154 large search space---finding a tour is like finding a needle in a |
154 even for small chessbord dimensions it has already an incredably large |
155 haystack. In the first part we want to see far we get with |
155 search space---finding a tour is like finding a needle in a |
156 exhaustively exploring the complete search space for small dimensions. |
156 haystack. In the first task we want to see far we get with |
157 |
157 exhaustively exploring the complete search space for small |
158 Let us first fix the basic datastructures for the implementation. A |
158 chessboards.\medskip |
159 \emph{position} (or field) on the chessboard is a pair of integers. A |
159 |
160 \emph{path} is a list of positions. The first (or 0th move) in a path |
160 \noindent |
161 should be the last element in this list; and the last move is the |
161 Let us first fix the basic datastructures for the implementation. The |
162 first element. For example the path for the $5\times 5$ chessboard |
162 board dimension is an integer (we will never go boyond board sizes of |
163 above is represented by |
163 $100 \times 100$). A \emph{position} (or field) on the chessboard is |
|
164 a pair of integers, like $(0, 0)$. A \emph{path} is a list of |
|
165 positions. The first (or 0th move) in a path is the last element in |
|
166 this list; and the last move in the path is the first element. For |
|
167 example the path for the $5\times 5$ chessboard above is represented |
|
168 by |
164 |
169 |
165 \[ |
170 \[ |
166 \texttt{List($\underbrace{\texttt{(0, 4)}}_{24}$, |
171 \texttt{List($\underbrace{\texttt{(0, 4)}}_{24}$, |
167 $\underbrace{\texttt{(2, 3)}}_{23}$, ..., (3, 2), $\underbrace{\texttt{(4, 4)}}_0$)} |
172 $\underbrace{\texttt{(2, 3)}}_{23}$, ..., |
|
173 $\underbrace{\texttt{(3, 2)}}_1$, $\underbrace{\texttt{(4, 4)}}_0$)} |
168 \] |
174 \] |
169 |
175 |
170 \noindent |
176 \noindent |
171 Suppose the dimension of a chessboard is $n$, then a path is a |
177 Suppose the dimension of a chessboard is $n$, then a path is a |
172 \emph{tour} if the length of the path is $n \times n$, each element |
178 \emph{tour} if the length of the path is $n \times n$, each element |
177 \subsubsection*{Tasks (file knight1.scala)} |
183 \subsubsection*{Tasks (file knight1.scala)} |
178 |
184 |
179 \begin{itemize} |
185 \begin{itemize} |
180 \item[(1a)] Implement a is-legal-move function that takes a dimension, a path |
186 \item[(1a)] Implement a is-legal-move function that takes a dimension, a path |
181 and a position as argument and tests whether the position is inside |
187 and a position as argument and tests whether the position is inside |
182 the board and not yet element in the path. |
188 the board and not yet element in the path. \hfill[1 Mark] |
183 |
189 |
184 \item[(1b)] Implement a legal-moves function that calculates for a |
190 \item[(1b)] Implement a legal-moves function that calculates for a |
185 position all legal follow-on moves. If the follow-on moves are |
191 position all legal onward moves. If the onward moves are |
186 placed on a circle, you should produce them starting from |
192 placed on a circle, you should produce them starting from |
187 ``12-oclock'' following in clockwise order. For example on an |
193 ``12-oclock'' following in clockwise order. For example on an |
188 $8\times 8$ board for a knight on position $(2, 2)$ and otherwise |
194 $8\times 8$ board for a knight on position $(2, 2)$ and otherwise |
189 empty board, the legal-moves function should produce the follow-on |
195 empty board, the legal-moves function should produce the onward |
190 positions |
196 positions |
191 |
197 |
192 \begin{center} |
198 \begin{center} |
193 \texttt{List((3,4), (4,3), (4,1), (3,0), (1,0), (0,1), (0,3), (1,4))} |
199 \texttt{List((3,4), (4,3), (4,1), (3,0), (1,0), (0,1), (0,3), (1,4))} |
194 \end{center} |
200 \end{center} |
195 |
201 |
196 If the board is not empty, then maybe some of the moves need to be filtered out from this list. |
202 in this order. If the board is not empty, then maybe some of the |
197 For a knight on field $(7, 7)$ and an empty board, the legal moves |
203 moves need to be filtered out from this list. For a knight on field |
198 are |
204 $(7, 7)$ and an empty board, the legal moves are |
199 |
205 |
200 \begin{center} |
206 \begin{center} |
201 \texttt{List((6,5), (5,6))} |
207 \texttt{List((6,5), (5,6))} |
202 \end{center} |
208 \end{center} |
|
209 \mbox{}\hfill[1 Mark] |
203 |
210 |
204 \item[(1c)] Implement two recursive functions (count-tours and |
211 \item[(1c)] Implement two recursive functions (count-tours and |
205 enum-tours). They each take a dimension and a path as |
212 enum-tours). They each take a dimension and a path as |
206 arguments. They exhaustively search for \underline{open} tours |
213 arguments. They exhaustively search for \underline{\bf open} tours |
207 starting from the given path. The first function counts all possible |
214 starting from the given path. The first function counts all possible |
208 open tours (there can be none for certain board sizes) and the second |
215 open tours (there can be none for certain board sizes) and the second |
209 collects all open tours in a list of paths. |
216 collects all open tours in a list of paths.\hfill[2 Marks] |
210 \end{itemize} |
217 \end{itemize} |
211 |
218 |
212 \noindent \textbf{Test data:} For the marking, these functions will be |
219 \noindent \textbf{Test data:} For the marking, the functions in (1c) |
213 called with board sizes up to $5 \times 5$. If you only search for |
220 will be called with board sizes up to $5 \times 5$. If you only search |
214 open tours starting from field $(0, 0)$, there are 304 of them. If you |
221 for open tours on $5 \times 5$ board starting from field $(0, 0)$, |
215 try out every field of a $5 \times 5$-board as a starting field and |
222 there are 304 of them. If you try out every field of a $5 \times |
216 add up all open tours, you obtain 1728. A $6\times 6$ board is already |
223 5$-board as a starting field and add up all open tours, you obtain |
217 too large to search exhaustively: the number of open tours on |
224 1728. A $6\times 6$ board is already too large to be searched |
218 $6\times 6$, $7\times 7$ and $8\times 8$ are |
225 exhaustively.\footnote{For your interest, the number of open tours on |
219 |
226 $6\times 6$, $7\times 7$ and $8\times 8$ are 6637920, 165575218320, |
220 \begin{center} |
227 19591828170979904, respectively.} |
221 \begin{tabular}{ll} |
|
222 $6\times 6$ & 6637920\\ |
|
223 $7\times 7$ & 165575218320\\ |
|
224 $8\times 8$ & 19591828170979904 |
|
225 \end{tabular} |
|
226 \end{center} |
|
227 |
228 |
228 \subsubsection*{Tasks (file knight2.scala)} |
229 \subsubsection*{Tasks (file knight2.scala)} |
229 |
230 |
230 \begin{itemize} |
231 \begin{itemize} |
231 \item[(2a)] Implement a first-function. This function takes a list of |
232 \item[(2a)] Implement a first-function. This function takes a list of |
233 position as argument and produces an optional path. The idea behind |
234 position as argument and produces an optional path. The idea behind |
234 the first-function is as follows: |
235 the first-function is as follows: |
235 |
236 |
236 \[ |
237 \[ |
237 \begin{array}{lcl} |
238 \begin{array}{lcl} |
238 first(\texttt{Nil}, f) & \dn & \texttt{None}\\ |
239 \textit{first}(\texttt{Nil}, f) & \dn & \texttt{None}\\ |
239 first(x\!::\!xs, f) & \dn & \begin{cases} |
240 \textit{first}(x\!::\!xs, f) & \dn & \begin{cases} |
240 f(x) & \textit{if}\;f(x) \not=\texttt{None}\\ |
241 f(x) & \textit{if}\;f(x) \not=\texttt{None}\\ |
241 first(xs, f) & \textit{otherwise}\\ |
242 \textit{first}(xs, f) & \textit{otherwise}\\ |
242 \end{cases} |
243 \end{cases} |
243 \end{array} |
244 \end{array} |
244 \] |
245 \] |
245 |
246 |
|
247 \noindent That is, we want to find the first position where the |
|
248 result of $f$ is not \texttt{None}.\newline\mbox{}\hfill[1 Mark] |
|
249 |
246 \item[(2b)] Implement a first-tour function. Using the first-function |
250 \item[(2b)] Implement a first-tour function. Using the first-function |
247 from (2a), search recursively for an open tour. Only use the field |
251 from (2a), search recursively for an open tour. As there might not |
248 $(0, 0)$ as a starting field of the tour. As there might not be such |
252 be such a tour at all, the first-tour function needs to return an |
249 a tour at all, the first-tour function needs to return an |
253 \texttt{Option[Path]}.\hfill[2 Marks] |
250 \texttt{Option[Path]}. For the marking, this function will be called |
254 \end{itemize} |
251 with board sizes up to $8 \times 8$. |
255 |
252 \end{itemize} |
256 \noindent |
|
257 \textbf{Testing} The first tour function will be called with board |
|
258 sizes of up to $8 \times 8$. |
253 |
259 |
254 |
260 |
255 \subsection*{Part 2 (4 Marks)} |
261 \subsection*{Part 2 (4 Marks)} |
256 |
262 |
257 For open tours beyond $8 \times 8$ boards and also for searching for |
263 As you should have seen in Part 1, a naive search for open tours |
258 closed tours, an heuristic (called Warnsdorf's rule) needs to be |
264 beyond $8 \times 8$ boards and also for searching for closed tours |
259 implemented. This rule states that a knight is moved so that it |
265 takes too much time. There is a heuristic (called Warnsdorf's rule) |
260 always proceeds to the square from which the knight will have the |
266 that can speed up finding a tour. This heuristice states that a knight |
261 fewest onward moves. For example for a knight on field $(1, 3)$, |
267 is moved so that it always proceeds to the square from which the |
262 the field $(0, 1)$ has the fewest possible onward moves. |
268 knight will have the \underline{fewest} onward moves. For example for |
|
269 a knight on field $(1, 3)$, the field $(0, 1)$ has the fewest possible |
|
270 onward moves, namely 2. |
263 |
271 |
264 \chessboard[maxfield=g7, |
272 \chessboard[maxfield=g7, |
265 pgfstyle= {[base,at={\pgfpoint{0pt}{-0.5ex}}]text}, |
273 pgfstyle= {[base,at={\pgfpoint{0pt}{-0.5ex}}]text}, |
266 text = \small 3, markfield=Z5, |
274 text = \small 3, markfield=Z5, |
267 text = \small 7, markfield=b5, |
275 text = \small 7, markfield=b5, |
270 text = \small 5, markfield=b1, |
278 text = \small 5, markfield=b1, |
271 text = \small 2, markfield=Z1, |
279 text = \small 2, markfield=Z1, |
272 setpieces={Na3}] |
280 setpieces={Na3}] |
273 |
281 |
274 \noindent |
282 \noindent |
275 Warnsdorf's rule states that the moves sould be tried out in the |
283 Warnsdorf's rule states that the moves on the board above sould be |
276 order |
284 tried out in the order |
277 |
285 |
278 \[ |
286 \[ |
279 (0, 1), (0, 5), (2, 1), (2, 5), (3, 4), (3, 2) |
287 (0, 1), (0, 5), (2, 1), (2, 5), (3, 4), (3, 2) |
280 \] |
288 \] |
281 |
289 |