Skip to content

Commit 84dd3bb

Browse files
authored
Merge pull request #197 from antonsoo/fix/multi-endpoint-predecessors
Retain weighted predecessors in many-to-many path queries
2 parents e974570 + 5762316 commit 84dd3bb

3 files changed

Lines changed: 23 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version
88

99
### Fixed
1010

11+
- Many-to-many `tcod.path.path2d` queries retain weighted predecessors when reconstructing their route.
1112
- Copying or pickling Perlin and Simplex noise no longer advances a shared random generator while preparing an unused wavelet tile.
1213
- `tcod.image.Image.rotate90` updates the width and height of rectangular images, keeping NumPy array conversions consistent with the rotated pixels.
1314
- Adding a root to an already resolved `tcod.path.Pathfinder` resets its predecessor only when its value improves the current distance. This prevents cyclic paths while preserving routes to cheaper roots.

‎tcod/path.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,10 +1559,10 @@ def path2d( # noqa: C901, PLR0912, PLR0913
15591559
out = out[::-1]
15601560
return out
15611561

1562-
# Crude Dijkstra implementation until issues with Pathfinder are fixed
1562+
# Resolve every endpoint before choosing the nearest start.
15631563
pf.resolve(None)
15641564
best_distance, best_ij = min((pf.distance[ij], ij) for ij in start_points)
15651565
if best_distance == np.iinfo(pf.distance.dtype).max:
15661566
return np.zeros((0, 2), dtype=np.intc) # All endpoints unreachable
15671567

1568-
return hillclimb2d(pf.distance, best_ij, cardinal=bool(cardinal), diagonal=bool(diagonal))
1568+
return pf.path_from(best_ij)

‎tests/test_path2d_predecessors.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Many-to-many paths must retain movement costs during reconstruction."""
2+
3+
import numpy as np
4+
import pytest
5+
6+
import tcod.path
7+
8+
9+
@pytest.mark.parametrize("diagonal", [0, 1, 2, 3])
10+
def test_path2d_diagonal_predecessors(diagonal: int) -> None:
11+
"""An expensive diagonal must not replace two cheaper cardinal moves."""
12+
cost = np.ones((4, 4), dtype=np.int16)
13+
path = tcod.path.path2d(
14+
cost, start_points=[(0, 0), (0, 1)], end_points=[(2, 2), (3, 3)], cardinal=1, diagonal=diagonal
15+
)
16+
total = sum(diagonal if np.all(a != b) else 1 for a, b in zip(path, path[1:]))
17+
assert total == (2 if diagonal == 1 else 3)
18+
assert tuple(path[0]) in [(0, 0), (0, 1)]
19+
assert tuple(path[-1]) in [(2, 2), (3, 3)]
20+
np.testing.assert_array_equal(cost, 1)

0 commit comments

Comments
 (0)