Files
2026-09-24 15:45:31 +02:00

106 lines
3.6 KiB
Python

"""Tests des Schlafmodus (Inseln, Einschlafen, Aufwecken)."""
from dataclasses import replace
from sandbox.config import PHYSICS
from sandbox.physics import Body, Contact, Vector2, collide
from sandbox.physics.sleep import UnionFind, find_islands, update_sleep, wake_touched_bodies
from tests.helpers import material
def _circle(x: float, y: float = 0.0, radius: float = 10.0) -> Body:
return Body.circle(Vector2(x, y), radius, material())
def _contacts(*pairs: tuple[Body, Body]) -> list[Contact]:
contacts = [collide(a, b) for a, b in pairs]
assert all(contact is not None for contact in contacts)
return [contact for contact in contacts if contact is not None]
def test_union_find_merges_sets() -> None:
union_find = UnionFind([1, 2, 3, 4, 5])
union_find.union(1, 2)
union_find.union(3, 4)
union_find.union(2, 4)
assert union_find.find(1) == union_find.find(3) == union_find.find(4)
assert union_find.find(5) != union_find.find(1)
def test_touching_bodies_form_islands_but_walls_do_not_connect_them() -> None:
a, b, c, d = _circle(0), _circle(15), _circle(30), _circle(200)
floor = Body.wall(Vector2(0, -1), -5, material()) # berührt alle Kreise
contacts = _contacts((a, b), (b, c), (a, floor), (d, floor))
islands = find_islands([a, b, c, d], contacts)
assert [set(island) for island in islands] == [{a, b, c}, {d}]
def test_an_island_sleeps_only_when_all_its_bodies_rest() -> None:
config = replace(PHYSICS, time_to_sleep=0.5)
resting, moving = _circle(0), _circle(15)
moving.velocity = Vector2(50, 0)
contacts = _contacts((resting, moving))
for _ in range(100):
update_sleep([resting, moving], contacts, 0.01, config)
assert resting.sleep_time >= 0.5
assert not resting.sleeping # der bewegte Nachbar hält die Insel wach
moving.velocity = Vector2(0, 0)
for _ in range(60):
update_sleep([resting, moving], contacts, 0.01, config)
assert resting.sleeping
assert moving.sleeping
def test_waking_one_body_wakes_its_whole_island() -> None:
a, b, c, outsider = _circle(0), _circle(15), _circle(30), _circle(300)
island = [a, b, c]
for body in island:
body.put_to_sleep(island)
outsider.put_to_sleep()
c.wake()
assert not any(body.sleeping for body in island)
assert all(body.sleep_island is None for body in island)
assert outsider.sleeping
def test_touching_a_sleeper_wakes_and_reports_its_whole_island() -> None:
a, b, c = _circle(0), _circle(15), _circle(30)
island = [b, c]
for body in island:
body.put_to_sleep(island)
woken = wake_touched_bodies(_contacts((a, b)))
assert set(woken) == {b, c}
assert not c.sleeping
def test_held_bodies_keep_their_island_awake() -> None:
held, other = _circle(0), _circle(15)
held.held = True
contacts = _contacts((held, other))
for _ in range(200):
update_sleep([held, other], contacts, 0.01, PHYSICS)
assert not held.sleeping
assert not other.sleeping
assert held.sleep_time == 0
def test_sleepers_touched_by_awake_bodies_are_woken_but_not_by_walls() -> None:
sleeper, awake = _circle(0), _circle(15)
sleeper.put_to_sleep()
floor = Body.wall(Vector2(0, -1), -5, material())
lonely = _circle(300)
lonely.put_to_sleep()
both_asleep = _circle(500)
both_asleep.put_to_sleep()
neighbour = _circle(515)
neighbour.put_to_sleep()
woken = wake_touched_bodies(
_contacts((sleeper, awake), (lonely, floor), (both_asleep, neighbour))
)
assert woken == [sleeper]
assert not sleeper.sleeping
assert lonely.sleeping
assert both_asleep.sleeping