From 6a06ba20d3d17877ea804993eb21e92329ccfa8d Mon Sep 17 00:00:00 2001 From: Bryson Zimmerman Date: Sat, 26 Oct 2024 18:31:34 -0400 Subject: [PATCH] Updated MazeFinder and supporting structures to improve performance and implement tools for the PathFinder --- src/main/kotlin/Main.kt | 7 ++- src/main/kotlin/MazeFinder.kt | 60 ++++++++++------------ src/main/kotlin/data/Directions.kt | 9 +++- src/main/kotlin/data/Tile.kt | 1 - src/main/kotlin/data/TileProperties.kt | 6 ++- src/main/kotlin/util/TrimmableArrayList.kt | 49 ------------------ src/test/kotlin/TrimmableArrayListTest.kt | 28 ---------- 7 files changed, 46 insertions(+), 114 deletions(-) delete mode 100644 src/main/kotlin/util/TrimmableArrayList.kt delete mode 100644 src/test/kotlin/TrimmableArrayListTest.kt diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 35c584e..2be77ab 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -19,7 +19,12 @@ class HierarchicalPathfinding { World.setSize(n, n) println("Start") val startTime = System.currentTimeMillis() - MazeFinder.primsAlgorithm() + try { + MazeFinder.primsAlgorithm() + } catch(e: Exception) { + println(World.toString()) + println(e.message) + } val endTime = System.currentTimeMillis() println(World.toString()) diff --git a/src/main/kotlin/MazeFinder.kt b/src/main/kotlin/MazeFinder.kt index 7be9cee..a5c5f64 100644 --- a/src/main/kotlin/MazeFinder.kt +++ b/src/main/kotlin/MazeFinder.kt @@ -3,7 +3,6 @@ package technology.zim import it.unimi.dsi.util.XoRoShiRo128PlusRandom import technology.zim.data.Directions import technology.zim.data.Tile -import technology.zim.util.TrimmableArrayList import kotlin.collections.ArrayList @@ -18,60 +17,49 @@ import kotlin.collections.ArrayList //http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm object MazeFinder { - val frontier = TrimmableArrayList() - var startingTile = World.getRandomLocation() + val frontier = ArrayList() val randGen = XoRoShiRo128PlusRandom() - val tempInGraph = ArrayList() + fun ArrayList.swapRemove(index: Int) { + this[index] = this[this.lastIndex] + this.removeLast() + } fun primsAlgorithm() { - cleanUp() - //val randomGen = Random - - //Prime the graph with the first connection, which marks the first visited Tiles - startingTile = World.getRandomLocation() + val startingTile = World.getRandomLocation() val tmpIndex = randGen.nextInt(Directions.ALL.size) val connectorTile = startingTile + Directions.ALL[tmpIndex] + + //Connect the first two tiles so they're recognized as in the graph startingTile.connect(connectorTile) - tempInGraph.add(startingTile) - tempInGraph.add(connectorTile) - //Choose an arbitrary vertex from G (the graph), and add it to some (initially empty) set V. + //Add their unexplored adjacent tiles to the frontier frontier.addAll(startingTile.getAdjacentTiles(false)) frontier.addAll((connectorTile.getAdjacentTiles(false))) - //Choose a random edge from G, that connects a vertex in V with another vertex not in V. + var current: Tile var inGraph: Tile var adjacentExplored: Set while(frontier.isNotEmpty()) { - //Grab a random tile from the frontier, mark it as visited - val random = randGen.nextInt(frontier.size()) - current = frontier.data.get(random) - frontier.removeAt(random) - + //Grab a random tile from the frontier + val random = randGen.nextInt(frontier.size) + current = frontier.get(random) + frontier.swapRemove(random) //Find adjacent tiles that are in the graph adjacentExplored = current.getAdjacentTiles(true) - if(adjacentExplored.isEmpty()) - println("") - - if(adjacentExplored.isEmpty()) { - val world = World.tiles - println ("No explored adjacent tiles found") - } - //Select a random tile from possibleTiles + //Select a random explored tile inGraph = adjacentExplored.elementAt(randGen.nextInt(adjacentExplored.size)) - //Connect the frontier tile to the graph + //Connect the frontier tile to the explored tile current.connect(inGraph) - tempInGraph.add(current) - //Add current's unexplored tiles to the frontier, if not already in frontier - frontier.addAll(current.getAdjacentTiles(false)) + //Look around the frontier tile for unexplored tiles, add them to the frontier + manifestDestiny(current) //print(World.toString()) //println("--------------------------------------------") @@ -80,9 +68,15 @@ object MazeFinder { println("prim") } - fun cleanUp() { - //Clean up the frontier - frontier.clear() + private fun manifestDestiny(current: Tile) { + current.getAdjacentTiles(false).forEach { + tile -> + val tileprops = World.get(tile) + if(!tileprops.isManifested()) { + World.update(tile, tileprops + Directions.MANIFEST) + frontier.add(tile) + } + } } //Todo: Consider growing World with null values, then use Mazefinder to instantiate as the maze is developed //Consequence: World becomes nullable, requiring null checks to be added in order to avoid... issues diff --git a/src/main/kotlin/data/Directions.kt b/src/main/kotlin/data/Directions.kt index a296144..60e80a4 100644 --- a/src/main/kotlin/data/Directions.kt +++ b/src/main/kotlin/data/Directions.kt @@ -5,9 +5,15 @@ enum class Directions(val dir: Int) { UP(1), DOWN(2), LEFT(4), - RIGHT(8); + RIGHT(8), + INPATH(16), //Chosen by the pathfinder + CHECKED(32), //Checked by the pathfinder + MANIFEST(64), //Checked by MazeFinder + ; companion object { + + //Todo: This breaks the connect() function when used fun opposite(dir: Directions): Directions { return when(dir) { UP -> DOWN @@ -48,4 +54,5 @@ enum class Directions(val dir: Int) { fun inv(): Int { return dir.inv() } + } \ No newline at end of file diff --git a/src/main/kotlin/data/Tile.kt b/src/main/kotlin/data/Tile.kt index df2a0fc..dba8935 100644 --- a/src/main/kotlin/data/Tile.kt +++ b/src/main/kotlin/data/Tile.kt @@ -48,7 +48,6 @@ value class Tile(private val value: ULong) { dirs.forEach { dir -> val candidateTile = this + dir - //Ensure that the tile is within bounds if(candidateTile.isInBounds() && World.get(candidateTile).visited() == explored) { diff --git a/src/main/kotlin/data/TileProperties.kt b/src/main/kotlin/data/TileProperties.kt index 1bdfc1a..6561a59 100644 --- a/src/main/kotlin/data/TileProperties.kt +++ b/src/main/kotlin/data/TileProperties.kt @@ -15,7 +15,7 @@ import technology.zim.data.Directions.* value class TileProperties(val connections: Int) { fun visited(): Boolean { - return connections != 0 + return connections and(UP.dir+DOWN.dir+LEFT.dir+RIGHT.dir )!= 0 } //Remove a direction from the list of connections @@ -53,6 +53,10 @@ value class TileProperties(val connections: Int) { return connections and(DOWN.dir) == DOWN.dir } + fun isManifested(): Boolean { + return connections and(MANIFEST.dir) == MANIFEST.dir + } + override fun toString():String { val ret = StringBuilder() if(isWest()) { diff --git a/src/main/kotlin/util/TrimmableArrayList.kt b/src/main/kotlin/util/TrimmableArrayList.kt deleted file mode 100644 index f4f203a..0000000 --- a/src/main/kotlin/util/TrimmableArrayList.kt +++ /dev/null @@ -1,49 +0,0 @@ -package technology.zim.util - -import kotlin.random.Random - -class TrimmableArrayList { - val data = ArrayList() - - fun addAll(c: Collection) { - data.addAll(c) - } - - fun isNotEmpty(): Boolean { - return data.isNotEmpty() - } - - fun isEmpty(): Boolean { - return data.isEmpty() - } - - fun add(item: E) { - data.add(item) - - } - - fun size():Int { - return data.size - } - - fun clear() { - data.clear() - } - - fun removeAt(index: Int) { - data[index] = data[data.size - 1] - data.removeLast() - } - - //O(n) remove operation - fun remove(item: E) { - data[data.indexOf(item)] = data[data.size - 1] - data.removeLast() - } - - - fun random(): E { - return data[Random.nextInt(data.size)] - } - -} \ No newline at end of file diff --git a/src/test/kotlin/TrimmableArrayListTest.kt b/src/test/kotlin/TrimmableArrayListTest.kt deleted file mode 100644 index bc85de4..0000000 --- a/src/test/kotlin/TrimmableArrayListTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -import technology.zim.data.Tile -import technology.zim.util.TrimmableArrayList -import kotlin.test.BeforeTest -import kotlin.test.Test - -class TrimmableArrayListTest { - - @Test - fun emptyTest() { - val empty = TrimmableArrayList() - assert(empty.isEmpty()) - - empty.add(Tile(0, 0)) - empty.remove(Tile(0, 0)) - assert(empty.isEmpty()) - - empty.addAll(listOf(Tile(0, 0), Tile(1, 1), Tile(2, 2))) - assert(empty.isNotEmpty()) - - empty.remove(Tile(0, 0)) - println("realSize: " + empty.size() + " Size: " + empty.data.size) - assert(empty.size() == 2) - - - } - - -} \ No newline at end of file