Updated MazeFinder and supporting structures to improve performance and implement tools for the PathFinder

main
Bryson Zimmerman 11 months ago
parent 8f274458a9
commit 6a06ba20d3

@ -19,7 +19,12 @@ class HierarchicalPathfinding {
World.setSize(n, n)
println("Start")
val startTime = System.currentTimeMillis()
try {
MazeFinder.primsAlgorithm()
} catch(e: Exception) {
println(World.toString())
println(e.message)
}
val endTime = System.currentTimeMillis()
println(World.toString())

@ -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<Tile>()
var startingTile = World.getRandomLocation()
val frontier = ArrayList<Tile>()
val randGen = XoRoShiRo128PlusRandom()
val tempInGraph = ArrayList<Tile>()
fun <E> ArrayList<E>.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<Tile>
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

@ -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()
}
}

@ -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)
{

@ -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()) {

@ -1,49 +0,0 @@
package technology.zim.util
import kotlin.random.Random
class TrimmableArrayList<E> {
val data = ArrayList<E>()
fun addAll(c: Collection<E>) {
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)]
}
}

@ -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<Tile>()
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)
}
}
Loading…
Cancel
Save