106 lines
3.6 KiB
Python
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
|