main
Bryson Zimmerman 11 months ago
parent d37ba3448c
commit 2a433508c7

@ -1,10 +1,13 @@
package technology.zim package technology.zim
import technology.zim.data.Directions
import technology.zim.data.Tile import technology.zim.data.Tile
import technology.zim.data.TileNavigatedArray
object BFSPathFinder { object BFSPathFinder {
//In this particular situation, only a single outcome is likely. Thus, BFS always finds the perfect (and only) path //In this particular situation, only a single outcome is likely. Thus, BFS always finds the perfect (and only) path
//Skipping the Heap data structure allows better utilization of on-die cache //Skipping the Heap data structure allows better utilization of on-die cache
var gVals:TileNavigatedArray<Int> = TileNavigatedArray<Int>()
fun generatePath(start: Tile, end: Tile) { fun generatePath(start: Tile, end: Tile) {
if (!start.isInBounds() || !end.isInBounds()) { if (!start.isInBounds() || !end.isInBounds()) {
@ -15,21 +18,48 @@ object BFSPathFinder {
println("Ouroboros detected") println("Ouroboros detected")
return return
} }
gVals = TileNavigatedArray<Int>(World.sizeX, World.sizeY, false)
//Queue for tiles to be checked //Queue for tiles to be checked
val frontier = ArrayDeque<Tile>() val frontier = ArrayDeque<Tile>()
frontier.addLast(start) frontier.addLast(start)
gVals.set(start, 0)
World.update(start, Directions.BFSFRONTIER)
var current:Tile
while (frontier.isNotEmpty()) { while (frontier.isNotEmpty()) {
//Grab the next tile //Grab the next tile and its gValue
current = frontier.removeFirst()
val currentG = gVals.get(current)?:Int.MAX_VALUE-1.also{throw IndexOutOfBoundsException("Couldn't get gValue in BFS")}
// Mark it explored // add its unexplored neighbors and update their gVals
current.getConnections().forEach {
//record its distance-cost tile ->
if(!World.get(tile).isBFSFrontier()) {
// add its unexplored neighbors World.update(tile, Directions.BFSFRONTIER)
frontier.addLast(tile)
gVals.set(tile, currentG + 1)
}
}
} }
markPath(start, end)
}
fun markPath(start: Tile, end:Tile) {
//Step through the path from end until start
var current = end
var lowestG = Int.MAX_VALUE
var lowestCost = end
while(current != start) {
World.update(current, current.getProperties() + Directions.BFSINPATH)
current.getConnections().forEach { candidateTile ->
val candidateG = gVals.get(candidateTile) ?: -1
if(candidateTile.isInBounds() && candidateG != -1 && candidateG < lowestG ) {
lowestG = candidateG
lowestCost = candidateTile
}
}
current = lowestCost
}
World.update(start, start.getProperties() + Directions.BFSINPATH)
} }
} }

@ -16,19 +16,27 @@ class HierarchicalPathfinding {
} }
println("Pathfinding") println("Pathfinding")
val BFSPathFinderTime = measureTime {
BFSPathFinder.generatePath(Tile(0, 0), Tile(n-1, n-1))
}
val ArrayBackedPathfinderTime = measureTime { val ArrayBackedPathfinderTime = measureTime {
ArrayBackedPathfinder.generatePath(Tile(0, 0), Tile(n - 1, (n - 1))) ArrayBackedPathfinder.generatePath(Tile(0, 0), Tile(n - 1, (n - 1)))
} }
/*
val MapBackedPathfinderTime = measureTime { val MapBackedPathfinderTime = measureTime {
MapBackedPathfinder.generatePath(Tile(0, 0), Tile(n - 1, (n - 1))) MapBackedPathfinder.generatePath(Tile(0, 0), Tile(n - 1, (n - 1)))
} }
*/
println(World.toString()) println(World.toString())
println(n*n) println(n*n)
println("Maze build time: ${buildMazeTime.inWholeMilliseconds} ms") println("Maze build time: ${buildMazeTime.inWholeMilliseconds} ms")
println("HashMap-Backed Pathfinder time: ${MapBackedPathfinderTime.inWholeMilliseconds}ms") //println("HashMap-Backed Pathfinder time: ${MapBackedPathfinderTime.inWholeMilliseconds}ms")
println("Array-Backed Pathfinder time: ${ArrayBackedPathfinderTime.inWholeMilliseconds}ms") println("Array-Backed Pathfinder time: ${ArrayBackedPathfinderTime.inWholeMilliseconds}ms")
println("BFS Pathfinder time: ${BFSPathFinderTime.inWholeMilliseconds}ms")
} }
//Clear the maze of pathfinding markers before running another pathfinding algorithm //Clear the maze of pathfinding markers before running another pathfinding algorithm

@ -34,6 +34,9 @@ object World {
const val ANSI_CYAN = "\u001B[36m" const val ANSI_CYAN = "\u001B[36m"
const val ANSI_WHITE = "\u001B[37m" const val ANSI_WHITE = "\u001B[37m"
fun update(tile: Tile, dir: Directions) {
update(tile, get(tile) + dir)
}
fun update(tile: Tile, to: TileProperties) { fun update(tile: Tile, to: TileProperties) {
tiles.set(tile, to) tiles.set(tile, to)
@ -74,7 +77,7 @@ object World {
for (y in 0..sizeY - 1) { for (y in 0..sizeY - 1) {
//Upper line: Print each tile, print right-hand connections //Upper line: Print each tile, print right-hand connections
for (x in 0..sizeX - 1) { for (x in 0..sizeX - 1) {
if(get(Tile(x, y)).connections and(INPATH.dir) != 0) if(get(Tile(x, y)).connections and(INPATH.dir+BFSINPATH.dir) != 0)
inPath = true inPath = true
else if (get(Tile(x, y)).connections and(FRONTIER.dir) != 0) else if (get(Tile(x, y)).connections and(FRONTIER.dir) != 0)
checked = true checked = true

@ -5,6 +5,8 @@ import technology.zim.World
//Tile is a ULong that represents the X,Y coordinates of a Tile //Tile is a ULong that represents the X,Y coordinates of a Tile
//Contains functions necessary for accessing and manipulating Tiles //Contains functions necessary for accessing and manipulating Tiles
//TODO: Untangle visited maths
@JvmInline @JvmInline
value class Tile(private val value: ULong) { value class Tile(private val value: ULong) {
@ -40,6 +42,7 @@ value class Tile(private val value: ULong) {
return Directions.convertModifier(Tile(otherTile.x() - this.x(), otherTile.y() - this.y()).value) return Directions.convertModifier(Tile(otherTile.x() - this.x(), otherTile.y() - this.y()).value)
} }
//Get adjacent tiles for Prim's Algorithm
fun getAdjacentTiles(explored:Boolean): Set<Tile> { fun getAdjacentTiles(explored:Boolean): Set<Tile> {
val adj = mutableSetOf<Tile>() val adj = mutableSetOf<Tile>()
val dirs = Directions.ALL val dirs = Directions.ALL
@ -111,6 +114,7 @@ value class Tile(private val value: ULong) {
return "<" + x() + ", " + y() + ">" return "<" + x() + ", " + y() + ">"
} }
//Get connections for pathfinding algorithms
fun getConnections(): Set<Tile> { fun getConnections(): Set<Tile> {
val connections = mutableSetOf<Tile>() val connections = mutableSetOf<Tile>()
val properties = getProperties() val properties = getProperties()

@ -58,6 +58,10 @@ value class TileProperties(val connections: Int) {
return connections and(MANIFEST.dir) == MANIFEST.dir return connections and(MANIFEST.dir) == MANIFEST.dir
} }
fun isBFSFrontier(): Boolean {
return connections and(BFSFRONTIER.dir) == BFSFRONTIER.dir
}
override fun toString():String { override fun toString():String {
val ret = StringBuilder() val ret = StringBuilder()
if(isWest()) { if(isWest()) {

Loading…
Cancel
Save