Skip to content

Commit 9cc2ff4

Browse files
committed
Fix concurrency on graph render, relax path condition
1 parent 669ebe1 commit 9cc2ff4

File tree

7 files changed

+52
-37
lines changed

7 files changed

+52
-37
lines changed

common/src/main/kotlin/com/lambda/pathing/PathingConfig.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ interface PathingConfig {
6161
val renderG: Boolean
6262
val renderRHS: Boolean
6363
val renderKey: Boolean
64+
val renderQueue: Boolean
6465
val maxRenderObjects: Int
6566
val fontScale: Double
6667
val assumeJesus: Boolean

common/src/main/kotlin/com/lambda/pathing/PathingSettings.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class PathingSettings(
7070
override val renderG by c.setting("Render G", false) { vis() && page == Page.Debug && renderGraph }
7171
override val renderRHS by c.setting("Render RHS", false) { vis() && page == Page.Debug && renderGraph }
7272
override val renderKey by c.setting("Render Key", false) { vis() && page == Page.Debug && renderGraph }
73+
override val renderQueue by c.setting("Render Queue", false) { vis() && page == Page.Debug && renderGraph }
7374
override val maxRenderObjects by c.setting("Max Render Objects", 1000, 0..10_000, 100) { vis() && page == Page.Debug && renderGraph }
7475
override val fontScale by c.setting("Font Scale", 0.4, 0.0..2.0, 0.01) { vis() && renderGraph && page == Page.Debug }
7576

common/src/main/kotlin/com/lambda/pathing/incremental/DStarLite.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ class DStarLite(
272272
if (config.renderG) label.add("g: %.3f".format(g(origin)))
273273
if (config.renderRHS) label.add("rhs: %.3f".format(rhs(origin)))
274274
if (config.renderKey) label.add("k: ${calculateKey(origin)}")
275-
if (origin in U) label.add("IN QUEUE")
275+
if (config.renderQueue && origin in U) label.add("QUEUED")
276276

277277
if (label.isNotEmpty()) {
278278
val pos = origin.toCenterVec3d()

common/src/main/kotlin/com/lambda/pathing/incremental/LazyGraph.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,26 @@ import kotlin.math.abs
4343
class LazyGraph(
4444
val nodeInitializer: (FastVector) -> Map<FastVector, Double>
4545
) {
46-
val successors = ConcurrentHashMap<FastVector, MutableMap<FastVector, Double>>()
47-
val predecessors = ConcurrentHashMap<FastVector, MutableMap<FastVector, Double>>()
46+
val successors = ConcurrentHashMap<FastVector, ConcurrentHashMap<FastVector, Double>>()
47+
val predecessors = ConcurrentHashMap<FastVector, ConcurrentHashMap<FastVector, Double>>()
4848

4949
val nodes get() = successors.keys + predecessors.keys
5050
val size get() = nodes.size
5151

5252
/** Initializes a node if not already initialized, then returns successors. */
53-
fun successors(u: FastVector): MutableMap<FastVector, Double> =
53+
fun successors(u: FastVector): ConcurrentHashMap<FastVector, Double> =
5454
successors.getOrPut(u) {
55-
nodeInitializer(u).onEach { (neighbor, cost) ->
56-
predecessors.getOrPut(neighbor) { hashMapOf() }[u] = cost
57-
}.toMutableMap()
55+
ConcurrentHashMap(nodeInitializer(u).onEach { (neighbor, cost) ->
56+
predecessors.getOrPut(neighbor) { ConcurrentHashMap() }[u] = cost
57+
})
5858
}
5959

6060
/** Initializes predecessors by ensuring successors of neighboring nodes. */
61-
fun predecessors(u: FastVector): MutableMap<FastVector, Double> =
61+
fun predecessors(u: FastVector): ConcurrentHashMap<FastVector, Double> =
6262
predecessors.getOrPut(u) {
63-
nodeInitializer(u).onEach { (neighbor, cost) ->
64-
successors.getOrPut(neighbor) { hashMapOf() }[u] = cost
65-
}.toMutableMap()
63+
ConcurrentHashMap(nodeInitializer(u).onEach { (neighbor, cost) ->
64+
successors.getOrPut(neighbor) { ConcurrentHashMap() }[u] = cost
65+
})
6666
}
6767

6868
fun removeNode(u: FastVector) {
@@ -84,8 +84,8 @@ class LazyGraph(
8484
}
8585

8686
fun setCost(u: FastVector, v: FastVector, c: Double) {
87-
successors.getOrPut(u) { hashMapOf() }[v] = c
88-
predecessors.getOrPut(v) { hashMapOf() }[u] = c
87+
successors.getOrPut(u) { ConcurrentHashMap() }[v] = c
88+
predecessors.getOrPut(v) { ConcurrentHashMap() }[u] = c
8989
}
9090

9191
fun clear() {

common/src/test/kotlin/pathing/DStarLiteTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.lambda.util.GraphUtil.createGridGraph18Conn
2424
import com.lambda.util.GraphUtil.createGridGraph26Conn
2525
import com.lambda.util.GraphUtil.createGridGraph6Conn
2626
import com.lambda.util.GraphUtil.euclideanHeuristic
27+
import com.lambda.util.GraphUtil.length
2728
import com.lambda.util.GraphUtil.manhattanHeuristic
2829
import com.lambda.util.world.FastVector
2930
import com.lambda.util.world.fastVectorOf
@@ -384,6 +385,6 @@ internal class DStarLiteTest {
384385
}
385386

386387
// The new path should go around the blocked node
387-
assertTrue(newPath.size >= initialPath.size, "New path should be at least as long as the initial path")
388+
assertTrue(newPath.length() >= initialPath.length(), "New path should be at least as long as the initial path")
388389
}
389390
}

common/src/test/kotlin/pathing/GraphConsistencyTest.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ class GraphConsistencyTest {
7373
val path2 = dStar2.path()
7474

7575
// Verify paths are identical
76-
assertEquals(blocked, path2,
77-
"Paths should be identical after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
76+
assertEquals(blocked.length(), path2.length(),
77+
"Paths should have identical length after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
7878

7979
// Verify the graph structure is consistent
8080
val (graphDifferences, valueDifferences) = dStar1.compareWith(dStar2)
@@ -129,8 +129,8 @@ class GraphConsistencyTest {
129129
val path2 = dStar2.path()
130130

131131
// Verify paths are identical
132-
assertEquals(blocked, path2,
133-
"Paths should be identical after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
132+
assertEquals(blocked.length(), path2.length(),
133+
"Paths should have identical length after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
134134

135135
// Verify the graph structure is consistent
136136
val (graphDifferences, valueDifferences) = dStar1.compareWith(dStar2)
@@ -176,8 +176,8 @@ class GraphConsistencyTest {
176176
val path2 = dStar2.path()
177177

178178
// Verify paths are identical
179-
assertEquals(blocked, path2,
180-
"Paths should be identical after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
179+
assertEquals(blocked.length(), path2.length(),
180+
"Paths should have identical length after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
181181

182182
// Verify graph structure is consistent
183183
val (graphDifferences, valueDifferences) = dStar1.compareWith(dStar2)
@@ -223,8 +223,8 @@ class GraphConsistencyTest {
223223
val path2 = dStar2.path()
224224

225225
// Verify paths are identical
226-
assertEquals(blocked, path2,
227-
"Paths should be identical after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
226+
assertEquals(blocked.length(), path2.length(),
227+
"Paths should have identical length after invalidation.\nPath1: ${blocked.string()}\nPath2: ${path2.string()}")
228228

229229
// Verify graph structure is consistent
230230
val (graphDifferences, valueDifferences) = dStar1.compareWith(dStar2)
@@ -274,8 +274,8 @@ class GraphConsistencyTest {
274274
val path2 = dStar2.path()
275275

276276
// Verify paths are identical
277-
assertEquals(path1, path2,
278-
"Paths should be identical after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
277+
assertEquals(path1.length(), path2.length(),
278+
"Paths should have identical length after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
279279

280280
// Verify graph structure is consistent
281281
val (graphDifferences, valueDifferences) = dStar1.compareWith(dStar2)

common/src/test/kotlin/pathing/PathConsistencyTest.kt

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,15 @@ class PathConsistencyTest {
7373
dStar2.computeShortestPath()
7474
val path2 = dStar2.path()
7575

76-
// Verify paths are identical
77-
assertEquals(path1, path2,
78-
"Paths should be identical after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
76+
// Verify paths have the same length (there can be multiple valid paths)
77+
assertEquals(path1.length(), path2.length(),
78+
"Paths should have the same length after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
79+
80+
// Verify both paths avoid blocked nodes
81+
blockedNodes.forEach { blocked ->
82+
assertTrue(!path1.contains(blocked), "Path1 should not contain blocked node ${blocked.string}")
83+
assertTrue(!path2.contains(blocked), "Path2 should not contain blocked node ${blocked.string}")
84+
}
7985
}
8086

8187
/**
@@ -126,9 +132,15 @@ class PathConsistencyTest {
126132
dStar2.computeShortestPath()
127133
val path2 = dStar2.path()
128134

129-
// Verify paths are identical
130-
assertEquals(path1, path2,
131-
"Paths should be identical after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
135+
// Verify paths have the same length (there can be multiple valid paths)
136+
assertEquals(path1.length(), path2.length(),
137+
"Paths should have the same length after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
138+
139+
// Verify both paths avoid blocked nodes
140+
blockedNodes.forEach { blocked ->
141+
assertTrue(!path1.contains(blocked), "Path1 should not contain blocked node ${blocked.string}")
142+
assertTrue(!path2.contains(blocked), "Path2 should not contain blocked node ${blocked.string}")
143+
}
132144
}
133145

134146
/**
@@ -199,8 +211,8 @@ class PathConsistencyTest {
199211
val path2 = dStar2.path()
200212

201213
// Verify paths are identical
202-
assertEquals(path1, path2,
203-
"Paths should be identical after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
214+
assertEquals(path1.length(), path2.length(),
215+
"Paths should be same length after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
204216
}
205217

206218
/**
@@ -231,7 +243,7 @@ class PathConsistencyTest {
231243

232244
// Unblock one node in the middle
233245
val nodeToToggle = fastVectorOf(0, 0, 5)
234-
blockedNodes.remove(nodeToToggle)
246+
if (blockedNodes.remove(nodeToToggle)) println("Unblocked node ${nodeToToggle.string}")
235247

236248
// Now it should path through the hole in the wall
237249
dStar1.invalidate(nodeToToggle)
@@ -245,8 +257,8 @@ class PathConsistencyTest {
245257
val path2 = dStar2.path()
246258

247259
// Verify paths are identical
248-
assertEquals(path1, path2,
249-
"Paths should be identical after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
260+
assertEquals(path1.length(), path2.length(),
261+
"Paths should be same length after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
250262
}
251263

252264
/**
@@ -291,8 +303,8 @@ class PathConsistencyTest {
291303
val path2 = dStar2.path()
292304

293305
// Verify paths are identical
294-
assertEquals(path1, path2,
295-
"Paths should be identical after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
306+
assertEquals(path1.length(), path2.length(),
307+
"Paths should be same length after invalidation.\nPath1: ${path1.string()}\nPath2: ${path2.string()}")
296308
}
297309

298310
/**

0 commit comments

Comments
 (0)