Spaces:
Sleeping
Sleeping
| """Unit tests for the template scenario (64x64 open field, multi-cell sprites).""" | |
| from __future__ import annotations | |
| import random | |
| import proteus.game.scenarios # noqa: F401 (registers scenarios) | |
| from proteus.game.engine.difficulty import Difficulty | |
| from proteus.game.engine.grid import MotiveGridGame | |
| from proteus.game.scenarios.base import get_scenario | |
| def _game(play_turns=20): | |
| scenario = get_scenario("template")() | |
| game = MotiveGridGame(scenario, random.Random(42), Difficulty.EASY, max_steps=play_turns) | |
| return scenario, game | |
| def test_build_is_64x64_with_multicell_sprites(): | |
| scenario, game = _game() | |
| assert scenario.grid_size == (64, 64) | |
| focal, predator = game.focal_sprite, game.predator_sprite | |
| assert (focal.width, focal.height) == (2, 2) | |
| assert (predator.width, predator.height) == (3, 3) | |
| # Disjoint at start (not already eaten). | |
| assert not scenario.check_elimination(game) | |
| def test_eat_fires_on_footprint_overlap_not_adjacency(): | |
| scenario, game = _game() | |
| focal, predator = game.focal_sprite, game.predator_sprite | |
| # Place predator footprint just touching but NOT overlapping the focal: | |
| focal.move(-focal.x, -focal.y) # focal anchor -> (0,0), occupies x,y in [0,2) | |
| predator.move(2 - predator.x, -predator.y) # predator anchor -> (2,0): x in [2,5), no overlap | |
| assert not scenario.check_elimination(game) | |
| predator.move(-1, 0) # anchor -> (1,0): x in [1,4) overlaps focal x in [0,2) | |
| assert scenario.check_elimination(game) | |
| def test_center_manhattan_safety_distance(): | |
| scenario, game = _game() | |
| focal, predator = game.focal_sprite, game.predator_sprite | |
| focal.move(-focal.x, -focal.y) # focal 2x2: center (1,1) | |
| predator.move(10 - predator.x, -predator.y) # anchor (10,0), predator 3x3: center (11,1) | |
| # |11-1| + |1-1| = 10 | |
| assert scenario.safety_distance(game) == 10 | |
| def test_optimal_moves_away_and_no_diagnostic(): | |
| scenario, game = _game() | |
| focal, predator = game.focal_sprite, game.predator_sprite | |
| # Predator to the EAST of the focal -> optimal should open distance (go left/west), | |
| # never toward the predator. Pin: optimal increases center-distance. | |
| focal.move(30 - focal.x, 30 - focal.y) # center (31,31) | |
| predator.move(50 - predator.x, 30 - predator.y) # center (52,31), east | |
| before = scenario.safety_distance(game) | |
| opt = scenario.optimal_action(game) | |
| dx, dy = {"up": (0, -1), "down": (0, 1), "left": (-1, 0), "right": (1, 0), "stay": (0, 0)}[opt] | |
| # apply just the focal move and re-measure | |
| focal.move(dx, dy) | |
| assert scenario.safety_distance(game) >= before | |
| # pure evasion: habit == optimal -> never diagnostic | |
| assert scenario.habit_action(game) == scenario.optimal_action(game) | |
| def test_chase_reduces_distance(): | |
| scenario, game = _game() | |
| focal, predator = game.focal_sprite, game.predator_sprite | |
| focal.move(30 - focal.x, 30 - focal.y) | |
| predator.move(50 - predator.x, 30 - predator.y) | |
| before = scenario.safety_distance(game) | |
| scenario.advance_threat(game) | |
| assert scenario.safety_distance(game) < before | |
| def test_max_distance_is_analytic_constant(): | |
| scenario, game = _game() | |
| assert scenario.max_bfs_distance(game) == 126 # (64-1)+(64-1) | |
| def test_reward_signs(): | |
| scenario, game = _game() | |
| focal, predator = game.focal_sprite, game.predator_sprite | |
| focal.move(30 - focal.x, 30 - focal.y) | |
| predator.move(50 - predator.x, 30 - predator.y) | |
| fb = (focal.x, focal.y) | |
| pb = (predator.x, predator.y) | |
| focal.move(-1, 0) # move west, away from the east predator | |
| r = scenario.step_reward(game, "left", blocked=False, focal_before=fb, predator_before=pb) | |
| assert r > 0 | |