"""Verify all code snippets from updated wiki pages run correctly.""" 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("=== INPUT AND EVENTS WIKI ===") # =================================================================== def test_keyboard_handler(): scene = mcrfpy.Scene("ie_test1") def handle_key(key, action): if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED: pass elif key == mcrfpy.Key.W and action == mcrfpy.InputState.PRESSED: pass scene.on_key = handle_key mcrfpy.current_scene = scene check("IE: keyboard handler with enums", test_keyboard_handler) def test_key_enum_members(): # Key.A has int value 0, so use hasattr or 'is not None' instead of truthiness assert hasattr(mcrfpy.Key, 'A') assert hasattr(mcrfpy.Key, 'Z') assert hasattr(mcrfpy.Key, 'NUM_0') assert hasattr(mcrfpy.Key, 'UP') assert hasattr(mcrfpy.Key, 'SPACE') assert hasattr(mcrfpy.Key, 'ENTER') assert hasattr(mcrfpy.Key, 'ESCAPE') assert hasattr(mcrfpy.Key, 'F1') assert hasattr(mcrfpy.Key, 'LEFT_SHIFT') check("IE: Key enum members", test_key_enum_members) def test_inputstate_legacy(): assert mcrfpy.InputState.PRESSED == "start" assert mcrfpy.InputState.RELEASED == "end" check("IE: InputState legacy string compat", test_inputstate_legacy) def test_click_handler(): frame = mcrfpy.Frame(pos=(100, 100), size=(200, 50)) def on_frame_click(pos, button, action): if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED: pass frame.on_click = on_frame_click frame.on_enter = lambda pos: None frame.on_exit = lambda pos: None frame.on_move = lambda pos: None check("IE: click/hover handlers", test_click_handler) def test_mousebutton_members(): assert mcrfpy.MouseButton.LEFT is not None assert mcrfpy.MouseButton.RIGHT is not None assert mcrfpy.MouseButton.MIDDLE is not None check("IE: MouseButton enum members", test_mousebutton_members) def test_grid_cell_events(): grid = mcrfpy.Grid(grid_size=(20, 15), pos=(50, 50), size=(400, 300)) def on_cell_click(cell_pos, button, action): if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED: x, y = int(cell_pos.x), int(cell_pos.y) point = grid.at(x, y) grid.on_cell_click = on_cell_click grid.on_cell_enter = lambda cell_pos: None grid.on_cell_exit = lambda cell_pos: None check("IE: grid cell events", test_grid_cell_events) def test_wasd_pattern(): scene = mcrfpy.Scene("wasd_test") def handle_key(key, action): if action != mcrfpy.InputState.PRESSED: return moves = { mcrfpy.Key.W: (0, -1), mcrfpy.Key.A: (-1, 0), mcrfpy.Key.S: (0, 1), mcrfpy.Key.D: (1, 0), } if key in moves: dx, dy = moves[key] scene.on_key = handle_key check("IE: WASD pattern", test_wasd_pattern) def test_button_widget(): def make_button(text, x, y, callback): btn = mcrfpy.Frame(pos=(x, y), size=(120, 40), fill_color=mcrfpy.Color(60, 60, 80)) label = mcrfpy.Caption(text=text, x=10, y=8) btn.children.append(label) def on_click(pos, button, action): if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED: callback() btn.on_click = on_click btn.on_enter = lambda pos: setattr(btn, 'fill_color', mcrfpy.Color(80, 80, 110)) btn.on_exit = lambda pos: setattr(btn, 'fill_color', mcrfpy.Color(60, 60, 80)) return btn btn = make_button("Test", 10, 10, lambda: None) assert btn.w == 120 check("IE: button widget pattern", test_button_widget) def test_scene_switching(): game_scene = mcrfpy.Scene("sw_game") menu_scene = mcrfpy.Scene("sw_menu") def game_keys(key, action): if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED: menu_scene.activate() def menu_keys(key, action): if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED: game_scene.activate() game_scene.on_key = game_keys menu_scene.on_key = menu_keys check("IE: scene switching pattern", test_scene_switching) # =================================================================== print("\n=== ANIMATION SYSTEM WIKI ===") # =================================================================== def test_animate_basic(): frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150)) frame.animate("x", 500.0, 2.0, mcrfpy.Easing.EASE_IN_OUT) frame.animate("opacity", 0.5, 1.0, mcrfpy.Easing.EASE_OUT_QUAD) check("AS: basic .animate()", test_animate_basic) def test_animate_callback(): frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150)) def on_complete(target, prop, value): pass frame.animate("x", 500.0, 2.0, mcrfpy.Easing.EASE_IN_OUT, callback=on_complete) check("AS: .animate() with callback", test_animate_callback) def test_animate_chaining(): frame = mcrfpy.Frame(pos=(0, 0), size=(100, 100)) def step2(target, prop, value): target.animate("y", 300.0, 1.0, mcrfpy.Easing.EASE_OUT_BOUNCE) def step1(target, prop, value): target.animate("x", 500.0, 1.0, mcrfpy.Easing.EASE_IN_OUT, callback=step2) frame.animate("opacity", 1.0, 0.5, mcrfpy.Easing.EASE_IN, callback=step1) check("AS: animation chaining", test_animate_chaining) def test_animate_frame_props(): f = mcrfpy.Frame(x=0, y=0, w=100, h=100) for prop in ['x', 'y', 'w', 'h', 'outline', 'opacity', 'fill_color', 'outline_color']: f.animate(prop, 1.0, 0.01, mcrfpy.Easing.LINEAR) check("AS: all Frame animatable properties", test_animate_frame_props) def test_animate_caption_props(): c = mcrfpy.Caption(text="test", x=0, y=0) for prop in ['x', 'y', 'opacity', 'outline', 'fill_color', 'outline_color']: c.animate(prop, 1.0, 0.01, mcrfpy.Easing.LINEAR) check("AS: all Caption animatable properties", test_animate_caption_props) def test_animate_sprite_props(): s = mcrfpy.Sprite(x=0, y=0, sprite_index=0) for prop in ['x', 'y', 'scale', 'sprite_index', 'opacity']: s.animate(prop, 1.0, 0.01, mcrfpy.Easing.LINEAR) check("AS: all Sprite animatable properties", test_animate_sprite_props) def test_animate_grid_props(): g = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160)) for prop in ['x', 'y', 'w', 'h', 'center_x', 'center_y', 'zoom']: g.animate(prop, 1.0, 0.01, mcrfpy.Easing.LINEAR) check("AS: all Grid animatable properties", test_animate_grid_props) def test_animate_entity_props(): g = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) e = mcrfpy.Entity(grid_pos=(5, 5), sprite_index=0) g.entities.append(e) for prop in ['x', 'y', 'draw_x', 'draw_y', 'sprite_index', 'sprite_scale']: e.animate(prop, 5.0, 0.01, mcrfpy.Easing.LINEAR) check("AS: all Entity animatable properties", test_animate_entity_props) def test_color_animation(): f = mcrfpy.Frame(x=0, y=0, w=100, h=100) f.animate("fill_color", (255, 0, 0, 255), 1.0, mcrfpy.Easing.EASE_IN) f.animate("outline_color", (255, 255, 255, 0), 0.5, mcrfpy.Easing.LINEAR) check("AS: color animations", test_color_animation) def test_animation_object(): anim = mcrfpy.Animation("x", 500.0, 2.0, mcrfpy.Easing.LINEAR) f = mcrfpy.Frame(x=0, y=0, w=100, h=100) anim.start(f) check("AS: Animation object (advanced)", test_animation_object) def test_easing_members(): easings = ['LINEAR', 'EASE_IN', 'EASE_OUT', 'EASE_IN_OUT', 'EASE_IN_QUAD', 'EASE_OUT_QUAD', 'EASE_IN_OUT_QUAD', 'EASE_IN_CUBIC', 'EASE_OUT_CUBIC', 'EASE_IN_OUT_CUBIC', 'EASE_OUT_BOUNCE', 'EASE_IN_ELASTIC'] for name in easings: assert hasattr(mcrfpy.Easing, name), f"Missing Easing.{name}" check("AS: Easing enum members", test_easing_members) # =================================================================== print("\n=== GRID SYSTEM WIKI ===") # =================================================================== def test_grid_basic_creation(): grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400)) assert grid.grid_w == 50 assert grid.grid_h == 50 check("GS: basic grid creation", test_grid_basic_creation) def test_grid_with_layers(): terrain = mcrfpy.TileLayer(name="terrain", z_index=-1) fog = mcrfpy.ColorLayer(name="fog", z_index=1) grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers=[terrain, fog]) assert len(grid.layers) == 2 check("GS: grid with layers=[]", test_grid_with_layers) def test_grid_empty_layers(): grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers=[]) assert len(grid.layers) == 0 check("GS: grid with layers=[] empty", test_grid_empty_layers) def test_grid_add_to_scene(): scene = mcrfpy.Scene("gs_test") grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160)) scene.children.append(grid) assert len(scene.children) > 0 check("GS: add grid to scene", test_grid_add_to_scene) def test_tilelayer_ops(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320), layers=[]) terrain = mcrfpy.TileLayer(name="terrain", z_index=-1) grid.add_layer(terrain) terrain.set((5, 3), 42) assert terrain.at((5, 3)) == 42 terrain.fill(0) assert terrain.at((5, 3)) == 0 terrain.set((5, 3), -1) assert terrain.at((5, 3)) == -1 check("GS: TileLayer set/at/fill", test_tilelayer_ops) def test_colorlayer_ops(): 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.set((5, 3), mcrfpy.Color(0, 0, 0, 200)) c = fog.at((5, 3)) assert c.a == 200 fog.fill(mcrfpy.Color(0, 0, 0, 255)) c2 = fog.at((0, 0)) assert c2.a == 255 check("GS: ColorLayer set/at/fill", test_colorlayer_ops) def test_layer_management(): grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160), layers=[]) tl = mcrfpy.TileLayer(name="terrain", z_index=-1) grid.add_layer(tl) assert len(grid.layers) == 1 found = grid.layer("terrain") assert found is not None grid.remove_layer(tl) assert len(grid.layers) == 0 check("GS: layer management (add/layer/remove)", test_layer_management) def test_gridpoint(): grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(160, 160)) point = grid.at(5, 5) point.walkable = True point.transparent = True assert point.walkable == True assert point.transparent == True check("GS: GridPoint access", test_gridpoint) def test_fov(): 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).transparent = True grid.at(5, 5).transparent = False grid.compute_fov((10, 10), radius=8) assert grid.is_in_fov((10, 10)) == True assert grid.is_in_fov((0, 0)) == False check("GS: FOV compute and query", test_fov) def test_pathfinding(): 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(5, 5).walkable = False path = grid.find_path((0, 0), (10, 10)) assert path is not None assert len(path) > 0 assert path.origin is not None assert path.destination is not None step = path.walk() assert step is not None # len() returns remaining steps, same as .remaining # After walk(), both decrease by 1 initial_len = len(path) + 1 # path had one more step before walk() assert path.remaining == initial_len - 1 check("GS: A* pathfinding", test_pathfinding) def test_dijkstra(): 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 dm = grid.get_dijkstra_map((5, 5)) d = dm.distance((10, 10)) assert d > 0 p = dm.path_from((10, 10)) assert len(p) > 0 s = dm.step_from((10, 10)) assert s is not None check("GS: Dijkstra map", test_dijkstra) def test_entity_on_grid(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0, name="player") grid.entities.append(player) assert player.grid is not None nearby = grid.entities_in_radius((10, 10), 5.0) assert len(nearby) > 0 player.die() check("GS: entity management on grid", test_entity_on_grid) def test_camera_control(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) grid.center = (80, 80) grid.center_x = 100 grid.center_y = 100 grid.center_camera((10, 10)) grid.zoom = 1.5 grid.animate("center_x", 50.0, 0.5, mcrfpy.Easing.EASE_IN_OUT) grid.animate("zoom", 2.0, 0.3, mcrfpy.Easing.EASE_OUT_QUAD) check("GS: camera control", test_camera_control) def test_grid_mouse_events(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) def on_grid_click(pos, button, action): if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED: pass grid.on_click = on_grid_click grid.on_enter = lambda pos: None grid.on_exit = lambda pos: None def on_cell_click(cell_pos, button, action): if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED: x, y = int(cell_pos.x), int(cell_pos.y) grid.on_cell_click = on_cell_click grid.on_cell_enter = lambda cell_pos: None grid.on_cell_exit = lambda cell_pos: None check("GS: mouse events", test_grid_mouse_events) def test_perspective(): 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 grid.perspective = None check("GS: perspective system", test_perspective) # =================================================================== print("\n=== ENTITY MANAGEMENT WIKI ===") # =================================================================== def test_entity_creation(): entity = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0) player = mcrfpy.Entity(grid_pos=(5, 5), sprite_index=0, name="player2") e = mcrfpy.Entity() assert e.grid_x == 0 check("EM: entity creation forms", test_entity_creation) def test_entity_grid_relationship(): grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(400, 400)) player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0, name="em_player") assert player.grid is None grid.entities.append(player) assert player.grid is not None assert player in grid.entities grid.entities.remove(player) assert player.grid is None check("EM: entity-grid relationship", test_entity_grid_relationship) def test_entity_movement(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) player = mcrfpy.Entity(grid_pos=(5, 5), sprite_index=0) grid.entities.append(player) player.grid_x = 15 player.grid_y = 20 player.animate("x", 15.0, 0.3, mcrfpy.Easing.EASE_OUT_QUAD) player.animate("y", 20.0, 0.3, mcrfpy.Easing.EASE_OUT_QUAD) check("EM: entity movement (direct + animated)", test_entity_movement) def test_entity_animate_with_callback(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) player = mcrfpy.Entity(grid_pos=(5, 5), sprite_index=0) grid.entities.append(player) def on_move_complete(target, prop, value): pass player.animate("x", 15.0, 0.3, mcrfpy.Easing.EASE_OUT_QUAD, callback=on_move_complete) check("EM: entity animate with callback", test_entity_animate_with_callback) def test_spatial_queries(): grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(400, 400)) for i in range(5): e = mcrfpy.Entity(grid_pos=(10 + i, 10), sprite_index=0, name=f"e{i}") grid.entities.append(e) nearby = grid.entities_in_radius((10, 10), 5.0) assert len(nearby) > 0 check("EM: spatial queries", test_spatial_queries) def test_entity_collection(): 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) e3 = mcrfpy.Entity(grid_pos=(3, 3), sprite_index=0) grid.entities.append(e1) grid.entities.extend([e2, e3]) assert len(grid.entities) == 3 grid.entities.remove(e2) assert len(grid.entities) == 2 check("EM: EntityCollection operations", test_entity_collection) def test_entity_die(): grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) e = mcrfpy.Entity(grid_pos=(5, 5), sprite_index=0) grid.entities.append(e) e.die() assert e.grid is None check("EM: Entity.die()", test_entity_die) def test_fog_of_war(): 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.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("EM: fog of war pattern", test_fog_of_war) def test_player_class_pattern(): class Player: def __init__(self, grid, start_pos): self.entity = mcrfpy.Entity( grid_pos=start_pos, sprite_index=0, name="pclass" ) grid.entities.append(self.entity) def move(self, dx, dy): new_x = int(self.entity.grid_x + dx) new_y = int(self.entity.grid_y + dy) point = self.entity.grid.at(new_x, new_y) if point and point.walkable: self.entity.animate("x", float(new_x), 0.15, mcrfpy.Easing.EASE_OUT_QUAD) self.entity.animate("y", float(new_y), 0.15, mcrfpy.Easing.EASE_OUT_QUAD) self.entity.grid_x = new_x self.entity.grid_y = new_y return True return False 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 p = Player(grid, (10, 10)) assert p.move(1, 0) == True check("EM: Player class pattern", test_player_class_pattern) def test_enemy_ai_pattern(): class Enemy: def __init__(self, grid, pos, aggro_range=10): self.entity = mcrfpy.Entity( grid_pos=pos, sprite_index=1, name="enemy_ai" ) self.aggro_range = aggro_range grid.entities.append(self.entity) def update(self): grid = self.entity.grid nearby = grid.entities_in_radius( (self.entity.grid_x, self.entity.grid_y), self.aggro_range ) return nearby 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=(12, 10), sprite_index=0, name="player_for_ai") grid.entities.append(player) enemy = Enemy(grid, (10, 10)) nearby = enemy.update() assert len(nearby) > 0 check("EM: Enemy AI pattern", test_enemy_ai_pattern) def test_item_pickup_pattern(): class Item: def __init__(self, grid, pos, item_type): self.entity = mcrfpy.Entity( grid_pos=pos, sprite_index=10 + item_type, name=f"item_{item_type}" ) self.item_type = item_type grid.entities.append(self.entity) def pickup(self, collector_inventory): collector_inventory.append(self.item_type) self.entity.die() grid = mcrfpy.Grid(grid_size=(20, 20), pos=(0, 0), size=(320, 320)) item = Item(grid, (5, 5), 3) assert len(grid.entities) == 1 inv = [] item.pickup(inv) assert inv == [3] assert len(grid.entities) == 0 check("EM: Item pickup pattern", test_item_pickup_pattern) def test_pathfinding_pattern(): 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 entity = mcrfpy.Entity(grid_pos=(0, 0), sprite_index=0) grid.entities.append(entity) path = grid.find_path( (int(entity.grid_x), int(entity.grid_y)), (10, 10) ) if path and len(path) > 0: next_step = path.walk() entity.grid_x = next_step.x entity.grid_y = next_step.y dm = grid.get_dijkstra_map((15, 15)) d = dm.distance((entity.grid_x, entity.grid_y)) assert d > 0 ns = dm.step_from((int(entity.grid_x), int(entity.grid_y))) assert ns is not None check("EM: pathfinding pattern", test_pathfinding_pattern) # =================================================================== print("\n" + "=" * 60) print(f"WIKI SNIPPET VERIFICATION: {passes} passed, {fails} failed") print("=" * 60) if fails: sys.exit(1) else: sys.exit(0)