Brroken: Changes to backing data structures in the name of speed

main
Bryson Zimmerman 12 months ago
parent 14e9b99189
commit c07952bec1

@ -0,0 +1,29 @@
<component name="libraryTable">
<library name="it.unimi.dsi.dsiutils" type="repository">
<properties maven-id="it.unimi.dsi:dsiutils:2.7.3" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/it/unimi/dsi/dsiutils/2.7.3/dsiutils-2.7.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.3/slf4j-api-2.0.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-core/1.3.4/logback-core-1.3.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-classic/1.3.4/logback-classic-1.3.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/activation/1.1/activation-1.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/it/unimi/dsi/fastutil/8.5.12/fastutil-8.5.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/it/unimi/di/jsap/20210129/jsap-20210129.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-configuration2/2.8.0/commons-configuration2-2.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.9/commons-text-1.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.2/commons-logging-1.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -5,13 +5,15 @@
</component>
<component name="ChangeListManager">
<list default="true" id="741ceb24-e493-47c1-9f25-1dbd8b151381" name="Changes" comment="Implemented MazeFinder, significant changes to supporting data structures to match developed understanding of Kotlin and Object-Oriented philosophy">
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/Main.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/Mazefinder.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/MazeFinder.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/MazeFinder.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/MazeFinder.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/World.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/World.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/data/Directions.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/data/Directions.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/data/Tile.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/data/Tile.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/data/TileProperties.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/data/TileProperties.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/data/WorldData.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/data/WorldData.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/test/kotlin/TileTest.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/kotlin/TileTest.kt" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />

@ -14,7 +14,7 @@ class HierarchicalPathfinding {
companion object {
@JvmStatic
fun main(args: Array<String>) {
val n = 5000
val n = 10
println("Building world")
World.setSize(n, n)
println("Start")

@ -31,31 +31,37 @@ object MazeFinder {
//Prime the graph with the first connection, which marks the first visited Tiles
startingTile = World.getRandomLocation()
startingTile.getProperties().visited = true
val tmpIndex = randGen.nextInt(Directions.ALL.size)
startingTile.connect(Directions.ALL.get(tmpIndex))
//Choose an arbitrary vertex from G (the graph), and add it to some (initially empty) set V.
frontier.addAll(startingTile.getAdjacentTiles(false))
frontier.addAll((startingTile + Directions.ALL.get(tmpIndex)).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: Pair<Tile, Directions>
var adjacentExplored: Set<Pair<Tile, Directions>>
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[random]
current.getProperties().visited = true
current = frontier.data.get(random)
current.getProperties().visited()
frontier.removeAt(random)
//Find adjacent tiles that are in the graph
adjacentExplored = current.getAdjacent(true)
adjacentExplored = current.getAdjacentTiles(true)
if(adjacentExplored.isEmpty())
println("No explored adjacent tiles found")
//Select a random tile from possibleTiles
inGraph = adjacentExplored.elementAt(randGen.nextInt(adjacentExplored.size))
//Connect the frontier tile to the graph
current.connect(inGraph.second)
current.connect(Directions.convertModifier(current.value - inGraph.value))
//Add current's unexplored tiles to the frontier, if not already in frontier
frontier.addAll(current.getAdjacentTiles(false))

@ -20,9 +20,13 @@ object World {
//Default size should be 20
val tiles = WorldData(ArrayList<ArrayList<TileProperties>>())
fun update(tile: Tile, to: TileProperties) {
tiles.value[tile.x()][tile.y()] = to
}
//Returns a coordinate pair
fun getRandomLocation(): Tile {
return Tile(Pair((0..tiles.value.size-1).random(), (0..tiles.value[0].size-1).random()))
return Tile((0..tiles.value.size-1).random(), (0..tiles.value.get(0).size-1).random())
}
fun setSize(x: Int, y: Int) {
@ -45,7 +49,7 @@ object World {
//Upper line: Print each tile, print right-hand connections
for (x in 0..tiles.value[0].size - 1) {
str.append(fi)
if(tiles.value[x][y].isEast())
if(tiles.value.get(x).get(y).isEast())
str.append(fi)
else
str.append(em)
@ -55,7 +59,7 @@ object World {
//Lower line: Print downward connections
for (x in 0..tiles.value.size - 1) {
if(tiles.value[x][y].isSouth()) {
if(tiles.value.get(x).get(y).isSouth()) {
str.append(fi)
}
else

@ -1,40 +1,51 @@
package technology.zim.data
enum class Directions(val value: Pair<Int, Int>) {
NORTH(Pair(0, -1)) {
override fun opposite(): Directions {
return SOUTH
}
},
SOUTH(Pair(0, 1)) {
override fun opposite(): Directions {
return NORTH
}
},
EAST(Pair(1, 0)) {
override fun opposite(): Directions {
return WEST
}
},
WEST(Pair(-1, 0)) {
override fun opposite(): Directions {
return EAST
enum class Directions(val dir: Int) {
NONE(0),
UP(1),
DOWN(2),
LEFT(4),
RIGHT(8),
VISITED(16)
;
companion object {
fun getModifier(dir: Directions): Long {
return when(dir) {
UP -> NORTH
DOWN -> SOUTH
LEFT -> WEST
RIGHT -> EAST
else -> 0L
}
}
};
//Return the opposite direction
abstract fun opposite(): Directions
fun convertModifier(mod: Long): Directions {
return when(mod) {
NORTH -> UP
SOUTH -> DOWN
WEST -> LEFT
EAST -> RIGHT
else -> NONE
}
}
fun x():Int {
return value.first
const val SOUTH: Long = 0x1L
const val NORTH: Long = -0x1L
const val WEST: Long = -0x100000000L
const val EAST: Long = 0x100000000L
val ALL = arrayOf(UP, DOWN, LEFT, RIGHT)
const val NONEMOD = 0L
}
fun y():Int {
return value.second
fun inv(): Int {
return dir.inv()
}
companion object {
val ALL = setOf(NORTH, EAST, WEST, SOUTH)
fun opposite(dir: Long): Long {
val x = (dir shr 32).toInt() * -1
val y = dir.toInt() * -1
return x.toLong() shl 32 + y
}
}

@ -2,10 +2,21 @@ package technology.zim.data
import technology.zim.World
class Tile(val value: Pair<Int, Int>): Comparable<Tile> {
@JvmInline
value class Tile(val value: Long) {
constructor(x: Int, y: Int): this(Pair(x, y))
constructor(x: Int, y: Int): this(pack(x, y)) {
}
fun connect(candidateTile: Tile) {
val dir = Directions.convertModifier(this.value - candidateTile.value)
if(candidateTile.isInBounds()) {
World.update(this, getProperties().add(dir))
}
else {
throw(ArrayIndexOutOfBoundsException("Cannot connect to an out of bounds tile"))
}
}
//Connect two tiles together.
//Calls utility function on the connected cell
fun connect(dir: Directions) {
@ -14,10 +25,8 @@ class Tile(val value: Pair<Int, Int>): Comparable<Tile> {
//Ensure that the tile is within bounds
if(candidateTile.isInBounds())
{
this.getProperties().add(dir)
candidateTile.getProperties().add(dir.opposite())
candidateTile.getProperties().visited = true
candidateTile.getProperties().visited = true
World.tiles.value[x()][y()] = getProperties().add(dir)
World.tiles.value[candidateTile.x()][candidateTile.y()] = candidateTile.getProperties().add(dir)
}
else {
//Shouldn't matter whether we skip connecting an out-of-bounds item
@ -39,7 +48,7 @@ class Tile(val value: Pair<Int, Int>): Comparable<Tile> {
dirs.forEach { dir ->
val candidateTile = this + dir
//Ensure that the tile is within bounds
if(candidateTile.isInBounds() && candidateTile.getProperties().visited == explored)
if(candidateTile.isInBounds() && candidateTile.getProperties().visited() == explored)
{
adj.add(candidateTile)
}
@ -47,95 +56,72 @@ class Tile(val value: Pair<Int, Int>): Comparable<Tile> {
return adj
}
fun getAdjacent(explored:Boolean): Set<Pair<Tile, Directions>> {
val adj = mutableSetOf<Pair<Tile, Directions>>()
val dirs = Directions.ALL
dirs.forEach { dir ->
val candidateTile = this + dir
//Ensure that the tile is within bounds
if(candidateTile.isInBounds() && candidateTile.getProperties().visited == explored)
{
adj.add(Pair(candidateTile, dir))
}
}
return adj
}
fun hasConnections(): Boolean {
return getProperties().connections.isNotEmpty()
return getProperties().connections != 0
}
//Gets Tile objects for all connected directions
//Used for finding a path through the maze
fun getConnections(): ArrayList<Tile> {
val listTiles = ArrayList<Tile>()
for (dir: Directions in getProperties().connections) {
//Use the ghost of linear algebra to identify potential neighbor tiles
listTiles.add(this+dir)
}
return listTiles
}
//Arguments could be made for either World or Tile knowing whether a Tile is in bounds
fun isInBounds(): Boolean {
return x() >= 0 &&
y() >= 0 &&
x() < World.tiles.value.size &&
y() < World.tiles.value[0].size
y() < World.tiles.value.get(0).size
}
//Get the properties of the tile at the given coordinates
fun getProperties(): TileProperties {
return World.tiles.value[x()][y()]
return World.tiles.value.get(x()).get(y())
}
//Get tile at given direction
operator fun plus(dir: Directions): Tile {
return Tile(x() + dir.x(), y() + dir.y())
return Tile(x() + x(dir), y() + y(dir))
}
//Get tile at direction opposite of given direction
operator fun minus(dir: Directions): Tile {
return Tile(x() - dir.x(), y() - dir.y())
operator fun minus(dir: Long): Tile {
return Tile(x() - x(dir), y() - y(dir))
}
fun x(): Int {
return value.first
return (value shr 32).toInt()
}
fun y():Int {
return value.second
//Gets the x value of the given coordinate form
fun x(coord: Long):Int {
return (coord shr 32).toInt()
}
override fun toString():String {
return "<" + value.first + ", " + value.second + ">"
//Gets the x value of the coordinate form of the given direction
fun x(dir: Directions):Int {
return (Directions.getModifier(dir) shr 32).toInt()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Tile) return false
fun y():Int {
return value.toInt()
}
if (x() != other.x() || y() != other.y()) return false
//Gets the y value of the given coordinate form
fun y(coord: Long):Int {
return coord.toInt()
}
return true
//Gets the y value of the coordinate form of the given direction
fun y(dir: Directions):Int {
return y() + Directions.getModifier(dir).toInt()
}
override fun compareTo(other: Tile): Int {
if(x() == other.x() && y() == other.y())
return 0
else if(y() > other.y())
return 1
else if(x() > other.x())
return 1
else
return -1
override fun toString():String {
return "<" + x() + ", " + y() + ">"
}
override fun hashCode(): Int {
return value.hashCode()
companion object {
fun pack(x: Int, y: Int):Long {
return (x.toLong() shl 32) + y.toLong()
}
}
}

@ -1,5 +1,8 @@
package technology.zim.data
import technology.zim.data.Directions.*
//Data holder for a Tile
//Should contain a mutable set of connected directions
//Later, can hold jumps to other locations other such fancy features
@ -8,35 +11,53 @@ package technology.zim.data
/*@JvmInline
value */
class TileProperties(val connections:MutableSet<Directions> = mutableSetOf<Directions>()) {
var visited = false
@JvmInline
value class TileProperties(val connections: Int) {
fun visited(): Boolean {
return connections != 0
}
//Remove a direction from the list of connections
fun remove(dir: Directions) {
connections.remove(dir)
fun remove(dir: Directions): TileProperties {
return TileProperties(connections and(dir.inv()))
}
//Add a direction to the list of connections
//Should only be accessed by the Tile class
fun add(dir: Directions) {
connections.add(dir)
fun add(dir: Directions): TileProperties {
return TileProperties(connections or(dir.dir))
}
fun isWest(): Boolean {
return connections.contains(Directions.WEST)
return connections and(LEFT.dir) != 0
}
fun isEast():Boolean {
return connections.contains(Directions.EAST)
return connections and(RIGHT.dir) != 0
}
fun isNorth(): Boolean {
return connections.contains(Directions.NORTH)
return connections and(UP.dir) != 0
}
fun isSouth():Boolean {
return connections.contains(Directions.SOUTH)
return connections and(DOWN.dir) != 0
}
infix fun and(dir: Directions): TileProperties {
return TileProperties(connections and(dir.dir))
}
infix fun and(dir: Int): TileProperties {
return TileProperties(connections and(dir))
}
infix fun or(dir: Directions): TileProperties {
return TileProperties(connections or(dir.dir))
}
infix fun or(dir: Int): TileProperties {
return TileProperties(connections or(dir))
}
}

@ -2,7 +2,6 @@ package technology.zim.data
//Data structure wrapper for a set of tiles
@JvmInline
value class WorldData(val value: ArrayList<ArrayList<TileProperties>>) {
fun setSize(xmin : Int, ymin : Int) {
@ -17,10 +16,14 @@ value class WorldData(val value: ArrayList<ArrayList<TileProperties>>) {
//Fill every row with TileProperties
for(x in value) {
for(i in 0..ymin-1) {
x.add(TileProperties())
x.add(emptyTile)
}
}
//println("WorldData now sized at: " + value.size + ", " + value[0].size)
}
companion object {
val emptyTile = TileProperties(0)
}
}

@ -15,10 +15,10 @@ class TileTest {
}
@Test
fun connectBottomToSouthInvalid() {
fun connectOutOfBoundsInvalid() {
val bottom = Tile(1, 9)
bottom.connect(SOUTH)
assertFalse(bottom.getConnections().contains(bottom+SOUTH))
bottom.connect(DOWN)
assertFalse(bottom.getProperties().isSouth())
}
@Test
@ -40,18 +40,13 @@ class TileTest {
assertFalse(tooSouth.isInBounds())
}
@Test
fun allDirectionsTest() {
val dirs = NORTH.all()
assert(dirs.containsAll(listOf(NORTH, EAST, SOUTH, WEST)))
}
@Test
fun connectNorthSouthTest() {
//Assumes the world is at least 10, 10 in size as set up by this test class
val someTile = Tile(5, 5)
someTile.connect(NORTH)
val northTile = someTile + NORTH
someTile.connect(UP)
val northTile = someTile + UP
assert(someTile.getProperties().isNorth())
assertFalse(someTile.getProperties().isSouth())
@ -67,16 +62,16 @@ class TileTest {
@Test
fun adjacentTest() {
val someTile = Tile(1, 1)
val northTile = someTile + NORTH
val southTile = someTile + SOUTH
val westTile = someTile + WEST
val eastTile = someTile + EAST
val northTile = someTile + UP
val southTile = someTile + DOWN
val westTile = someTile + LEFT
val eastTile = someTile + RIGHT
//Generating raw tiles without built-in bounds checking, do it here
if(northTile.isInBounds())
northTile.getProperties().visited = true
northTile.getProperties().visited()
if(southTile.isInBounds())
southTile.getProperties().visited = true
southTile.getProperties().visited()
val adjacent = someTile.getAdjacentTiles(false)

Loading…
Cancel
Save