#!/usr/bin/env python3 """ Test Perspective Binding System =============================== Tests the integration between: 1. ColorLayer.apply_perspective() - binding a layer to an entity 2. entity.updateVisibility() - automatically updating bound layers 3. ColorLayer.clear_perspective() - removing the binding This implements issue #113 requirements for "Agent POV Integration". """ import mcrfpy import sys def run_tests(): """Run perspective binding tests""" print("=== Perspective Binding Tests ===\n") # Test 1: Create grid with entity and color layer print("Test 1: Setup") grid = mcrfpy.Grid(pos=(0, 0), size=(640, 400), grid_size=(40, 25)) # Set up walls for y in range(25): for x in range(40): point = grid.at(x, y) # Border walls if x == 0 or x == 39 or y == 0 or y == 24: point.walkable = False point.transparent = False # Central wall elif x == 20 and y != 12: # Wall with door at y=12 point.walkable = False point.transparent = False else: point.walkable = True point.transparent = True # Create player entity player = mcrfpy.Entity((5, 12)) grid.entities.append(player) print(f" Player at ({player.x}, {player.y})") print(" Grid setup complete") print() # Test 2: Apply perspective binding print("Test 2: Perspective Binding") fov_layer = grid.add_layer('color', z_index=-1) fov_layer.fill((0, 0, 0, 255)) # Start with black (unknown) fov_layer.apply_perspective( entity=player, visible=(255, 255, 200, 64), discovered=(100, 100, 100, 128), unknown=(0, 0, 0, 255) ) print(" Applied perspective to layer") # Check layer is bound # (We can't directly check internal state, but we can verify behavior) print() # Test 3: updateVisibility should update the bound layer print("Test 3: Entity updateVisibility") player.update_visibility() # Check that the player's position is now visible visible_cell = fov_layer.at(int(player.x), int(player.y)) assert visible_cell.r == 255, f"Player position should be visible (got r={visible_cell.r})" print(" Player position has visible color after updateVisibility()") # Check that cells behind wall are unknown behind_wall = fov_layer.at(21, 5) assert behind_wall.r == 0, f"Behind wall should be unknown (got r={behind_wall.r})" print(" Cell behind wall is unknown") print() # Test 4: Moving entity and calling updateVisibility print("Test 4: Entity Movement with Perspective") # Move player through the door player.x = 21 player.y = 12 player.update_visibility() # Now the player should see both sides of the wall # Check a cell that was previously hidden now_visible = fov_layer.at(25, 12) # To the right of where player moved # This should now be visible (or discovered if was visible) assert now_visible.r in [255, 100], f"Cell should be visible or discovered (got r={now_visible.r})" print(f" After moving to door, cell (25,12) has r={now_visible.r}") # Player's new position should be visible new_pos_color = fov_layer.at(int(player.x), int(player.y)) assert new_pos_color.r == 255, f"New player position should be visible (got r={new_pos_color.r})" print(f" Player's new position ({player.x}, {player.y}) is visible") print() # Test 5: Check discovered cells remain discovered print("Test 5: Discovered State Persistence") # Move player away from original position player.x = 35 player.y = 12 player.update_visibility() # Original position (5, 12) should now be discovered (not visible, but was seen) original_pos = fov_layer.at(5, 12) # It could be visible if in line of sight, or discovered if not print(f" Original position (5,12) color: r={original_pos.r}") print() # Test 6: Clear perspective print("Test 6: Clear Perspective") fov_layer.clear_perspective() # After clearing, updateVisibility should not affect this layer fov_layer.fill((128, 0, 128, 255)) # Fill with purple player.update_visibility() # Layer should still be purple (not modified by updateVisibility) check_cell = fov_layer.at(int(player.x), int(player.y)) assert check_cell.r == 128, f"Layer should be unchanged after clear_perspective (got r={check_cell.r})" assert check_cell.g == 0, f"Layer should be unchanged (got g={check_cell.g})" assert check_cell.b == 128, f"Layer should be unchanged (got b={check_cell.b})" print(" Layer unchanged after clear_perspective()") print() # Test 7: Grid FOV settings print("Test 7: Grid FOV Settings Integration") # Create a new grid and layer to test FOV radius without discovered interference grid2 = mcrfpy.Grid(pos=(0, 0), size=(640, 400), grid_size=(40, 25)) # Set all cells walkable/transparent for y in range(25): for x in range(40): point = grid2.at(x, y) point.walkable = True point.transparent = True # Create player entity player2 = mcrfpy.Entity((20, 12)) grid2.entities.append(player2) # Set grid FOV settings grid2.fov = mcrfpy.FOV.SHADOW grid2.fov_radius = 5 # Smaller radius # Create layer and bind perspective fov_layer2 = grid2.add_layer('color', z_index=-1) fov_layer2.fill((0, 0, 0, 255)) # Start with black (unknown) fov_layer2.apply_perspective( entity=player2, visible=(255, 0, 0, 64), # Red for visible discovered=(100, 100, 100, 128), unknown=(0, 0, 0, 255) ) # Update visibility - this should only illuminate cells within radius 5 player2.update_visibility() # With radius 5, cells far from player should be unknown (never discovered) far_cell = fov_layer2.at(30, 12) # 10 cells away from player assert far_cell.r == 0, f"Far cell should be unknown with radius 5 (got r={far_cell.r})" print(f" Far cell (30,12) is unknown with radius=5") # Near cell should be visible near_cell = fov_layer2.at(22, 12) # 2 cells away assert near_cell.r == 255, f"Near cell should be visible (got r={near_cell.r})" print(f" Near cell (22,12) is visible") print() # Test 8: visible_entities method print("Test 8: Entity.visible_entities()") # Create a grid with multiple entities grid3 = mcrfpy.Grid(pos=(0, 0), size=(640, 400), grid_size=(40, 25)) # Set all cells transparent for y in range(25): for x in range(40): point = grid3.at(x, y) point.walkable = True point.transparent = True # Add a wall to block visibility for y in range(25): if y != 12: # Door at y=12 point = grid3.at(20, y) point.walkable = False point.transparent = False # Create entities player3 = mcrfpy.Entity((5, 12)) # Left side ally = mcrfpy.Entity((8, 12)) # Near player enemy1 = mcrfpy.Entity((35, 12)) # Behind wall enemy2 = mcrfpy.Entity((25, 12)) # Through door (should be visible) grid3.entities.append(player3) grid3.entities.append(ally) grid3.entities.append(enemy1) grid3.entities.append(enemy2) # Set grid FOV settings grid3.fov = mcrfpy.FOV.SHADOW grid3.fov_radius = 20 # Get visible entities from player visible = player3.visible_entities() visible_positions = [(int(e.x), int(e.y)) for e in visible] print(f" Player at (5, 12)") print(f" Visible entities: {visible_positions}") # Ally should be visible assert (8, 12) in visible_positions, "Ally at (8,12) should be visible" print(" Ally at (8, 12) is visible") # Enemy1 behind wall should NOT be visible assert (35, 12) not in visible_positions, "Enemy1 at (35,12) should NOT be visible (behind wall)" print(" Enemy1 at (35, 12) is NOT visible (behind wall)") # Enemy2 through door should be visible assert (25, 12) in visible_positions, "Enemy2 at (25,12) should be visible through door" print(" Enemy2 at (25, 12) is visible (through door)") print() # Test 9: visible_entities with radius override print("Test 9: visible_entities with radius override") # With small radius, only ally should be visible visible_small = player3.visible_entities(radius=4) visible_small_positions = [(int(e.x), int(e.y)) for e in visible_small] print(f" With radius=4: {visible_small_positions}") assert (8, 12) in visible_small_positions, "Ally should be visible with radius=4" assert (25, 12) not in visible_small_positions, "Enemy2 should NOT be visible with radius=4" print(" Correctly limited visibility to nearby entities") print() print("=== All Perspective Binding Tests Passed! ===") return True # Main execution if __name__ == "__main__": try: if run_tests(): print("\nPASS") sys.exit(0) else: print("\nFAIL") sys.exit(1) except Exception as e: print(f"\nFAIL: {e}") import traceback traceback.print_exc() sys.exit(1)