| author | Christian Urban <christian.urban@kcl.ac.uk> | 
| Mon, 26 Dec 2022 16:49:53 +0000 | |
| changeset 455 | 86a48bdf82d1 | 
| parent 421 | 864107857d27 | 
| child 460 | f5c0749858fd | 
| permissions | -rw-r--r-- | 
| 421 | 1  | 
// Main Part 4 about finding Knight's tours  | 
2  | 
//==========================================  | 
|
3  | 
import scala.annotation.tailrec  | 
|
4  | 
||
5  | 
||
6  | 
object M4a {
 | 
|
| 
391
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
7  | 
|
| 421 | 8  | 
// If you need any auxiliary functions, feel free to  | 
9  | 
// implement them, but do not make any changes to the  | 
|
10  | 
// templates below. Also have a look whether the functions  | 
|
11  | 
// at the end of the file are of any help.  | 
|
12  | 
||
13  | 
||
| 
391
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
14  | 
|
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
15  | 
type Pos = (Int, Int) // a position on a chessboard  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
16  | 
type Path = List[Pos] // a path...a list of positions  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
17  | 
|
| 421 | 18  | 
//(1) Complete the function that tests whether the position x  | 
19  | 
// is inside the board and not yet element in the path.  | 
|
| 
391
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
20  | 
|
| 421 | 21  | 
def is_legal(dim: Int, path: Path, x: Pos) : Boolean = {
 | 
22  | 
(x._1 < dim) && (x._2 < dim) && (!path.contains(x))  | 
|
23  | 
}  | 
|
24  | 
||
25  | 
||
26  | 
||
27  | 
//(2) Complete the function that calculates for a position x  | 
|
28  | 
// all legal onward moves that are not already in the path.  | 
|
29  | 
// The moves should be ordered in a "clockwise" manner.  | 
|
30  | 
||
31  | 
def legal_moves(dim: Int, path: Path, x: Pos) : List[Pos] = {
 | 
|
32  | 
val movesets = List(  | 
|
33  | 
(x._1 + 1, x._2 + 2),  | 
|
34  | 
(x._1 + 2, x._2 + 1),  | 
|
35  | 
(x._1 + 2, x._2 - 1),  | 
|
36  | 
(x._1 + 1, x._2 - 2),  | 
|
37  | 
(x._1 - 1, x._2 - 2),  | 
|
38  | 
(x._1 - 2, x._2 - 1),  | 
|
39  | 
(x._1 - 2, x._2 + 1),  | 
|
40  | 
(x._1 - 1, x._2 + 2)  | 
|
41  | 
)  | 
|
42  | 
movesets.filter(is_legal(dim, path, _))  | 
|
43  | 
}  | 
|
44  | 
||
45  | 
||
46  | 
//some testcases  | 
|
47  | 
//  | 
|
48  | 
//assert(legal_moves(8, Nil, (2,2)) ==  | 
|
49  | 
// List((3,4), (4,3), (4,1), (3,0), (1,0), (0,1), (0,3), (1,4)))  | 
|
50  | 
//assert(legal_moves(8, Nil, (7,7)) == List((6,5), (5,6)))  | 
|
51  | 
//assert(legal_moves(8, List((4,1), (1,0)), (2,2)) ==  | 
|
52  | 
// List((3,4), (4,3), (3,0), (0,1), (0,3), (1,4)))  | 
|
53  | 
//assert(legal_moves(8, List((6,6)), (7,7)) == List((6,5), (5,6)))  | 
|
54  | 
||
55  | 
||
56  | 
//(3) Complete the two recursive functions below.  | 
|
57  | 
// They exhaustively search for knight's tours starting from the  | 
|
58  | 
// given path. The first function counts all possible tours,  | 
|
59  | 
// and the second collects all tours in a list of paths.  | 
|
60  | 
||
61  | 
def count_tours(dim: Int, path: Path) : Int = {
 | 
|
62  | 
  if (dim <= 4) 0 else {
 | 
|
63  | 
    if (path.length >= (dim * dim)) 1 else {
 | 
|
64  | 
val movesets = legal_moves(dim, path, path.head)  | 
|
65  | 
(for (move <- movesets) yield count_tours(dim, move :: path)).sum  | 
|
66  | 
}  | 
|
67  | 
}  | 
|
68  | 
}  | 
|
69  | 
||
70  | 
def enum_tours(dim: Int, path: Path) : List[Path] = {
 | 
|
71  | 
  if (dim <= 4) Nil else {
 | 
|
72  | 
    if (path.length >= (dim * dim)) List(path) else {
 | 
|
73  | 
val movesets = legal_moves(dim, path, path.head)  | 
|
74  | 
(for (move <- movesets) yield enum_tours(dim, move :: path)).flatten  | 
|
75  | 
}  | 
|
76  | 
}  | 
|
77  | 
}  | 
|
78  | 
||
79  | 
||
80  | 
//(4) Implement a first-function that finds the first  | 
|
81  | 
// element, say x, in the list xs where f is not None.  | 
|
82  | 
// In that case Return f(x), otherwise None. If possible,  | 
|
83  | 
// calculate f(x) only once.  | 
|
84  | 
||
85  | 
@tailrec  | 
|
86  | 
def first(xs: List[Pos], f: Pos => Option[Path]) : Option[Path] = {
 | 
|
87  | 
  xs match {
 | 
|
88  | 
case Nil => None  | 
|
89  | 
    case head :: rest => {
 | 
|
90  | 
val result = f(head)  | 
|
91  | 
if (result.isEmpty) first(rest, f) else result  | 
|
92  | 
}  | 
|
93  | 
}  | 
|
94  | 
}  | 
|
95  | 
||
96  | 
||
97  | 
// testcases  | 
|
98  | 
//  | 
|
99  | 
//def foo(x: (Int, Int)) = if (x._1 > 3) Some(List(x)) else None  | 
|
100  | 
//  | 
|
101  | 
//first(List((1, 0),(2, 0),(3, 0),(4, 0)), foo) // Some(List((4,0)))  | 
|
102  | 
//first(List((1, 0),(2, 0),(3, 0)), foo) // None  | 
|
103  | 
||
104  | 
||
105  | 
//(5) Implement a function that uses the first-function from (4) for  | 
|
106  | 
// trying out onward moves, and searches recursively for a  | 
|
107  | 
// knight tour on a dim * dim-board.  | 
|
108  | 
||
109  | 
def first_tour(dim: Int, path: Path) : Option[Path] = ???  | 
|
110  | 
||
111  | 
||
112  | 
||
113  | 
/* Helper functions  | 
|
114  | 
||
115  | 
||
116  | 
// for measuring time  | 
|
| 
391
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
117  | 
def time_needed[T](code: => T) : T = {
 | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
118  | 
val start = System.nanoTime()  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
119  | 
val result = code  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
120  | 
val end = System.nanoTime()  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
121  | 
  println(f"Time needed: ${(end - start) / 1.0e9}%3.3f secs.")
 | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
122  | 
result  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
123  | 
}  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
124  | 
|
| 421 | 125  | 
// can be called for example with  | 
126  | 
//  | 
|
127  | 
// time_needed(count_tours(dim, List((0, 0))))  | 
|
128  | 
//  | 
|
129  | 
// in order to print out the time that is needed for  | 
|
130  | 
// running count_tours  | 
|
131  | 
||
132  | 
||
| 
391
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
133  | 
// for printing a board  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
134  | 
def print_board(dim: Int, path: Path): Unit = {
 | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
135  | 
println()  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
136  | 
  for (i <- 0 until dim) {
 | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
137  | 
    for (j <- 0 until dim) {
 | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
138  | 
      print(f"${path.reverse.indexOf((j, dim - i - 1))}%3.0f ")
 | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
139  | 
}  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
140  | 
println()  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
141  | 
}  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
142  | 
}  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
143  | 
|
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
144  | 
|
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
145  | 
*/  | 
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
146  | 
|
| 
 
048fc6b70776
added main4 marking
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents:  
diff
changeset
 | 
147  | 
}  |