diff --git a/.gitignore b/.gitignore index e524a17..8e9219b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,6 @@ -*.class -*.log - -# sbt specific -.cache -.history -.lib/ -dist/* target/ -lib_managed/ -src_managed/ -project/boot/ -project/plugins/project/ - -# Scala-IDE specific -.scala_dependencies -.worksheet -.idea/ -*.iml +.cache +.classpath +.project +.settings/ +.idea \ No newline at end of file diff --git a/build.sbt b/build.sbt index 4906dc6..7bb01b2 100644 --- a/build.sbt +++ b/build.sbt @@ -1,13 +1,26 @@ -enablePlugins(ScalaJSPlugin) +// Build file for example applications written in ScalaJS using scala-js-dom and scala-js-workbench +lazy val root = (project in file(".")).enablePlugins(ScalaJSPlugin) +workbenchSettings -name := "A Star Maze Solver" - -scalaVersion := "2.11.5" +// Name is a prefix in the object code filename. +name := "A*Star Maze Solver" +scalaVersion := "2.11.7" +// Optional, necessary to sbt run, needs phantomJS to be installed. +jsDependencies += RuntimeDOM scalaJSStage in Global := FastOptStage -libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.8.0" -libraryDependencies += "org.scala-js" % "scalajs-tools_2.11" % "0.6.0" -libraryDependencies += "com.lihaoyi" %%% "utest" % "0.3.0" % "test" +libraryDependencies ++= Seq( + "org.scala-js" %%% "scalajs-dom" % "0.8.1", + "com.lihaoyi" %%% "scalatags" % "0.5.2", + "be.doeraene" %%% "scalajs-jquery" % "0.8.0", + "com.lihaoyi" %%% "utest" % "0.3.1" % "test" +) +skip in packageJSDependencies := false // All JavaScript dependencies to be concatenated to a single file + +// Workbench has to know how to restart your application. +bootSnippet := "astar.AStarApp().doDynContent();" +// Update without refreshing the page every time fastOptJS completes +updateBrowsers <<= updateBrowsers.triggeredBy(fastOptJS in Compile) -testFrameworks += new TestFramework("utest.runner.Framework") \ No newline at end of file +testFrameworks += new TestFramework("utest.runner.Framework") diff --git a/project/build.properties b/project/build.properties index 748703f..176a863 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.7 +sbt.version=0.13.9 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 0dd657b..605d7a6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1,2 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.4") +addSbtPlugin("com.lihaoyi" % "workbench" % "0.2.3") \ No newline at end of file diff --git a/src/main/resources/astar-demo-20.html b/src/main/resources/astar-demo-20.html deleted file mode 100644 index 07b900e..0000000 --- a/src/main/resources/astar-demo-20.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - A* Maze Solver - - -
- -
- - - - - \ No newline at end of file diff --git a/src/main/resources/index-dev.html b/src/main/resources/index-dev.html new file mode 100644 index 0000000..ae8f581 --- /dev/null +++ b/src/main/resources/index-dev.html @@ -0,0 +1,22 @@ + + + + + + + + A*Star Maze Solver + + + + + + +
+ + Your browser does not support HTML 5 Canvas. + +
+ + + \ No newline at end of file diff --git a/src/main/scala/astar/AStarApp.scala b/src/main/scala/astar/AStarApp.scala index cb39ad7..6229c47 100644 --- a/src/main/scala/astar/AStarApp.scala +++ b/src/main/scala/astar/AStarApp.scala @@ -1,69 +1,60 @@ package astar -import astar.maze.Mazes -import astar.maze.Cell -import astar.search.{Heuristics, Agent} +import astar.maze.{Cell, Mazes} +import astar.search.{Agent, Heuristics} +import org.scalajs.dom import org.scalajs.dom.html import scala.scalajs.js.JSApp -import org.scalajs.dom - import scala.scalajs.js.annotation.JSExport @JSExport -object AStarApp extends JSApp { +object AStarApp { - private val GREY = "#333333" - private val BLUE = "#4682b4" - private val YELLOW = "#ffb958" - private val zoom = 10 //px - private val offset = 5 //px - private val drawRate = 10 //milliseconds + private val (grey, blue, yellow) = ("#333333", "#4682b4", "#ffb958") + private val (zoom, offset, drawRate) = (10 /*px*/ , 5 /*px*/ , 10 /*milliseconds*/ ) - def main(): Unit = { - val canvas = dom.document - .getElementById("canvas") - .asInstanceOf[html.Canvas] + @JSExport + def doDynContent(): Unit = { + val canvas = dom.document.getElementById("canvas").asInstanceOf[html.Canvas] - val ctx = canvas.getContext("2d") - .asInstanceOf[dom.CanvasRenderingContext2D] + val ctx = canvas.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D] - val maze = Mazes.maze20 + val maze = Mazes.maze40 val agent = new Agent(maze, Heuristics.manhattan) - val solution:Option[List[Cell]] = agent.search() + val solution: Option[List[Cell]] = agent.search() - canvas.height = maze.height * zoom - canvas.width = maze.width * zoom + canvas.height = maze.height * zoom; canvas.width = maze.width * zoom - ctx.clearRect (0, 0, canvas.width, canvas.height) + ctx.clearRect(0, 0, canvas.width, canvas.height) - ctx.fillStyle = GREY - for(wall <- maze.walls) { + ctx.fillStyle = grey + for (wall <- maze.walls) { ctx.fillRect(wall.x * zoom, wall.y * zoom, zoom, zoom) } - if(solution.isDefined) { + if (solution.isDefined) { dom.console.log("Nodes expanded:" + agent.searchHistory.size) draw(agent.searchHistory) } - def draw(fringe:Seq[List[Cell]]):Unit = { - if(fringe.nonEmpty) { - ctx.fillStyle = BLUE + def draw(fringe: Seq[List[Cell]]): Unit = { + if (fringe.nonEmpty) { + ctx.fillStyle = blue for (cell <- fringe.head) { ctx.beginPath() - ctx.arc(cell.x * zoom + offset, cell.y * zoom + offset, (zoom -2)/ 2, 0, 2 * math.Pi) + ctx.arc(cell.x * zoom + offset, cell.y * zoom + offset, (zoom - 2) / 2, 0, 2 * math.Pi) ctx.fill() } dom.setTimeout(() => draw(fringe.tail), drawRate) } else { val path = solution.get - ctx.fillStyle = YELLOW - for(cell <- path) { + ctx.fillStyle = yellow + for (cell <- path) { ctx.beginPath() - ctx.arc(cell.x * zoom + offset, cell.y * zoom + offset, (zoom -2)/ 2, 0, 2 * math.Pi) + ctx.arc(cell.x * zoom + offset, cell.y * zoom + offset, (zoom - 2) / 2, 0, 2 * math.Pi) ctx.fill() } } diff --git a/src/main/scala/astar/maze/Cell.scala b/src/main/scala/astar/maze/Cell.scala deleted file mode 100644 index 7fdce0b..0000000 --- a/src/main/scala/astar/maze/Cell.scala +++ /dev/null @@ -1,25 +0,0 @@ -package astar.maze - -/** - * Author: Phillip Johnson - * Date: 3/15/15 - */ -class Cell(val x:Int, val y:Int) { - - override def toString = "(" + x + ", " + y + ")" - - def canEqual(other: Any): Boolean = other.isInstanceOf[Cell] - - override def equals(other: Any): Boolean = other match { - case that: Cell => - (that canEqual this) && - x == that.x && - y == that.y - case _ => false - } - - override def hashCode(): Int = { - val state = Seq(x, y) - state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b) - } -} diff --git a/src/main/scala/astar/maze/Maze.scala b/src/main/scala/astar/maze/Maze.scala index 25701e9..1ea1b11 100644 --- a/src/main/scala/astar/maze/Maze.scala +++ b/src/main/scala/astar/maze/Maze.scala @@ -1,58 +1,63 @@ -package astar.maze +package astar +package maze /** * A problem for the A* agent to traverse. * * Mazes are generated from text strings using: - * +- for horizontal walls - * | for vertical walls - * spaces for paths - * I for the entrance - * O for the exit + * +- for horizontal walls + * | for vertical walls + * spaces for paths + * I for the entrance + * O for the exit * * Author: Phillip Johnson * Date: 3/14/15 (mmmm...π) */ -class Maze(val pattern:String) { - require(pattern.contains("I")) - require(pattern.contains("O")) +case class Cell(x: Int, y: Int) { + override def toString = s"($x, $y)" +} + +class Maze(val pattern: String) { + require(pattern.contains("I") && pattern.contains("O"), "Missing maze entrance or exit") - private val WALL_STRINGS = Set('+','-','|') - private val PATH_STRINGS = Set(' ','I','O') + private val WALL_STRINGS = Set('+', '-', '|') + private val PATH_STRINGS = Set(' ', 'I', 'O') private val ENTRANCE_STRING = 'I' private val EXIT_STRING = 'O' - private val stringRows = pattern.split("\n") - - val width = stringRows.head.length - val height = stringRows.size + //private val stringRows = pattern.split('\n') + val (width, height) = sizeOfMaze - lazy val walls:Set[Cell] = { + lazy val walls: Set[Cell] = { searchMaze(WALL_STRINGS) } - lazy val paths:Set[Cell] = { + lazy val paths: Set[Cell] = { searchMaze(PATH_STRINGS) } - lazy val entrance:(Cell) = { + lazy val entrance: (Cell) = { searchMaze(Set(ENTRANCE_STRING)).head } - lazy val exit:(Cell) = { + lazy val exit: (Cell) = { searchMaze(Set(EXIT_STRING)).head } - private def searchMaze(chars:Set[Char]) = { - val rows = stringRows - val results = for { - (rowStr:String, row:Int) <- rows.zipWithIndex - (char:Char, col:Int) <- rowStr.zipWithIndex - if chars.contains(char) - } yield new Cell(col, row) + def sizeOfMaze = { + val stringRows = pattern.split('\n') + (stringRows.head.length - 1, stringRows.length) + } - results.toSet + private def searchMaze(chars: Set[Char]) = { + val stringRows = pattern.split('\n') + (for { + (rowStr: String, row: Int) <- stringRows.zipWithIndex + (char: Char, col: Int) <- rowStr.zipWithIndex + if chars.contains(char) + } yield Cell(col, row)).toSet } } diff --git a/src/main/scala/astar/maze/Mazes.scala b/src/main/scala/astar/maze/Mazes.scala index fe1b840..cd37d80 100644 --- a/src/main/scala/astar/maze/Mazes.scala +++ b/src/main/scala/astar/maze/Mazes.scala @@ -1,15 +1,137 @@ -package astar.maze +package astar +package maze /** - * Author: Phillip Johnson + * Author: Phillip C Johnson * Date: 3/8/15 */ object Mazes { - private val maze40Str = "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\nI | | | | | | | | | | | | | |\n+-+-+-+-+-+ + + + + +-+-+ + + +-+ + +-+ +-+-+-+ + + + + + + +-+-+ + + + + +-+ + +\n| | | | | | | | | | | | | | | | | | | | | | | | |\n+ +-+-+ + + + +-+-+ + +-+-+ + +-+-+ + +-+-+ + + + +-+-+-+-+ +-+ + +-+-+ + + + +-+\n| | | | | | | | | | | | | | | | | | | | | | |\n+ + + +-+-+-+-+ + + + + +-+-+-+ +-+-+ + + + + + +-+-+ + + +-+ + + +-+ +-+-+-+ + +\n| | | | | | | | | | | | | | | | | | | | | | | |\n+-+-+ + + +-+-+-+-+-+ + + + +-+ + + +-+ + + +-+-+ + +-+ + + +-+-+-+ +-+ + +-+-+ +\n| | | | | | | | | | | | | | | | | | | | | | | | |\n+ + +-+-+-+ + +-+ +-+ +-+-+-+-+-+ +-+ +-+ + + + + +-+ + + + + + + + + + + +-+ + +\n| | | | | | | | | | | | | | | | | | | | | | | |\n+ + + + +-+ + +-+-+ +-+ +-+-+-+-+-+ + + + + +-+-+-+-+-+-+ + + + +-+ +-+-+ + + + +\n| | | | | | | | | | | | | | | | | | | | | | |\n+ + + +-+ +-+ +-+ + + +-+-+-+ + + + + +-+-+ + + +-+-+-+ + + +-+-+-+ + + +-+-+ + +\n| | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+ +-+ + + + + +-+-+-+ + + +-+-+ + +-+-+-+ +-+-+-+ +-+-+-+-+-+-+ +-+-+ +\n| | | | | | | | | | | | | | | | | |\n+ + + +-+ +-+-+ +-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+ +-+-+ +-+-+-+ + + + +-+-+ + + +\n| | | | | | | | | | | | | | | | | | | | | | |\n+ + + + + + + +-+ +-+ +-+-+-+ + +-+-+-+ + + +-+ +-+ + +-+-+ + +-+ + + + +-+ + +-+\n| | | | | | | | | | | | | | | | | | | | | | | |\n+-+-+ +-+-+-+ + +-+ + + + +-+-+-+-+-+-+-+ +-+ +-+ + +-+ + +-+ + + +-+ +-+-+-+-+ +\n| | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+-+-+ + + +-+-+-+-+-+-+-+ + + +-+ +-+ + + + +-+-+ + +-+-+ +-+ + + + + +\n| | | | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+ + + +-+ +-+-+ +-+-+ + + +-+ + + +-+-+-+ + +-+ + + + +-+ + + +-+ + +-+ +\n| | | | | | | | | | | | | | | | | | | | | |\n+-+ + + +-+ + + + + +-+-+ + +-+-+-+ +-+ +-+-+ + +-+-+ + +-+-+-+-+ + + + +-+-+ +-+\n| | | | | | | | | | | | | | | | | | | | |\n+-+ +-+ + +-+-+-+ +-+ +-+-+-+ + +-+-+ + + +-+-+-+ +-+-+ + + + +-+-+ +-+ +-+ +-+ +\n| | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+ + +-+ +-+-+-+ +-+ + + + +-+-+-+ + + +-+-+-+ +-+ + +-+ + + +-+-+ + + +-+\n| | | | | | | | | | | | | | | | | | | | |\n+-+-+ + + +-+ +-+-+-+-+-+-+ + + +-+-+ + +-+-+-+ +-+ +-+ + + + +-+ +-+ +-+ +-+-+ +\n| | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+ +-+-+ + + + + + + +-+-+ + +-+-+ +-+-+-+ + +-+-+-+-+-+ + + + +-+ + + +\n| | | | | | | | | | | | | | | | | | | |\n+-+-+-+-+ +-+-+-+-+-+ + + + +-+-+ +-+-+-+ +-+ + + + + +-+ +-+-+ + +-+-+-+-+-+-+ +\n| | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+-+ + + + + +-+ + + +-+-+ +-+-+-+ +-+-+ +-+-+ +-+ + +-+-+-+ +-+ + + +-+\n| | | | | | | | | | | | | | | | | | | | | |\n+-+ + +-+ + +-+-+-+ +-+ + + + + + +-+-+-+ +-+ + +-+ + + +-+ +-+-+ +-+-+ +-+-+ + +\n| | | | | | | | | | | | | | | | | | | | | | |\n+ + +-+ +-+-+ +-+ + + +-+-+ +-+-+ +-+-+ +-+ + +-+-+-+-+ + +-+ +-+-+ +-+-+ + +-+ +\n| | | | | | | | | | | | | | | | | | | |\n+ +-+ + + +-+ + + +-+-+ +-+-+-+ +-+-+ + + +-+-+-+ +-+-+-+ + +-+-+-+-+ + + + + +-+\n| | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+ + +-+ +-+-+ +-+ + +-+-+-+ + + + +-+-+-+ +-+ + +-+-+-+-+ +-+-+ +-+-+-+ +\n| | | | | | | | | | | | | | | | | | | |\n+-+ +-+ + + + +-+ + + +-+-+ + + +-+ + +-+-+ + +-+-+ +-+-+-+-+-+ + + + +-+-+ + +-+\n| | | | | | | | | | | | | | | | | | | | | | |\n+ + + +-+ + + + +-+ +-+-+ +-+-+-+ + +-+ +-+ +-+ + +-+-+ + + +-+ + + +-+-+-+-+-+ +\n| | | | | | | | | | | | | | | | | | | | | |\n+ + + + +-+ + + + +-+ + +-+-+-+-+-+ + +-+ +-+-+-+-+ + +-+ +-+ + +-+-+-+-+ +-+-+ +\n| | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+ +-+ +-+ + + + +-+-+-+ + +-+ + + + + + + + +-+ + +-+ +-+-+-+ +-+-+ + +\n| | | | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+-+ + + + +-+-+-+ + + +-+-+-+ +-+-+ +-+-+-+-+-+ +-+ + + + + +-+ +-+ + +\n| | | | | | | | | | | | | | | | | | |\n+ +-+-+ + + +-+-+-+ + + +-+ + +-+ +-+ +-+ + +-+ +-+-+-+ + +-+ + +-+-+-+ +-+ + + +\n| | | | | | | | | | | | | | | | | | | | | |\n+ + + + + +-+-+ + +-+ + + +-+-+-+-+ + + +-+ + +-+-+ + +-+-+-+-+ + +-+ +-+-+-+-+ +\n| | | | | | | | | | | | | | | | | | |\n+-+-+ +-+ + +-+-+ + +-+ +-+ + + +-+-+-+-+-+-+ + + +-+-+-+ +-+ +-+-+-+ + +-+-+ +-+\n| | | | | | | | | | | | | | | | | | | |\n+ + + + +-+ + +-+-+ + +-+-+ + + + +-+-+ + + +-+-+-+-+-+-+-+ +-+ + + +-+ + +-+ +-+\n| | | | | | | | | | | | | | | | | | | | | |\n+ + +-+-+ +-+-+ + +-+ + +-+-+ + +-+ + +-+-+-+-+-+-+ + + +-+ + +-+-+-+ +-+-+ +-+ +\n| | | | | | | | | | | | | | | | | | | | |\n+ +-+-+-+-+-+ +-+-+ + +-+-+ +-+-+-+-+-+-+ + + + + +-+-+-+-+-+ +-+ + + + + + +-+ +\n| | | | | | | | | | | | | | | | |\n+ + +-+-+-+ +-+-+-+-+-+ + +-+-+-+ + + + +-+-+-+-+ +-+ +-+-+ +-+ +-+-+ +-+ +-+-+ +\n| | | | | | | | | | | | | | | | | | |\n+ +-+ +-+ +-+-+-+-+-+-+-+ + +-+ + +-+ + +-+-+-+ +-+ +-+-+-+-+ + + + +-+ +-+-+ + +\n| | | | | | | | | | | | | | | | | |\n+-+ +-+ +-+-+ + +-+-+ + +-+ + + + + + +-+-+-+-+ +-+-+ +-+-+ +-+-+ +-+ +-+-+-+-+-+\n| | | | | | | | | | | | | | | | | | | |\n+ +-+ +-+-+-+ + + + +-+ + + + + +-+-+-+ + + +-+-+-+ + + + +-+-+-+-+-+-+ + + +-+ +\n| | | | | | | | | | | | O\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+" + private def maze40Str = + """+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !I | | | | | | | | | | | | | | + !+-+-+-+-+-+ + + + + +-+-+ + + +-+ + +-+ +-+-+-+ + + + + + + +-+-+ + + + + +-+ + + + !| | | | | | | | | | | | | | | | | | | | | | | | | + !+ +-+-+ + + + +-+-+ + +-+-+ + +-+-+ + +-+-+ + + + +-+-+-+-+ +-+ + +-+-+ + + + +-+ + !| | | | | | | | | | | | | | | | | | | | | | | + !+ + + +-+-+-+-+ + + + + +-+-+-+ +-+-+ + + + + + +-+-+ + + +-+ + + +-+ +-+-+-+ + + + !| | | | | | | | | | | | | | | | | | | | | | | | + !+-+-+ + + +-+-+-+-+-+ + + + +-+ + + +-+ + + +-+-+ + +-+ + + +-+-+-+ +-+ + +-+-+ + + !| | | | | | | | | | | | | | | | | | | | | | | | | + !+ + +-+-+-+ + +-+ +-+ +-+-+-+-+-+ +-+ +-+ + + + + +-+ + + + + + + + + + + +-+ + + + !| | | | | | | | | | | | | | | | | | | | | | | | + !+ + + + +-+ + +-+-+ +-+ +-+-+-+-+-+ + + + + +-+-+-+-+-+-+ + + + +-+ +-+-+ + + + + + !| | | | | | | | | | | | | | | | | | | | | | | + !+ + + +-+ +-+ +-+ + + +-+-+-+ + + + + +-+-+ + + +-+-+-+ + + +-+-+-+ + + +-+-+ + + + !| | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+ +-+ + + + + +-+-+-+ + + +-+-+ + +-+-+-+ +-+-+-+ +-+-+-+-+-+-+ +-+-+ + + !| | | | | | | | | | | | | | | | | | + !+ + + +-+ +-+-+ +-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+ +-+-+ +-+-+-+ + + + +-+-+ + + + + !| | | | | | | | | | | | | | | | | | | | | | | + !+ + + + + + + +-+ +-+ +-+-+-+ + +-+-+-+ + + +-+ +-+ + +-+-+ + +-+ + + + +-+ + +-+ + !| | | | | | | | | | | | | | | | | | | | | | | | + !+-+-+ +-+-+-+ + +-+ + + + +-+-+-+-+-+-+-+ +-+ +-+ + +-+ + +-+ + + +-+ +-+-+-+-+ + + !| | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+-+-+ + + +-+-+-+-+-+-+-+ + + +-+ +-+ + + + +-+-+ + +-+-+ +-+ + + + + + + !| | | | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+ + + +-+ +-+-+ +-+-+ + + +-+ + + +-+-+-+ + +-+ + + + +-+ + + +-+ + +-+ + + !| | | | | | | | | | | | | | | | | | | | | | + !+-+ + + +-+ + + + + +-+-+ + +-+-+-+ +-+ +-+-+ + +-+-+ + +-+-+-+-+ + + + +-+-+ +-+ + !| | | | | | | | | | | | | | | | | | | | | + !+-+ +-+ + +-+-+-+ +-+ +-+-+-+ + +-+-+ + + +-+-+-+ +-+-+ + + + +-+-+ +-+ +-+ +-+ + + !| | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+ + +-+ +-+-+-+ +-+ + + + +-+-+-+ + + +-+-+-+ +-+ + +-+ + + +-+-+ + + +-+ + !| | | | | | | | | | | | | | | | | | | | | + !+-+-+ + + +-+ +-+-+-+-+-+-+ + + +-+-+ + +-+-+-+ +-+ +-+ + + + +-+ +-+ +-+ +-+-+ + + !| | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+ +-+-+ + + + + + + +-+-+ + +-+-+ +-+-+-+ + +-+-+-+-+-+ + + + +-+ + + + + !| | | | | | | | | | | | | | | | | | | | + !+-+-+-+-+ +-+-+-+-+-+ + + + +-+-+ +-+-+-+ +-+ + + + + +-+ +-+-+ + +-+-+-+-+-+-+ + + !| | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+-+ + + + + +-+ + + +-+-+ +-+-+-+ +-+-+ +-+-+ +-+ + +-+-+-+ +-+ + + +-+ + !| | | | | | | | | | | | | | | | | | | | | | + !+-+ + +-+ + +-+-+-+ +-+ + + + + + +-+-+-+ +-+ + +-+ + + +-+ +-+-+ +-+-+ +-+-+ + + + !| | | | | | | | | | | | | | | | | | | | | | | + !+ + +-+ +-+-+ +-+ + + +-+-+ +-+-+ +-+-+ +-+ + +-+-+-+-+ + +-+ +-+-+ +-+-+ + +-+ + + !| | | | | | | | | | | | | | | | | | | | + !+ +-+ + + +-+ + + +-+-+ +-+-+-+ +-+-+ + + +-+-+-+ +-+-+-+ + +-+-+-+-+ + + + + +-+ + !| | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+ + +-+ +-+-+ +-+ + +-+-+-+ + + + +-+-+-+ +-+ + +-+-+-+-+ +-+-+ +-+-+-+ + + !| | | | | | | | | | | | | | | | | | | | + !+-+ +-+ + + + +-+ + + +-+-+ + + +-+ + +-+-+ + +-+-+ +-+-+-+-+-+ + + + +-+-+ + +-+ + !| | | | | | | | | | | | | | | | | | | | | | | + !+ + + +-+ + + + +-+ +-+-+ +-+-+-+ + +-+ +-+ +-+ + +-+-+ + + +-+ + + +-+-+-+-+-+ + + !| | | | | | | | | | | | | | | | | | | | | | + !+ + + + +-+ + + + +-+ + +-+-+-+-+-+ + +-+ +-+-+-+-+ + +-+ +-+ + +-+-+-+-+ +-+-+ + + !| | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+ +-+ +-+ + + + +-+-+-+ + +-+ + + + + + + + +-+ + +-+ +-+-+-+ +-+-+ + + + !| | | | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+-+ + + + +-+-+-+ + + +-+-+-+ +-+-+ +-+-+-+-+-+ +-+ + + + + +-+ +-+ + + + !| | | | | | | | | | | | | | | | | | | + !+ +-+-+ + + +-+-+-+ + + +-+ + +-+ +-+ +-+ + +-+ +-+-+-+ + +-+ + +-+-+-+ +-+ + + + + !| | | | | | | | | | | | | | | | | | | | | | + !+ + + + + +-+-+ + +-+ + + +-+-+-+-+ + + +-+ + +-+-+ + +-+-+-+-+ + +-+ +-+-+-+-+ + + !| | | | | | | | | | | | | | | | | | | + !+-+-+ +-+ + +-+-+ + +-+ +-+ + + +-+-+-+-+-+-+ + + +-+-+-+ +-+ +-+-+-+ + +-+-+ +-+ + !| | | | | | | | | | | | | | | | | | | | + !+ + + + +-+ + +-+-+ + +-+-+ + + + +-+-+ + + +-+-+-+-+-+-+-+ +-+ + + +-+ + +-+ +-+ + !| | | | | | | | | | | | | | | | | | | | | | + !+ + +-+-+ +-+-+ + +-+ + +-+-+ + +-+ + +-+-+-+-+-+-+ + + +-+ + +-+-+-+ +-+-+ +-+ + + !| | | | | | | | | | | | | | | | | | | | | + !+ +-+-+-+-+-+ +-+-+ + +-+-+ +-+-+-+-+-+-+ + + + + +-+-+-+-+-+ +-+ + + + + + +-+ + + !| | | | | | | | | | | | | | | | | + !+ + +-+-+-+ +-+-+-+-+-+ + +-+-+-+ + + + +-+-+-+-+ +-+ +-+-+ +-+ +-+-+ +-+ +-+-+ + + !| | | | | | | | | | | | | | | | | | | + !+ +-+ +-+ +-+-+-+-+-+-+-+ + +-+ + +-+ + +-+-+-+ +-+ +-+-+-+-+ + + + +-+ +-+-+ + + + !| | | | | | | | | | | | | | | | | | + !+-+ +-+ +-+-+ + +-+-+ + +-+ + + + + + +-+-+-+-+ +-+-+ +-+-+ +-+-+ +-+ +-+-+-+-+-+ + !| | | | | | | | | | | | | | | | | | | | + !+ +-+ +-+-+-+ + + + +-+ + + + + +-+-+-+ + + +-+-+-+ + + + +-+-+-+-+-+-+ + + +-+ + + !| | | | | | | | | | | | O + !+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+""".stripMargin('!') lazy val maze40 = new Maze(maze40Str) - private val maze20Str = "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\nI | | | | | | |\n+ +-+-+-+ + + + + + + + + + + +-+ +-+ + +\n| | | | | | | | | | | | | |\n+-+-+-+-+-+ +-+-+-+-+ + +-+ + + + + +-+-+\n| | | | | | | |\n+ + +-+-+-+-+-+ + +-+-+ + +-+-+-+ +-+-+ +\n| | | | | | | | | | |\n+-+-+ +-+-+-+-+-+ + + + +-+ + + + + + + +\n| | | | | | | | | | | | | | |\n+ +-+-+ + + + + +-+ + + + + + + +-+-+ + +\n| | | | | | | | | | | | | |\n+ +-+ + + + +-+-+ +-+ + +-+-+-+ +-+-+-+-+\n| | | | | | | | | | |\n+-+-+-+ + +-+ + + + +-+-+-+ + +-+ + + + +\n| | | | | | | | | | | | |\n+ + + + +-+-+-+ + + +-+ + +-+ + + +-+ + +\n| | | | | | | | | | | |\n+ + +-+ +-+-+ +-+-+-+ +-+ + +-+ +-+ +-+ +\n| | | | | | | | | | | | |\n+-+-+ + + +-+-+ + +-+-+ +-+ + + + +-+ + +\n| | | | | | | | | | | |\n+ +-+-+-+-+-+-+-+ + + +-+ + + +-+-+ +-+ +\n| | | | | | | | | | | |\n+ + +-+-+-+-+ + + + +-+ + +-+ + + + + + +\n| | | | | | | | | | | | | |\n+ + +-+ + + + +-+-+ + + + + + +-+-+ +-+-+\n| | | | | | | | | | | |\n+-+-+ +-+-+-+-+-+-+ +-+-+ + + + +-+-+ + +\n| | | | | | | | | | | |\n+ + +-+ +-+ +-+ +-+-+-+ +-+-+-+ + + + + +\n| | | | | | | | | | | | | |\n+ +-+ +-+ +-+ +-+ +-+ + + + + +-+ + + + +\n| | | | | | | | | | | | | | | |\n+ + +-+ + +-+ + +-+ +-+-+ + + + + +-+ + +\n| | | | | | | | | | |\n+-+-+ +-+-+ +-+-+ +-+-+ +-+-+ +-+-+ + + +\n| | | | | | |\n+ +-+-+ + + +-+-+ +-+-+ +-+-+-+-+-+-+-+ +\n| | | | O\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+" + private def maze20Str = + """+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !I | | | | | | | + !+ +-+-+-+ + + + + + + + + + + +-+ +-+ + + + !| | | | | | | | | | | | | | + !+-+-+-+-+-+ +-+-+-+-+ + +-+ + + + + +-+-+ + !| | | | | | | | + !+ + +-+-+-+-+-+ + +-+-+ + +-+-+-+ +-+-+ + + !| | | | | | | | | | | + !+-+-+ +-+-+-+-+-+ + + + +-+ + + + + + + + + !| | | | | | | | | | | | | | | + !+ +-+-+ + + + + +-+ + + + + + + +-+-+ + + + !| | | | | | | | | | | | | | + !+ +-+ + + + +-+-+ +-+ + +-+-+-+ +-+-+-+-+ + !| | | | | | | | | | | + !+-+-+-+ + +-+ + + + +-+-+-+ + +-+ + + + + + !| | | | | | | | | | | | | + !+ + + + +-+-+-+ + + +-+ + +-+ + + +-+ + + + !| | | | | | | | | | | | + !+ + +-+ +-+-+ +-+-+-+ +-+ + +-+ +-+ +-+ + + !| | | | | | | | | | | | | + !+-+-+ + + +-+-+ + +-+-+ +-+ + + + +-+ + + + !| | | | | | | | | | | | + !+ +-+-+-+-+-+-+-+ + + +-+ + + +-+-+ +-+ + + !| | | | | | | | | | | | + !+ + +-+-+-+-+ + + + +-+ + +-+ + + + + + + + !| | | | | | | | | | | | | | + !+ + +-+ + + + +-+-+ + + + + + +-+-+ +-+-+ + !| | | | | | | | | | | | + !+-+-+ +-+-+-+-+-+-+ +-+-+ + + + +-+-+ + + + !| | | | | | | | | | | | + !+ + +-+ +-+ +-+ +-+-+-+ +-+-+-+ + + + + + + !| | | | | | | | | | | | | | + !+ +-+ +-+ +-+ +-+ +-+ + + + + +-+ + + + + + !| | | | | | | | | | | | | | | | + !+ + +-+ + +-+ + +-+ +-+-+ + + + + +-+ + + + !| | | | | | | | | | | + !+-+-+ +-+-+ +-+-+ +-+-+ +-+-+ +-+-+ + + + + !| | | | | | | + !+ +-+-+ + + +-+-+ +-+-+ +-+-+-+-+-+-+-+ + + !| | | | O + !+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+""".stripMargin('!') lazy val maze20 = new Maze(maze20Str) - } diff --git a/src/main/scala/astar/search/Agent.scala b/src/main/scala/astar/search/Agent.scala index 3f9c1f3..b997764 100644 --- a/src/main/scala/astar/search/Agent.scala +++ b/src/main/scala/astar/search/Agent.scala @@ -1,4 +1,5 @@ -package astar.search +package astar +package search import astar.maze.{Cell, Maze} @@ -14,48 +15,55 @@ import scala.collection.mutable.ListBuffer * Author: Phillip Johnson * Date: 3/15/15 */ -class Agent(maze:Maze, heuristic: (State, Maze) => Int) { - class Direction(val dx:Int, val dy:Int) - val NORTH = new Direction(0, -1) - val SOUTH = new Direction(0, 1) - val EAST = new Direction(1, 0) - val WEST = new Direction(-1, 0) +case class State(cell: Cell) { + val (x, y) = (cell.x, cell.y) +} + +class Agent(maze: Maze, heuristic: (State, Maze) => Int) { + + case class Direction(dx: Int, dy: Int) + + val NORTH = Direction(0, -1) + val SOUTH = Direction(0, 1) + val EAST = Direction(1, 0) + val WEST = Direction(-1, 0) var searchHistory = new ListBuffer[List[Cell]] - class Successor(val move:Cell, val direction:Direction, val cost:Int) + class Successor(val move: Cell, val direction: Direction, val cost: Int) - def successors(state:State):Set[Successor] = { - Set(NORTH,SOUTH,EAST,WEST) + def successors(state: State): Set[Successor] = { + Set(NORTH, SOUTH, EAST, WEST) .withFilter(d => maze.paths.contains(new Cell(state.x + d.dx, state.y + d.dy))) .map(d => new Successor(new Cell(state.x + d.dx, state.y + d.dy), d, 1)) } - def isGoal(state:State) = new Cell(state.x, state.y) == maze.exit + def isGoal(state: State) = new Cell(state.x, state.y) == maze.exit - class FringeElement(val state:State, val path:List[Cell], val cost:Int) extends Ordered[FringeElement] { + class FringeElement(val state: State, val path: List[Cell], val cost: Int) extends Ordered[FringeElement] { override def compare(that: FringeElement): Int = { val thisCost = this.cost + heuristic.apply(this.state, maze) val thatCost = that.cost + heuristic.apply(that.state, maze) thatCost.compare(thisCost) } - override def toString = "(" + state.x + ", " + state.y + ")" + " Cells: " + path.size + " Cost: " + cost + + override def toString = s"($state.x, ${state.y}) Cells: ${path.size} Cost: $cost" } - def search():Option[List[Cell]] = { - var closed:Set[State] = Set.empty + def search(): Option[List[Cell]] = { + var closed: Set[State] = Set.empty val fringe = new mutable.PriorityQueue[FringeElement]() fringe += new FringeElement(new State(maze.entrance), List.empty, 0) - while(fringe.nonEmpty) { + while (fringe.nonEmpty) { val next = fringe.dequeue() - if(isGoal(next.state)) { + if (isGoal(next.state)) { //Bookend with the entrance and exit for clarity return Some(next.path.::(maze.exit).+:(maze.entrance)) } - if(!closed.contains(next.state)) { + if (!closed.contains(next.state)) { closed = closed + next.state - for(s <- successors(next.state)) { + for (s <- successors(next.state)) { val newPath = next.path.::(s.move) val newCost = next.cost + s.cost val expansion = new FringeElement(new State(s.move), newPath, newCost) diff --git a/src/main/scala/astar/search/State.scala b/src/main/scala/astar/search/State.scala deleted file mode 100644 index 8787aef..0000000 --- a/src/main/scala/astar/search/State.scala +++ /dev/null @@ -1,28 +0,0 @@ -package astar.search - -import astar.maze.Cell - -/** - * Author: Phillip Johnson - * Date: 3/15/15 - */ -class State(cell:Cell) { - val x = cell.x - val y = cell.y - - - def canEqual(other: Any): Boolean = other.isInstanceOf[State] - - override def equals(other: Any): Boolean = other match { - case that: State => - (that canEqual this) && - x == that.x && - y == that.y - case _ => false - } - - override def hashCode(): Int = { - val state = Seq(x, y) - state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b) - } -}