world-simulator / tests /test_world_spawning.py
DeltaZN
feat: improved perception
54db442
Raw
History Blame Contribute Delete
8.42 kB
from __future__ import annotations
from world_simulator.config import GameConfig, NpcConfig, ServerConfig, SimulationConfig, WorldConfig
from world_simulator.domain import Vec3
from world_simulator.simulation.connectors.base import NpcDirective, TickPlan
from world_simulator.simulation.mechanics import distance_between
from world_simulator.simulation.spawning import create_world
from world_simulator.simulation.tick import advance_world, apply_tick_plan
def test_world_spawns_configured_npc_count() -> None:
world = create_world(_config(npc_count=12))
assert world.terrain.kind == "plain_green"
assert world.terrain.width == 80
assert len(world.npcs) == 12
assert world.npcs[0].id == "npc-001"
assert world.npcs[0].health == 100
assert 5 <= world.npcs[0].attack_damage <= 15
def test_world_spawning_is_deterministic() -> None:
first = create_world(_config(npc_count=5))
second = create_world(_config(npc_count=5))
assert first.to_dict() == second.to_dict()
def test_tick_advances_world_without_leaving_terrain() -> None:
world = create_world(_config(npc_count=5))
advance_world(world)
assert world.tick == 1
assert all(-40 <= npc.position.x <= 40 for npc in world.npcs)
assert all(-40 <= npc.position.z <= 40 for npc in world.npcs)
assert {npc.intention for npc in world.npcs} == {"walking"}
assert all(len(npc.memory) == 1 for npc in world.npcs)
def test_attack_damages_target_inside_one_block_radius() -> None:
world = create_world(_config(npc_count=2))
attacker, target = world.npcs
attacker.position = Vec3(x=0.0, y=0.0, z=0.0)
target.position = Vec3(x=1.0, y=0.0, z=0.0)
attacker.attack_damage = 10
attacker.god_directive = "Kill Boris."
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=attacker.id,
action="strike",
target_npc_id=target.id,
)
],
),
)
assert 85 <= target.health <= 90
assert attacker.intention == "attacking Boris"
assert "damage" in target.memory[-1].text
def test_attack_outside_one_block_radius_has_no_effect() -> None:
world = create_world(_config(npc_count=2))
attacker, target = world.npcs
attacker.position = Vec3(x=0.0, y=0.0, z=0.0)
target.position = Vec3(x=12.0, y=0.0, z=0.0)
attacker.god_directive = "Kill Boris."
starting_distance = distance_between(attacker, target)
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=attacker.id,
action="strike",
target_npc_id=target.id,
)
],
),
)
assert target.health == 100
assert distance_between(attacker, target) < starting_distance
assert attacker.intention == "approaching Boris to attack"
def test_talk_adds_memory_inside_three_block_radius() -> None:
world = create_world(_config(npc_count=2))
speaker, listener = world.npcs
speaker.position = Vec3(x=0.0, y=0.0, z=0.0)
listener.position = Vec3(x=0.0, y=0.0, z=3.0)
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=speaker.id,
action="speak",
target_npc_id=listener.id,
message="Hold this position.",
)
],
),
)
assert speaker.intention == "talking to Boris"
assert "Hold this position." in speaker.memory[-1].text
assert "Hold this position." in listener.memory[-1].text
def test_memory_keeps_recent_entries_and_summarizes_older_events() -> None:
world = create_world(_config(npc_count=2))
speaker, listener = world.npcs
speaker.position = Vec3(x=0.0, y=0.0, z=0.0)
listener.position = Vec3(x=0.0, y=0.0, z=3.0)
for tick in range(1, 8):
apply_tick_plan(
world,
tick,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=speaker.id,
action="speak",
target_npc_id=listener.id,
message=f"Message {tick}.",
)
],
),
)
assert len(speaker.memory) == 5
assert speaker.memory_summary is not None
assert "Ada arrived as a citizen." in speaker.memory_summary
assert "Message 1." in speaker.memory_summary
assert "Message 7." in speaker.memory[-1].text
def test_dead_npc_stays_in_place_and_cannot_act() -> None:
world = create_world(_config(npc_count=2))
dead, target = world.npcs
dead.position = Vec3(x=0.0, y=0.0, z=0.0)
dead.health = -1
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=dead.id,
action="strike",
target_npc_id=target.id,
)
],
),
)
assert dead.position == Vec3(x=0.0, y=0.0, z=0.0)
assert dead.intention == "dead"
assert target.health == 100
def test_two_npcs_can_walk_to_same_destination_in_one_tick() -> None:
world = create_world(_config(npc_count=2))
first, second = world.npcs
first.position = Vec3(x=-2.0, y=0.0, z=2.0)
second.position = Vec3(x=6.0, y=0.0, z=2.0)
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=first.id,
action="move",
target=Vec3(x=2.0, y=0.0, z=2.0),
),
NpcDirective(
npc_id=second.id,
action="move",
target=Vec3(x=2.0, y=0.0, z=2.0),
),
],
),
)
assert first.position == Vec3(x=2.0, y=0.0, z=2.0)
assert second.position == Vec3(x=2.0, y=0.0, z=2.0)
assert first.intention == "walking"
assert second.intention == "walking"
def test_npc_can_walk_into_occupied_position() -> None:
world = create_world(_config(npc_count=2))
first, second = world.npcs
first.position = Vec3(x=-2.0, y=0.0, z=2.0)
second.position = Vec3(x=2.0, y=0.0, z=2.0)
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=first.id,
action="move",
target=Vec3(x=2.0, y=0.0, z=2.0),
)
],
),
)
assert first.position == Vec3(x=2.0, y=0.0, z=2.0)
assert second.position == Vec3(x=2.0, y=0.0, z=2.0)
assert first.intention == "walking"
def test_attack_that_drops_health_below_zero_marks_target_dead() -> None:
world = create_world(_config(npc_count=2))
attacker, target = world.npcs
attacker.position = Vec3(x=0.0, y=0.0, z=0.0)
target.position = Vec3(x=1.0, y=0.0, z=0.0)
attacker.attack_damage = 10
target.health = 5
attacker.god_directive = "Kill Boris."
apply_tick_plan(
world,
1,
TickPlan(
source="test",
directives=[
NpcDirective(
npc_id=attacker.id,
action="strike",
target_npc_id=target.id,
),
NpcDirective(
npc_id=target.id,
action="move",
target=Vec3(x=2.0, y=0.0, z=0.0),
),
],
),
)
assert target.health < 0
assert target.intention == "dead"
assert target.position == Vec3(x=1.0, y=0.0, z=0.0)
assert target.memory[-1].text == "You died."
def _config(*, npc_count: int) -> GameConfig:
return GameConfig(
world=WorldConfig(width=80, depth=80, terrain="plain_green", seed=42),
npcs=NpcConfig(count=npc_count),
simulation=SimulationConfig(tick_ms=500),
server=ServerConfig(host="127.0.0.1", port=8000),
)