"""Tests for the square grid: distance, step costs, corner rule, Dijkstra.""" from __future__ import annotations from pf1e_simulator.grid import Grid from pf1e_simulator.map import MapSpec, TerrainType def make_grid(rows: list[str]) -> Grid: legend = { "#": TerrainType(type="wall", move_cost=None, blocks_los=True), ".": TerrainType(type="floor", move_cost=1), "T": TerrainType(type="rubble", move_cost=2), "C": TerrainType(type="pillar", move_cost=None, cover=True), } spec = MapSpec(name="test", terrain=tuple(rows), legend=legend) return Grid.from_spec(spec) def test_distance_diagonal_table() -> None: grid = make_grid([".....", ".....", ".....", ".....", "....."]) assert grid.distance((0, 0), (1, 1)) == 1 assert grid.distance((0, 0), (2, 2)) == 3 assert grid.distance((0, 0), (3, 2)) == 4 assert grid.distance((0, 0), (0, 3)) == 3 assert grid.distance((2, 1), (0, 0)) == 2 def test_step_cost_orthogonal_uses_entered_square() -> None: grid = make_grid([".T."]) assert grid.step_cost((0, 0), (0, 1), 0) == 2 assert grid.step_cost((0, 1), (0, 2), 0) == 1 def test_step_cost_diagonal_alternates() -> None: grid = make_grid(["...", "...", "..."]) assert grid.step_cost((0, 0), (1, 1), 0) == 1 assert grid.step_cost((1, 1), (2, 2), 1) == 2 assert grid.step_cost((1, 1), (2, 2), 2) == 1 def test_step_cost_diagonal_into_difficult_is_flat_three() -> None: grid = make_grid(["..", ".T"]) assert grid.step_cost((0, 0), (1, 1), 0) == 3 assert grid.step_cost((0, 0), (1, 1), 1) == 3 def test_diagonal_forbidden_past_wall_corner() -> None: grid = make_grid([ ".#.", ".#.", "...", ]) assert grid.diagonal_allowed((2, 1), (1, 0)) is False assert grid.diagonal_allowed((2, 1), (1, 2)) is False def test_diagonal_allowed_with_both_flanks_clear() -> None: grid = make_grid([ ".#.", "...", "...", ]) assert grid.diagonal_allowed((2, 1), (1, 0)) is True def test_reachable_open_floor_costs() -> None: grid = make_grid([".....", ".....", ".....", ".....", "....."]) reached = grid.reachable((0, 0), 10, frozenset()) assert reached[(0, 0)] == 0 assert reached[(1, 1)] == 1 assert reached[(2, 2)] == 3 assert reached[(3, 3)] == 4 assert reached[(4, 4)] == 6 assert reached[(0, 4)] == 4 def test_reachable_respects_budget() -> None: grid = make_grid([".....", ".....", ".....", ".....", "....."]) reached = grid.reachable((0, 0), 2, frozenset()) assert (1, 1) in reached assert (2, 2) not in reached def test_reachable_diagonal_into_rubble_costs_three() -> None: grid = make_grid(["...", ".T.", "..."]) reached = grid.reachable((0, 0), 6, frozenset()) assert reached[(1, 1)] == 3 def test_reachable_through_rubble_cheaper_than_around() -> None: grid = make_grid(["...", ".T.", "..."]) reached = grid.reachable((1, 0), 6, frozenset()) assert reached[(1, 2)] == 3 def test_reachable_creatures_block_entry_not_diagonals() -> None: grid = make_grid(["...", "...", "..."]) reached = grid.reachable((1, 1), 1, frozenset({(1, 0)})) assert (1, 0) not in reached assert reached[(0, 2)] == 1 def test_reachable_paths_around_creatures() -> None: grid = make_grid(["...", ".#.", "..."]) reached = grid.reachable((1, 0), 6, frozenset()) assert (1, 1) not in reached assert reached[(1, 2)] == 4 def test_reachable_cannot_enter_wall_squares() -> None: grid = make_grid(["..", ".C"]) reached = grid.reachable((0, 0), 9, frozenset()) assert (1, 1) not in reached def test_reachable_unbounded_budget_covers_whole_map() -> None: grid = make_grid([".#.", "..."]) reached = grid.reachable((0, 0), None, frozenset()) assert reached == {(0, 0): 0, (1, 0): 1, (1, 1): 2, (1, 2): 3, (0, 2): 4}