| author | Christian Urban <christian.urban@kcl.ac.uk> | 
| Wed, 09 Nov 2022 18:30:46 +0000 | |
| changeset 433 | a6aa851361d0 | 
| 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 | } |