McRogueFace/tests/wiki_phase_d2_verify.py

408 lines
13 KiB
Python
Raw Normal View History

2026-02-09 08:15:18 -05:00
"""Verify code snippets from Phase D batch 2 wiki updates."""
import mcrfpy
import sys
passes = 0
fails = 0
def check(name, fn):
global passes, fails
try:
fn()
passes += 1
print(f" [OK] {name}")
except Exception as e:
fails += 1
print(f" [FAIL] {name}: {e}")
# ===================================================================
print("=== AI AND PATHFINDING ===")
# ===================================================================
def test_aip_fov_basic():
"""Basic FOV setup"""
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(400, 400))
for x in range(50):
for y in range(50):
grid.at(x, y).transparent = True
grid.at(x, y).walkable = True
# Set some walls
grid.at(10, 10).transparent = False
grid.at(10, 10).walkable = False
# Compute FOV
grid.compute_fov((25, 25), radius=10)
assert grid.is_in_fov((25, 25)) # Origin visible
assert not grid.is_in_fov((0, 0)) # Far away not visible
check("AIP: basic FOV", test_aip_fov_basic)
def test_aip_perspective():
"""Perspective system"""
grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320))
player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0)
grid.entities.append(player)
grid.perspective = player
assert grid.perspective is not None
check("AIP: perspective", test_aip_perspective)
def test_aip_fog_of_war():
"""Fog of war pattern"""
grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320), layers=[])
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
grid.add_layer(fog)
fog.fill(mcrfpy.Color(0, 0, 0, 255))
for x in range(20):
for y in range(20):
grid.at(x, y).transparent = True
grid.at(x, y).walkable = True
player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0)
grid.entities.append(player)
# Compute FOV and reveal fog
grid.compute_fov((10, 10), radius=8)
for x in range(20):
for y in range(20):
if grid.is_in_fov((x, y)):
fog.set((x, y), mcrfpy.Color(0, 0, 0, 0))
# Check fog revealed at origin
c = fog.at((10, 10))
assert c.a == 0 # Revealed
check("AIP: fog of war", test_aip_fog_of_war)
def test_aip_astar():
"""A* pathfinding via grid"""
grid = mcrfpy.Grid(grid_size=(30, 30), pos=(0, 0), size=(400, 400))
for x in range(30):
for y in range(30):
grid.at(x, y).walkable = True
path = grid.find_path((10, 10), (20, 20))
assert path is not None
assert len(path) > 0
step = path.walk()
assert step is not None
assert path.remaining >= 0
assert path.origin is not None
assert path.destination is not None
check("AIP: A* pathfinding", test_aip_astar)
def test_aip_dijkstra():
"""Dijkstra map"""
grid = mcrfpy.Grid(grid_size=(30, 30), pos=(0, 0), size=(400, 400))
for x in range(30):
for y in range(30):
grid.at(x, y).walkable = True
dm = grid.get_dijkstra_map((15, 15))
d = dm.distance((0, 0))
assert d > 0
p = dm.path_from((0, 0))
assert len(p) > 0
s = dm.step_from((0, 0))
assert s is not None
check("AIP: Dijkstra map", test_aip_dijkstra)
def test_aip_chase_pattern():
"""Enemy chase AI pattern"""
grid = mcrfpy.Grid(grid_size=(30, 30), pos=(0, 0), size=(400, 400))
for x in range(30):
for y in range(30):
grid.at(x, y).walkable = True
player = mcrfpy.Entity(grid_pos=(15, 15), sprite_index=0, name="player")
enemy = mcrfpy.Entity(grid_pos=(5, 5), sprite_index=1, name="enemy")
grid.entities.append(player)
grid.entities.append(enemy)
# Chase: use dijkstra from player, step enemy toward player
dm = grid.get_dijkstra_map((int(player.grid_x), int(player.grid_y)))
next_step = dm.step_from((int(enemy.grid_x), int(enemy.grid_y)))
assert next_step is not None
# Move enemy
enemy.grid_x = int(next_step.x)
enemy.grid_y = int(next_step.y)
check("AIP: chase pattern", test_aip_chase_pattern)
def test_aip_wasd_fov():
"""WASD + FOV update pattern"""
scene = mcrfpy.Scene("aip_wasd")
grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320))
for x in range(20):
for y in range(20):
grid.at(x, y).walkable = True
grid.at(x, y).transparent = True
scene.children.append(grid)
player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0)
grid.entities.append(player)
grid.perspective = player
def on_player_move(dx, dy):
new_x = int(player.grid_x + dx)
new_y = int(player.grid_y + dy)
point = grid.at(new_x, new_y)
if point and point.walkable:
player.grid_x = new_x
player.grid_y = new_y
grid.compute_fov((new_x, new_y), radius=10)
on_player_move(1, 0)
assert player.grid_x == 11
move_map = {
mcrfpy.Key.W: (0, -1),
mcrfpy.Key.A: (-1, 0),
mcrfpy.Key.S: (0, 1),
mcrfpy.Key.D: (1, 0),
}
def handle_key(key, action):
if action != mcrfpy.InputState.PRESSED:
return
if key in move_map:
dx, dy = move_map[key]
on_player_move(dx, dy)
scene.on_key = handle_key
check("AIP: WASD + FOV pattern", test_aip_wasd_fov)
def test_aip_spatial_query():
"""Spatial query for AI aggro"""
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(400, 400))
for x in range(50):
for y in range(50):
grid.at(x, y).walkable = True
player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0, name="p")
enemy = mcrfpy.Entity(grid_pos=(12, 10), sprite_index=1, name="e")
grid.entities.append(player)
grid.entities.append(enemy)
# Check aggro range
nearby = grid.entities_in_radius((int(enemy.grid_x), int(enemy.grid_y)), 5.0)
assert len(nearby) >= 2 # enemy and player both in range
check("AIP: spatial query for aggro", test_aip_spatial_query)
# ===================================================================
print("\n=== WRITING TESTS ===")
# ===================================================================
def test_wt_direct_execution():
"""Direct execution test template"""
scene = mcrfpy.Scene("wt_test")
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150))
frame.fill_color = mcrfpy.Color(255, 0, 0)
scene.children.append(frame)
assert frame.x == 100
assert frame.w == 200
check("WT: direct execution template", test_wt_direct_execution)
def test_wt_property_roundtrip():
"""Property round-trip test pattern"""
obj = mcrfpy.Frame(pos=(0, 0), size=(100, 100))
test_values = [0, 50, 100, 255, 127]
for value in test_values:
obj.x = value
assert obj.x == value, f"Failed for {value}"
check("WT: property round-trip", test_wt_property_roundtrip)
def test_wt_exception_testing():
"""Exception testing pattern"""
grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160))
try:
grid.at(-1, -1)
assert False, "Should have raised"
except Exception:
pass # Expected
check("WT: exception testing", test_wt_exception_testing)
def test_wt_grid_test_pattern():
"""Grid test pattern"""
grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160))
grid.at(5, 5).walkable = True
assert grid.at(5, 5).walkable == True
check("WT: grid test pattern", test_wt_grid_test_pattern)
def test_wt_click_callback_setup():
"""Click callback test setup"""
clicks_received = []
scene = mcrfpy.Scene("wt_click")
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150),
fill_color=mcrfpy.Color(0, 255, 0))
def on_click(pos, button, action):
clicks_received.append((pos, button, action))
frame.on_click = on_click
scene.children.append(frame)
check("WT: click callback setup", test_wt_click_callback_setup)
def test_wt_timer_pattern():
"""Timer creation pattern"""
t = mcrfpy.Timer("wt_test_timer", lambda timer, runtime: None, 100)
assert t is not None
t.stop()
check("WT: timer creation", test_wt_timer_pattern)
# ===================================================================
print("\n=== HEADLESS MODE ===")
# ===================================================================
def test_hm_step():
"""Step function"""
dt = mcrfpy.step(0.1)
# In headless mode, returns the dt
assert dt is not None
check("HM: step()", test_hm_step)
def test_hm_timer_with_step():
"""Timer fires with step"""
fired = [False]
def on_timer(timer, runtime):
fired[0] = True
t = mcrfpy.Timer("hm_test", on_timer, 500)
mcrfpy.step(0.6) # 600ms - timer should fire
assert fired[0], "Timer should have fired"
t.stop()
check("HM: timer with step", test_hm_timer_with_step)
def test_hm_animation_with_step():
"""Animation updates with step"""
frame = mcrfpy.Frame(pos=(0, 0), size=(100, 100))
frame.animate("x", 500.0, 2.0, mcrfpy.Easing.EASE_IN_OUT)
mcrfpy.step(1.0)
# Should be roughly halfway
assert frame.x > 0 # At least started moving
check("HM: animation with step", test_hm_animation_with_step)
def test_hm_scene_setup():
"""Scene setup in headless"""
scene = mcrfpy.Scene("hm_test")
frame = mcrfpy.Frame(pos=(50, 50), size=(100, 100))
scene.children.append(frame)
mcrfpy.current_scene = scene
assert frame.x == 50
check("HM: scene setup headless", test_hm_scene_setup)
# ===================================================================
print("\n=== UI COMPONENT HIERARCHY ===")
# ===================================================================
def test_uch_parent_child():
"""Parent-child coordinates"""
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150))
label = mcrfpy.Caption(text="Hello", pos=(10, 10))
frame.children.append(label)
assert label.x == 10 # Relative to parent
check("UCH: parent-child coords", test_uch_parent_child)
def test_uch_all_types():
"""All drawable types exist"""
f = mcrfpy.Frame(pos=(0, 0), size=(100, 100))
c = mcrfpy.Caption(text="test", pos=(0, 0))
s = mcrfpy.Sprite(x=0, y=0, sprite_index=0)
g = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160))
e = mcrfpy.Entity(grid_pos=(0, 0), sprite_index=0)
assert hasattr(mcrfpy, 'Arc')
assert hasattr(mcrfpy, 'Circle')
assert hasattr(mcrfpy, 'Line')
check("UCH: all drawable types", test_uch_all_types)
def test_uch_common_properties():
"""Common UIDrawable properties"""
f = mcrfpy.Frame(pos=(50, 60), size=(100, 100))
assert f.x == 50
assert f.y == 60
assert f.w == 100
assert f.h == 100
f.visible = False
assert f.visible == False
f.visible = True
f.opacity = 0.5
assert f.opacity == 0.5
f.z_index = 42
assert f.z_index == 42
check("UCH: common properties", test_uch_common_properties)
def test_uch_click_callbacks():
"""Callback signatures"""
f = mcrfpy.Frame(pos=(0, 0), size=(100, 100))
# on_click: (pos: Vector, button: MouseButton, action: InputState)
f.on_click = lambda pos, button, action: None
# on_enter/on_exit: (pos: Vector)
f.on_enter = lambda pos: None
f.on_exit = lambda pos: None
# on_move: (pos: Vector)
f.on_move = lambda pos: None
check("UCH: callback signatures", test_uch_click_callbacks)
def test_uch_collection():
"""UICollection operations"""
scene = mcrfpy.Scene("uch_coll")
f1 = mcrfpy.Frame(pos=(0, 0), size=(10, 10))
f2 = mcrfpy.Caption(text="test", pos=(0, 0))
f3 = mcrfpy.Sprite(x=0, y=0, sprite_index=0)
scene.children.append(f1)
scene.children.append(f2)
scene.children.append(f3)
assert len(scene.children) >= 3
# Can iterate
for item in scene.children:
pass
check("UCH: UICollection", test_uch_collection)
def test_uch_entity_collection():
"""UIEntityCollection operations"""
grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320))
e1 = mcrfpy.Entity(grid_pos=(1, 1), sprite_index=0)
e2 = mcrfpy.Entity(grid_pos=(2, 2), sprite_index=0)
grid.entities.append(e1)
grid.entities.append(e2)
assert len(grid.entities) == 2
grid.entities.remove(e1)
assert len(grid.entities) == 1
check("UCH: EntityCollection", test_uch_entity_collection)
def test_uch_type_preservation():
"""Type preserved through collections"""
scene = mcrfpy.Scene("uch_types")
sprite = mcrfpy.Sprite(x=10, y=10, sprite_index=0)
scene.children.append(sprite)
retrieved = scene.children[0]
assert type(retrieved).__name__ == "Sprite"
check("UCH: type preservation", test_uch_type_preservation)
# ===================================================================
print("\n" + "=" * 60)
print(f"PHASE D2 VERIFICATION: {passes} passed, {fails} failed")
print("=" * 60)
if fails:
sys.exit(1)
else:
sys.exit(0)