"""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)