/** * Let's Walk Through a Maze. * * Imagine there is a maze whose walls are the big 'O' letters. * Now, I stand where a big 'I' stands and some cool prize lies * somewhere marked with a '$' sign. Like this: * * OOOOOOOOOOOOOOOOO * O O * O$ O O * OOOOO O * O O * O OOOOOOOOOOOOOO * O O I O * O O * OOOOOOOOOOOOOOOOO * * I want to get the prize, and this program helps me do so as soon * as I possibly can by finding a shortest path through the maze. */ /** * This function looks for a path from max.start to maze.end through * free space (a path does not go through walls). One can move only * straightly up, down, left or right, no diagonal moves allowed. */ fun findPath(maze: Maze): List>? { val previous = HashMap, Pair> val queue = LinkedList> val visited = HashSet> queue.offer(maze.start) visited.add(maze.start) while (!queue.isEmpty()) { val cell = queue.poll() if (cell == maze.end) break for (newCell in maze.neighbors(cell._1, cell._2)) { if (newCell in visited) continue previous[newCell] = cell queue.offer(newCell) visited.add(cell) } } if (previous[maze.end] == null) return null val path = ArrayList>() var current = previous[maze.end] while (current != maze.start) { path.add(0, current) current = previous[current] } return path } /** * Find neighbors of the (i, j) cell that are not walls */ fun Maze.neighbors(i: Int, j: Int): List> { val result = ArrayList> addIfFree(i - 1, j, result) addIfFree(i, j - 1, result) addIfFree(i + 1, j, result) addIfFree(i, j + 1, result) return result } fun Maze.addIfFree(i: Int, j: Int, result: List>) { if (i !in 0..height - 1) return if (j !in 0..width - 1) return if (walls[i][j]) return result.add(Pair(i, j)) } /** * A data class that represents a maze */ class Maze( // Number or columns val width: Int, // Number of rows val height: Int, // true for a wall, false for free space val walls: Array>, // The starting point (must not be a wall) val start: Pair, // The target point (must not be a wall) val end: Pair ) { } /** A few maze examples here */ fun main(args: Array) { printMaze("I $") printMaze("I O $") printMaze(""" O $ O O O O I """) printMaze(""" OOOOOOOOOOO O $ O OOOOOOO OOO O O OOOOO OOOOO O O O OOOOOOOOO O OO OOOOOO IO """) printMaze(""" OOOOOOOOOOOOOOOOO O O O$ O O OOOOO O O O O OOOOOOOOOOOOOO O O I O O O OOOOOOOOOOOOOOOOO """) } // UTILITIES fun printMaze(str: String) { val maze = makeMaze(str) println("Maze:") val path = findPath(maze) for (i in 0..maze.height - 1) { for (j in 0..maze.width - 1) { val cell = Pair(i, j) print( if (maze.walls[i][j]) "O" else if (cell == maze.start) "I" else if (cell == maze.end) "$" else if (path != null && path.contains(cell)) "~" else " " ) } println("") } println("Result: " + if (path == null) "No path" else "Path found") println("") } /** * A maze is encoded in the string s: the big 'O' letters are walls. * I stand where a big 'I' stands and the prize is marked with * a '$' sign. * * Example: * * OOOOOOOOOOOOOOOOO * O O * O$ O O * OOOOO O * O O * O OOOOOOOOOOOOOO * O O I O * O O * OOOOOOOOOOOOOOOOO */ fun makeMaze(s: String): Maze { val lines = s.split("\n")!! val w = max(lines.toList(), comparator { o1, o2 -> val l1: Int = o1?.size ?: 0 val l2 = o2?.size ?: 0 l1 - l2 })!! val data = Array>(lines.size) { Array(w.size) { false } } var start: Pair? = null var end: Pair? = null for (line in lines.indices) { for (x in lines[line].indices) { val c = lines[line]!![x] data[line][x] = c == 'O' when (c) { 'I' -> start = Pair(line, x) '$' -> end = Pair(line, x) else -> { } } } } if (start == null) { throw IllegalArgumentException("No starting point in the maze (should be indicated with 'I')") } if (end == null) { throw IllegalArgumentException("No goal point in the maze (should be indicated with a '$' sign)") } return Maze(w.size, lines.size, data, start!!, end!!) } // An excerpt from the Standard Library val String?.indices: IntRange get() = IntRange(0, this!!.size) fun Map.set(k: K, v: V) { put(k, v) } fun comparator (f: (T, T) -> Int): Comparator = object : Comparator { override fun compare(o1: T, o2: T): Int = f(o1, o2) override fun equals(p: Any?): Boolean = false } fun > Array.to(result: C): C { for (elem in this) result.add(elem) return result }