Significant adjustments to MazeFinder and data structures to improve speed and memory efficiency

main
Bryson Zimmerman 12 months ago
parent fa654e4ce8
commit 1444be1ee4

@ -5,7 +5,7 @@
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="" /> <option name="gradleJvm" value="temurin-11" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="temurin-11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="temurin-11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>

@ -5,7 +5,16 @@
</component> </component>
<component name="ChangeListManager"> <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"> <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 afterPath="$PROJECT_DIR$/src/main/kotlin/util/TrimmableArrayList.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/kotlin/MazeFinderTest.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/kotlin/TileTest.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/kotlin/TrimmableArrayListTest.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" />
<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$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/settings.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/settings.gradle.kts" 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/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/Directions.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/data/Directions.kt" afterDir="false" />
@ -23,6 +32,79 @@
<ProjectState /> <ProjectState />
</projectState> </projectState>
</component> </component>
<component name="ExternalProjectsManager">
<system id="GRADLE">
<state>
<task path="$PROJECT_DIR$">
<activation />
</task>
<projects_view>
<tree_state>
<expand>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
<item name="compileClasspath" type="62daadca:ExternalSystemViewDefaultContributor$DependencyScopeExternalSystemNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
<item name="compileClasspath" type="62daadca:ExternalSystemViewDefaultContributor$DependencyScopeExternalSystemNode" />
<item name="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0" type="b3323d55:ExternalSystemViewDefaultContributor$DependencyExternalSystemNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
<item name="runtimeClasspath" type="62daadca:ExternalSystemViewDefaultContributor$DependencyScopeExternalSystemNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
<item name="runtimeClasspath" type="62daadca:ExternalSystemViewDefaultContributor$DependencyScopeExternalSystemNode" />
<item name="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0" type="b3323d55:ExternalSystemViewDefaultContributor$DependencyExternalSystemNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
<item name="testCompileClasspath" type="62daadca:ExternalSystemViewDefaultContributor$DependencyScopeExternalSystemNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Dependencies" type="6de06a37:ExternalSystemViewDefaultContributor$MyDependenciesNode" />
<item name="testRuntimeClasspath" type="62daadca:ExternalSystemViewDefaultContributor$DependencyScopeExternalSystemNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="HierarchicalPathfinding" type="f1a62948:ProjectNode" />
<item name="Run Configurations" type="7b0102dc:RunConfigurationsNode" />
</path>
</expand>
<select />
</tree_state>
</projects_view>
</state>
</system>
</component>
<component name="FileTemplateManagerImpl"> <component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
@ -36,6 +118,9 @@
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/src/main/kotlin/util/TrimmableArrayList.kt" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="ProblemsViewState"> <component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" /> <option name="selectedTabId" value="CurrentFile" />
</component> </component>
@ -48,24 +133,53 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent"><![CDATA[{
&quot;keyToString&quot;: { "keyToString": {
&quot;Gradle.Build HierarchicalPathfinding.executor&quot;: &quot;Run&quot;, "Application.Build & Run.executor": "Profiler#1",
&quot;Gradle.TestingClassTest.basic.executor&quot;: &quot;Run&quot;, "Downloaded.Files.Path.Enabled": "false",
&quot;Gradle.Tests in 'HierarchicalPathfinding.test'.executor&quot;: &quot;Run&quot;, "Gradle.Build HierarchicalPathfinding.executor": "Run",
&quot;Kotlin.MainKt.executor&quot;: &quot;Run&quot;, "Gradle.Building Gradle Dependencies.executor": "Run",
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, "Gradle.HierarchicalPathfinding [:build].executor": "Run",
&quot;com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrary&quot;: &quot;JUnit5&quot;, "Gradle.HierarchicalPathfinding [:technology.zim.HierarchicalPathfinding.main()].executor": "Run",
&quot;com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrarySuperClass.JUnit5&quot;: &quot;&quot;, "Gradle.Load Gradle Dependencies.executor": "Run",
&quot;git-widget-placeholder&quot;: &quot;main&quot;, "Gradle.MazeFinderTest.allTilesVisited.executor": "Run",
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;, "Gradle.MazeFinderTest.bottomRowConnectedSouth.executor": "Run",
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;, "Gradle.MazeFinderTest.executor": "Run",
&quot;project.structure.last.edited&quot;: &quot;Modules&quot;, "Gradle.MazeFinderTest.isCleanedUp.executor": "Run",
&quot;project.structure.proportion&quot;: &quot;0.15&quot;, "Gradle.MazeFinderTest.topRowConnectedSouth.executor": "Run",
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;, "Gradle.TestingClassTest.basic.executor": "Run",
&quot;settings.editor.selected.configurable&quot;: &quot;reference.settingsdialog.project.gradle&quot; "Gradle.Tests in 'HierarchicalPathfinding.test'.executor": "Run",
"Gradle.TileTest.adjacentTest.executor": "Run",
"Gradle.TileTest.adjacentUnexploredTest.executor": "Run",
"Gradle.TileTest.allDirectionsTest.executor": "Run",
"Gradle.TileTest.connectBottomToSouthInvalid.executor": "Run",
"Gradle.TileTest.connectNorthSouthTest.executor": "Run",
"Gradle.TileTest.executor": "Run",
"Gradle.TileTest.outOfBoundsTest.executor": "Run",
"Gradle.TrimmableArrayListTest.emptyTest.executor": "Run",
"Gradle.TrimmableArrayListTest.executor": "Run",
"Kotlin.MainKt.executor": "Run",
"Repository.Attach.Annotations": "false",
"Repository.Attach.JavaDocs": "false",
"Repository.Attach.Sources": "false",
"RunOnceActivity.ShowReadmeOnStart": "true",
"com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrary": "JUnit5",
"com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrarySuperClass.JUnit5": "",
"git-widget-placeholder": "main",
"ignore.virus.scanning.warn.message": "true",
"kotlin-language-version-configured": "true",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"project.structure.last.edited": "Project",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.31536606",
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle",
"vue.rearranger.settings.migration": "true"
} }
}</component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CreateTestDialog.Recents.Supers"> <key name="CreateTestDialog.Recents.Supers">
<recent name="" /> <recent name="" />
@ -77,8 +191,107 @@
<recent name="technology.zim.data" /> <recent name="technology.zim.data" />
</key> </key>
</component> </component>
<component name="RunManager" selected="Gradle.TestingClassTest.basic"> <component name="RunManager" selected="Application.Build &amp; Run">
<configuration name="TestingClassTest.basic" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <configuration name="Build &amp; Run" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="technology.zim.HierarchicalPathfinding" />
<module name="HierarchicalPathfinding.main" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="HierarchicalPathfinding" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list />
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="MazeFinderTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":test" />
<option value="--tests" />
<option value="&quot;MazeFinderTest&quot;" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
<configuration name="TileTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":test" />
<option value="--tests" />
<option value="&quot;TileTest&quot;" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
<configuration name="TileTest.adjacentTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":test" />
<option value="--tests" />
<option value="&quot;TileTest.adjacentTest&quot;" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
<configuration name="TileTest.adjacentUnexploredTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings> <ExternalSystemSettings>
<option name="executionName" /> <option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
@ -91,7 +304,7 @@
<list> <list>
<option value=":test" /> <option value=":test" />
<option value="--tests" /> <option value="--tests" />
<option value="&quot;TestingClassTest.basic&quot;" /> <option value="&quot;TileTest.adjacentUnexploredTest&quot;" />
</list> </list>
</option> </option>
<option name="vmOptions" /> <option name="vmOptions" />
@ -102,7 +315,7 @@
<RunAsTest>true</RunAsTest> <RunAsTest>true</RunAsTest>
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="Tests in 'HierarchicalPathfinding.test'" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> <configuration name="TrimmableArrayListTest.emptyTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings> <ExternalSystemSettings>
<option name="executionName" /> <option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
@ -114,6 +327,8 @@
<option name="taskNames"> <option name="taskNames">
<list> <list>
<option value=":test" /> <option value=":test" />
<option value="--tests" />
<option value="&quot;TrimmableArrayListTest.emptyTest&quot;" />
</list> </list>
</option> </option>
<option name="vmOptions" /> <option name="vmOptions" />
@ -124,13 +339,33 @@
<RunAsTest>true</RunAsTest> <RunAsTest>true</RunAsTest>
<method v="2" /> <method v="2" />
</configuration> </configuration>
<list>
<item itemvalue="Application.Build &amp; Run" />
<item itemvalue="Gradle.HierarchicalPathfinding" />
<item itemvalue="Gradle.TileTest" />
<item itemvalue="Gradle.TileTest.adjacentTest" />
<item itemvalue="Gradle.TileTest.adjacentUnexploredTest" />
<item itemvalue="Gradle.MazeFinderTest" />
<item itemvalue="Gradle.TrimmableArrayListTest.emptyTest" />
</list>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Gradle.TestingClassTest.basic" /> <item itemvalue="Gradle.MazeFinderTest" />
<item itemvalue="Gradle.Tests in 'HierarchicalPathfinding.test'" /> <item itemvalue="Gradle.TileTest.adjacentTest" />
<item itemvalue="Gradle.TileTest.adjacentUnexploredTest" />
<item itemvalue="Gradle.TileTest" />
<item itemvalue="Gradle.TrimmableArrayListTest.emptyTest" />
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-28b599e66164-intellij.indexing.shared.core-IU-242.23339.11" />
<option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-IU-242.23339.11" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager"> <component name="TaskManager">
<task active="true" id="Default" summary="Default task"> <task active="true" id="Default" summary="Default task">
@ -139,6 +374,11 @@
<option name="number" value="Default" /> <option name="number" value="Default" />
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1728574259273</updated> <updated>1728574259273</updated>
<workItem from="1729631402625" duration="4007000" />
<workItem from="1729635718708" duration="9320000" />
<workItem from="1729647683265" duration="4068000" />
<workItem from="1729689482885" duration="6548000" />
<workItem from="1729708627987" duration="3017000" />
</task> </task>
<task id="LOCAL-00001" summary="Project Setup"> <task id="LOCAL-00001" summary="Project Setup">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -188,9 +428,20 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1729362135731</updated> <updated>1729362135731</updated>
</task> </task>
<option name="localTasksCounter" value="7" /> <task id="LOCAL-00007" summary="Implemented MazeFinder, significant changes to supporting data structures to match developed understanding of Kotlin and Object-Oriented philosophy">
<option name="closed" value="true" />
<created>1729560909423</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1729560909423</updated>
</task>
<option name="localTasksCounter" value="8" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties"> <component name="Vcs.Log.Tabs.Properties">
<option name="OPEN_GENERIC_TABS"> <option name="OPEN_GENERIC_TABS">
<map> <map>
@ -237,4 +488,24 @@
<MESSAGE value="Implemented MazeFinder, significant changes to supporting data structures to match developed understanding of Kotlin and Object-Oriented philosophy" /> <MESSAGE value="Implemented MazeFinder, significant changes to supporting data structures to match developed understanding of Kotlin and Object-Oriented philosophy" />
<option name="LAST_COMMIT_MESSAGE" value="Implemented MazeFinder, significant changes to supporting data structures to match developed understanding of Kotlin and Object-Oriented philosophy" /> <option name="LAST_COMMIT_MESSAGE" value="Implemented MazeFinder, significant changes to supporting data structures to match developed understanding of Kotlin and Object-Oriented philosophy" />
</component> </component>
<component name="XDebuggerManager">
<pin-to-top-manager>
<pinned-members>
<PinnedItemInfo parentTag="technology.zim.util.TrimmableArrayList" memberName="data" />
<PinnedItemInfo parentTag="technology.zim.MazeFinder" memberName="visited" />
</pinned-members>
</pin-to-top-manager>
<watches-manager>
<configuration name="GradleRunConfiguration">
<watch expression="((MazeFinder)this).frontier" language="kotlin" custom="import technology.zim.MazeFinder" />
<watch expression="((MazeFinder)this).visited" custom="technology.zim.MazeFinder" />
<watch expression="((TrimmableArrayList)((MazeFinder)this).frontier).data" custom="technology.zim.MazeFinder,technology.zim.util.TrimmableArrayList" />
<watch expression="((TrimmableArrayList)((MazeFinder)this).frontier).data" custom="technology.zim.MazeFinder,technology.zim.util.TrimmableArrayList" />
</configuration>
</watches-manager>
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
<select />
</component>
</project> </project>

@ -11,6 +11,10 @@ repositories {
dependencies { dependencies {
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
implementation(kotlin("stdlib-jdk8"))
// https://mvnrepository.com/artifact/it.unimi.dsi/dsiutils
implementation("it.unimi.dsi:dsiutils:2.7.3")
} }
tasks.test { tasks.test {
@ -18,4 +22,5 @@ tasks.test {
} }
kotlin { kotlin {
jvmToolchain(11) jvmToolchain(11)
jvmToolchain(8)
} }

@ -1,3 +1,8 @@
pluginManagement {
plugins {
kotlin("jvm") version "2.0.21"
}
}
plugins { plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
} }

@ -9,6 +9,21 @@ Minimum int size is probably 32 or 64 bit, so huge memory waste if that's used
Abandon fancy stretch goals, just get pathfinding done and scale it up to demonstrate large scale Abandon fancy stretch goals, just get pathfinding done and scale it up to demonstrate large scale
*/ */
fun main() { class HierarchicalPathfinding {
println("Hello World!")
companion object {
@JvmStatic
fun main(args: Array<String>) {
val n = 1000
World.setSize(n, n)
val startTime = System.currentTimeMillis()
MazeFinder.primsAlgorithm()
val endTime = System.currentTimeMillis()
println(World.toString())
println(n*n)
println((endTime - startTime).toString() + "ms")
}
}
} }

@ -1,7 +1,13 @@
package technology.zim package technology.zim
import it.unimi.dsi.util.XoRoShiRo128PlusRandom
import technology.zim.data.Directions import technology.zim.data.Directions
import technology.zim.data.Tile import technology.zim.data.Tile
import technology.zim.data.TileProperties
import technology.zim.util.TrimmableArrayList
import kotlin.collections.ArrayList
import kotlin.collections.forEach
import kotlin.random.Random
//Build the maze //Build the maze
@ -14,64 +20,60 @@ import technology.zim.data.Tile
//http://weblog.jamisbuck.org/2011/1/10/maze-generation-prim-s-algorithm //http://weblog.jamisbuck.org/2011/1/10/maze-generation-prim-s-algorithm
//http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm //http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm
class MazeFinder { object MazeFinder {
val visited = ArrayList<ArrayList<Boolean>>(World.tiles.data.size).apply { val frontier = TrimmableArrayList<Tile>()
this.forEach() { element -> element.fill(false) } var startingTile = World.getRandomLocation()
} val randGen = XoRoShiRo128PlusRandom()
val frontier = mutableSetOf<Tile>()
val tempInGraph = ArrayList<Pair<Tile, TileProperties>>()
fun primsAlgorithm() { fun primsAlgorithm() {
cleanUp()
//val randomGen = Random
startingTile = World.getRandomLocation()
startingTile.getProperties().visited = true
//Choose an arbitrary vertex from G (the graph), and add it to some (initially empty) set V. //Choose an arbitrary vertex from G (the graph), and add it to some (initially empty) set V.
frontier.add(World.getRandomLocation()) frontier.addAll(startingTile.getAdjacentTiles(false))
//Choose a random edge from G, that connects a vertex in V with another vertex not in V. //Choose a random edge from G, that connects a vertex in V with another vertex not in V.
var current: Tile var current: Tile
var prospect: Pair<Tile, Directions> var inGraph: Pair<Tile, Directions>
var possibleTiles: Set<Pair<Tile, Directions>> var adjacentExplored: Set<Pair<Tile, Directions>>
while(frontier.isNotEmpty()) { while(frontier.isNotEmpty()) {
//Grab a random tile from the frontier, mark it as visited //Grab a random tile from the frontier, mark it as visited
current = frontier.random() val random = randGen.nextInt(frontier.size())
frontier.remove(current) current = frontier.data[random]
visited[current.value.first][current.value.second] = true current.getProperties().visited = true
frontier.removeAt(random)
//Find all adjacent tiles to that tile
possibleTiles = getUnexploredAdjacent(current)
//It's possible that this frontier tile has no unexplored neighbors, in which case removing it from the frontier is enough //Find adjacent tiles that are in the graph
if(possibleTiles.isNotEmpty()) { adjacentExplored = current.getAdjacent(true)
//Grab a random tile from the possible set
prospect = possibleTiles.random()
//Connect it to the current tile //Select a random tile from possibleTiles
current.connect(prospect.second) inGraph = adjacentExplored.elementAt(randGen.nextInt(adjacentExplored.size))
//Connect the frontier tile to the graph
current.connect(inGraph.second)
//Add current's unexplored tiles to the frontier, if not already in frontier
frontier.addAll(current.getAdjacentTiles(false))
//print(World.toString())
//println("--------------------------------------------")
//Add its adjacent unexplored tiles to frontier
frontier.addAll(getUnexploredAdjacentWithoutDirection(prospect.first))
}
} }
println("prim")
} }
fun getUnexploredAdjacentWithoutDirection(tile: Tile): Set<Tile> {
val result = mutableSetOf<Tile>()
getUnexploredAdjacent(tile).forEach { pair -> result.add(pair.first)}
return result
}
fun getUnexploredAdjacent(tile: Tile): Set<Pair<Tile, Directions>> { fun cleanUp() {
val adj = mutableSetOf<Pair<Tile, Directions>>() //Clean up the frontier
val dirs = Directions.NORTH.all() frontier.clear()
dirs.forEach { dir ->
val candidateTile = tile + dir
//Ensure that the tile is within bounds
if(candidateTile.isInBounds() && !visited[candidateTile.value.first][candidateTile.value.second])
{
adj.add(Pair(candidateTile, dir))
}
}
return adj
} }
//Todo: Consider growing World with null values, then use Mazefinder to instantiate as the maze is developed //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 //Consequence: World becomes nullable, requiring null checks to be added in order to avoid... issues
//It's probable that this will only be needed at large scales //It's probable that this will only be needed at large scales

@ -18,17 +18,57 @@ import java.util.*
object World { object World {
//Default size should be 20 //Default size should be 20
val tiles = WorldData(ArrayList<ArrayList<TileProperties>>(20)) val tiles = WorldData(ArrayList<ArrayList<TileProperties>>())
//Returns a coordinate pair //Returns a coordinate pair
fun getRandomLocation(): Tile { fun getRandomLocation(): Tile {
return Tile(Pair((0..tiles.data.size).random(), (0..tiles.data[0].size).random())) return Tile(Pair((0..tiles.value.size-1).random(), (0..tiles.value[0].size-1).random()))
} }
fun setSize(x: Int, y: Int) { fun setSize(x: Int, y: Int) {
tiles.setSize(x, y) tiles.setSize(x, y)
} }
//TODO: https://en.wikipedia.org/wiki/Box-drawing_characters
//^ make the maze look like a maze
override fun toString(): String {
val em = ' ' //Empty character
val fi = '█' //Filled character
val dot = '•'
val str = StringBuilder()
//Reading left to right, top to bottom - can do a simple for each on the rows
for(y in 0..tiles.value[0].size- 1) {
//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())
str.append(fi)
else
str.append(em)
}
//End upper line
str.appendLine()
//Lower line: Print downward connections
for (x in 0..tiles.value.size - 1) {
if(tiles.value[x][y].isSouth()) {
str.append(fi)
}
else
str.append(em)
str.append(em)
}
//End lower line
str.appendLine()
}
str.appendLine()
return str.toString()
}
//TODO: toString method //TODO: toString method
//Reads array left to right, top to bottom //Reads array left to right, top to bottom
//Only looks at SOUTH and EAST connections //Only looks at SOUTH and EAST connections

@ -1,12 +1,12 @@
package technology.zim.data package technology.zim.data
enum class Directions(val value: Pair<Int, Int>) { enum class Directions(val value: Pair<Int, Int>) {
NORTH(Pair(0, 1)) { NORTH(Pair(0, -1)) {
override fun opposite(): Directions { override fun opposite(): Directions {
return SOUTH return SOUTH
} }
}, },
SOUTH(Pair(0, -1)) { SOUTH(Pair(0, 1)) {
override fun opposite(): Directions { override fun opposite(): Directions {
return NORTH return NORTH
} }
@ -29,4 +29,12 @@ enum class Directions(val value: Pair<Int, Int>) {
return mutableSetOf(NORTH, SOUTH, EAST, WEST) return mutableSetOf(NORTH, SOUTH, EAST, WEST)
} }
fun x():Int {
return value.first
}
fun y():Int {
return value.second
}
} }

@ -1,9 +1,9 @@
package technology.zim.data package technology.zim.data
import technology.zim.MazeFinder
import technology.zim.World import technology.zim.World
@JvmInline class Tile(val value: Pair<Int, Int>): Comparable<Tile> {
value class Tile(val value: Pair<Int, Int>) {
constructor(x: Int, y: Int): this(Pair(x, y)) constructor(x: Int, y: Int): this(Pair(x, y))
@ -16,22 +16,55 @@ value class Tile(val value: Pair<Int, Int>) {
if(candidateTile.isInBounds()) if(candidateTile.isInBounds())
{ {
this.getProperties().add(dir) this.getProperties().add(dir)
(this + dir).getProperties().add(dir.opposite()) candidateTile.getProperties().add(dir.opposite())
candidateTile.getProperties().visited = true
candidateTile.getProperties().visited = true
} }
else { else {
//TODO: Consider just silently not connecting out-of-bounds values //Shouldn't matter whether we skip connecting an out-of-bounds item
//Should also never get to the point where the attempt is made
println("Attempted to connect to outside bounds: <" + println("Attempted to connect to outside bounds: <" +
candidateTile.value.first + ", " + candidateTile.value.second candidateTile.x() + ", " + candidateTile.y()
+ "> From Tile: <" + this.value.first + ", " + + "> From Tile: <" + x() + ", " +
this.value.second + ">") y() + ">")
return return
} }
} }
fun getAdjacentTiles(explored:Boolean): Set<Tile> {
val result = mutableSetOf<Tile>()
getAdjacent(explored).forEach {
pair ->
result.add(pair.first)
}
return result
}
fun getAdjacent(explored:Boolean): Set<Pair<Tile, Directions>> {
val adj = mutableSetOf<Pair<Tile, Directions>>()
val dirs = Directions.NORTH.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()
}
//Gets Tile objects for all connected directions //Gets Tile objects for all connected directions
//Used for finding a path through the maze //Used for finding a path through the maze
fun getConnections(): MutableSet<Tile> { fun getConnections(): ArrayList<Tile> {
val listTiles = mutableSetOf<Tile>() val listTiles = ArrayList<Tile>()
for (dir: Directions in getProperties().connections) { for (dir: Directions in getProperties().connections) {
//Use the ghost of linear algebra to identify potential neighbor tiles //Use the ghost of linear algebra to identify potential neighbor tiles
listTiles.add(this+dir) listTiles.add(this+dir)
@ -39,25 +72,63 @@ value class Tile(val value: Pair<Int, Int>) {
return listTiles return listTiles
} }
//Arguments could be made for either World or Tile knowing whether a Tile is in bounds
fun isInBounds(): Boolean { fun isInBounds(): Boolean {
return value.first > 0 && return x() >= 0 &&
value.second > 0 && y() >= 0 &&
value.first < World.tiles.data.size && x() < World.tiles.value.size &&
value.second < World.tiles.data[value.second].size y() < World.tiles.value[0].size
} }
//Get the properties of the tile at the given coordinates //Get the properties of the tile at the given coordinates
fun getProperties(): TileProperties { fun getProperties(): TileProperties {
return World.tiles.data.elementAt(value.first).elementAt(value.second) return World.tiles.value[x()][y()]
} }
//Get tile at given direction //Get tile at given direction
operator fun plus(dir: Directions): Tile { operator fun plus(dir: Directions): Tile {
return Tile(value.first + dir.value.first, value.second + dir.value.second) return Tile(x() + dir.x(), y() + dir.y())
} }
//Get tile at direction opposite of given direction //Get tile at direction opposite of given direction
operator fun minus(dir: Directions): Tile { operator fun minus(dir: Directions): Tile {
return Tile(value.first - dir.value.first, value.second - dir.value.second) return Tile(x() - dir.x(), y() - dir.y())
}
fun x(): Int {
return value.first
} }
fun y():Int {
return value.second
}
override fun toString():String {
return "<" + value.first + ", " + value.second + ">"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Tile) return false
if (x() != other.x() || y() != other.y()) return false
return true
}
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 hashCode(): Int {
return value.hashCode()
}
} }

@ -11,9 +11,10 @@ import technology.zim.World.tiles
@JvmInline /*@JvmInline
value class TileProperties(val connections:MutableSet<Directions> = mutableSetOf<Directions>()) { value */
class TileProperties(val connections:MutableSet<Directions> = mutableSetOf<Directions>()) {
var visited = false
//Remove a direction from the list of connections //Remove a direction from the list of connections
fun remove(dir: Directions) { fun remove(dir: Directions) {
connections.remove(dir) connections.remove(dir)
@ -25,4 +26,20 @@ value class TileProperties(val connections:MutableSet<Directions> = mutableSetOf
connections.add(dir) connections.add(dir)
} }
fun isWest(): Boolean {
return connections.contains(Directions.WEST)
}
fun isEast():Boolean {
return connections.contains(Directions.EAST)
}
fun isNorth(): Boolean {
return connections.contains(Directions.NORTH)
}
fun isSouth():Boolean {
return connections.contains(Directions.SOUTH)
}
} }

@ -4,20 +4,23 @@ package technology.zim.data
@JvmInline @JvmInline
value class WorldData(val data: ArrayList<ArrayList<TileProperties>>) { value class WorldData(val value: ArrayList<ArrayList<TileProperties>>) {
fun setSize(xmin : Int, ymin : Int) { fun setSize(xmin : Int, ymin : Int) {
//println("WorldData setting to: " + xmin + ", " + ymin)
//Fill every column with an ArrayList of TileProperties //Fill every column with an ArrayList of TileProperties
with(data) { value.removeAll({true})
this.ensureCapacity(xmin)
this.fill(ArrayList<TileProperties>()) for(i in 0..xmin-1) {
value.add(ArrayList<TileProperties>())
} }
//Fill every row with TileProperties //Fill every row with TileProperties
for(y in data) { for(x in value) {
with(y) { for(i in 0..ymin-1) {
this.ensureCapacity((ymin)) x.add(TileProperties())
this.fill(TileProperties())
} }
} }
//println("WorldData now sized at: " + value.size + ", " + value[0].size)
} }
} }

@ -0,0 +1,49 @@
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)]
}
}

@ -0,0 +1,70 @@
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
import technology.zim.MazeFinder
import technology.zim.World
import technology.zim.data.Tile
import kotlin.test.BeforeTest
class MazeFinderTest {
@BeforeTest
fun setup() {
World.setSize(10, 10)
MazeFinder.primsAlgorithm()
}
@Test
fun topRowConnectedSouth() {
var southExists = false
World.tiles.value.forEach {
col ->
if(col[0].isSouth())
southExists = true
}
assert(southExists)
}
@Test
fun bottomRowConnectedSouth() {
var southNotExists = true
World.tiles.value.forEach {
col ->
if(col[9].isSouth())
southNotExists = false
}
assert(southNotExists)
}
@Test
fun allTilesVisited() {
World.tiles.value.forEach {
col ->
col.forEach {
tile ->
assert(tile.visited)
}
}
}
@Test
fun allTilesHaveConnections() {
World.tiles.value.forEachIndexed {
x,
col ->
col.forEachIndexed { y,
tileprop ->
if(tileprop.connections.isEmpty())
println("Empty Connections at: " + Tile(x, y).toString())
assert(tileprop.connections.isNotEmpty())
}
}
}
@Test
fun isCleanedUp() {
MazeFinder.cleanUp()
assert(MazeFinder.frontier.isEmpty())
}
}

@ -0,0 +1,103 @@
import org.junit.jupiter.api.Test
import kotlin.test.BeforeTest
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.TestFactory
import technology.zim.MazeFinder
import technology.zim.World
import technology.zim.data.*
import technology.zim.data.Directions.*
class TileTest {
@BeforeTest
fun setup() {
World.setSize(10, 10)
MazeFinder.cleanUp()
}
@Test
fun connectBottomToSouthInvalid() {
val bottom = Tile(1, 9)
bottom.connect(SOUTH)
assertFalse(bottom.getConnections().contains(bottom+SOUTH))
}
@Test
fun outOfBoundsTest() {
val tooNorth = Tile(1, -1)
val tooWest = Tile(-1, 1)
val tooEast = Tile(10, 1)
val tooSouth = Tile(1, 10)
val northIn = Tile(0, 0)
val southIn = Tile(9, 9)
assert(northIn.isInBounds())
assert(southIn.isInBounds())
assertFalse(tooNorth.isInBounds())
assertFalse(tooWest.isInBounds())
assertFalse(tooEast.isInBounds())
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
assert(someTile.getProperties().isNorth())
assertFalse(someTile.getProperties().isSouth())
assertFalse(someTile.getProperties().isWest())
assertFalse(someTile.getProperties().isEast())
assert(northTile.getProperties().isSouth())
assertFalse(northTile.getProperties().isNorth())
assertFalse(northTile.getProperties().isWest())
assertFalse(northTile.getProperties().isEast())
}
@Test
fun adjacentTest() {
val someTile = Tile(1, 1)
val northTile = someTile + NORTH
val southTile = someTile + SOUTH
val westTile = someTile + WEST
val eastTile = someTile + EAST
//Generating raw tiles without built-in bounds checking, do it here
if(northTile.isInBounds())
northTile.getProperties().visited = true
if(southTile.isInBounds())
southTile.getProperties().visited = true
val adjacent = someTile.getAdjacentTiles(false)
assertFalse(adjacent.contains(northTile))
assertFalse(adjacent.contains(southTile))
assert(adjacent.contains(eastTile))
assert(adjacent.contains(westTile))
val explored = someTile.getAdjacentTiles(true)
assert(explored.elementAt(0) == northTile)
assert(explored.contains(northTile))
assert(explored.contains(southTile))
assertFalse(explored.contains(eastTile))
assertFalse(explored.contains(westTile))
adjacent.forEach {
it ->
println(it.toString())
}
}
}

@ -0,0 +1,28 @@
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