McRogueFace/tests/dijkstra_interactive_enhanc...

344 lines
11 KiB
Python

#!/usr/bin/env python3
"""
Enhanced Dijkstra Pathfinding Interactive Demo
==============================================
Interactive visualization with entity pathfinding animations.
Controls:
- Press 1/2/3 to select the first entity
- Press A/B/C to select the second entity
- Space to clear selection
- M to make selected entity move along path
- P to pause/resume animation
- R to reset entity positions
- Q or ESC to quit
"""
import mcrfpy
import sys
import math
# Colors
WALL_COLOR = mcrfpy.Color(60, 30, 30)
FLOOR_COLOR = mcrfpy.Color(200, 200, 220)
PATH_COLOR = mcrfpy.Color(200, 250, 220)
VISITED_COLOR = mcrfpy.Color(180, 230, 200)
ENTITY_COLORS = [
mcrfpy.Color(255, 100, 100), # Entity 1 - Red
mcrfpy.Color(100, 255, 100), # Entity 2 - Green
mcrfpy.Color(100, 100, 255), # Entity 3 - Blue
]
# Global state
grid = None
entities = []
first_point = None
second_point = None
current_path = []
animating = False
animation_progress = 0.0
animation_speed = 2.0 # cells per second
original_positions = [] # Store original entity positions
def create_map():
"""Create the interactive map with the layout specified by the user"""
global grid, entities, original_positions
mcrfpy.createScene("dijkstra_enhanced")
# Create grid - 14x10 as specified
grid = mcrfpy.Grid(grid_x=14, grid_y=10)
grid.fill_color = mcrfpy.Color(0, 0, 0)
# Define the map layout from user's specification
# . = floor, W = wall, E = entity position
map_layout = [
"..............", # Row 0
"..W.....WWWW..", # Row 1
"..W.W...W.EW..", # Row 2
"..W.....W..W..", # Row 3
"..W...E.WWWW..", # Row 4
"E.W...........", # Row 5
"..W...........", # Row 6
"..W...........", # Row 7
"..W.WWW.......", # Row 8
"..............", # Row 9
]
# Create the map
entity_positions = []
for y, row in enumerate(map_layout):
for x, char in enumerate(row):
cell = grid.at(x, y)
if char == 'W':
# Wall
cell.walkable = False
cell.transparent = False
cell.color = WALL_COLOR
else:
# Floor
cell.walkable = True
cell.transparent = True
cell.color = FLOOR_COLOR
if char == 'E':
# Entity position
entity_positions.append((x, y))
# Create entities at marked positions
entities = []
original_positions = []
for i, (x, y) in enumerate(entity_positions):
entity = mcrfpy.Entity(x, y)
entity.sprite_index = 49 + i # '1', '2', '3'
grid.entities.append(entity)
entities.append(entity)
original_positions.append((x, y))
return grid
def clear_path_highlight():
"""Clear any existing path highlighting"""
global current_path
# Reset all floor tiles to original color
for y in range(grid.grid_y):
for x in range(grid.grid_x):
cell = grid.at(x, y)
if cell.walkable:
cell.color = FLOOR_COLOR
current_path = []
def highlight_path():
"""Highlight the path between selected entities using entity.path_to()"""
global current_path
if first_point is None or second_point is None:
return
# Clear previous highlighting
clear_path_highlight()
# Get entities
entity1 = entities[first_point]
entity2 = entities[second_point]
# Use the new path_to method!
path = entity1.path_to(int(entity2.x), int(entity2.y))
if path:
current_path = path
# Highlight the path
for i, (x, y) in enumerate(path):
cell = grid.at(x, y)
if cell.walkable:
# Use gradient for path visualization
if i < len(path) - 1:
cell.color = PATH_COLOR
else:
cell.color = VISITED_COLOR
# Highlight start and end with entity colors
grid.at(int(entity1.x), int(entity1.y)).color = ENTITY_COLORS[first_point]
grid.at(int(entity2.x), int(entity2.y)).color = ENTITY_COLORS[second_point]
# Update info
info_text.text = f"Path: Entity {first_point+1} to Entity {second_point+1} - {len(path)} steps"
else:
info_text.text = f"No path between Entity {first_point+1} and Entity {second_point+1}"
current_path = []
def animate_movement(dt):
"""Animate entity movement along path"""
global animation_progress, animating, current_path
if not animating or not current_path or first_point is None:
return
entity = entities[first_point]
# Update animation progress
animation_progress += animation_speed * dt
# Calculate current position along path
path_index = int(animation_progress)
if path_index >= len(current_path):
# Animation complete
animating = False
animation_progress = 0.0
# Snap to final position
if current_path:
final_x, final_y = current_path[-1]
entity.x = float(final_x)
entity.y = float(final_y)
return
# Interpolate between path points
if path_index < len(current_path) - 1:
curr_x, curr_y = current_path[path_index]
next_x, next_y = current_path[path_index + 1]
# Calculate interpolation factor
t = animation_progress - path_index
# Smooth interpolation
entity.x = curr_x + (next_x - curr_x) * t
entity.y = curr_y + (next_y - curr_y) * t
else:
# At last point
entity.x, entity.y = current_path[path_index]
def handle_keypress(scene_name, keycode):
"""Handle keyboard input"""
global first_point, second_point, animating, animation_progress
# Number keys for first entity
if keycode == 49: # '1'
first_point = 0
status_text.text = f"First: Entity 1 | Second: {f'Entity {second_point+1}' if second_point is not None else '?'}"
highlight_path()
elif keycode == 50: # '2'
first_point = 1
status_text.text = f"First: Entity 2 | Second: {f'Entity {second_point+1}' if second_point is not None else '?'}"
highlight_path()
elif keycode == 51: # '3'
first_point = 2
status_text.text = f"First: Entity 3 | Second: {f'Entity {second_point+1}' if second_point is not None else '?'}"
highlight_path()
# Letter keys for second entity
elif keycode == 65 or keycode == 97: # 'A' or 'a'
second_point = 0
status_text.text = f"First: {f'Entity {first_point+1}' if first_point is not None else '?'} | Second: Entity 1"
highlight_path()
elif keycode == 66 or keycode == 98: # 'B' or 'b'
second_point = 1
status_text.text = f"First: {f'Entity {first_point+1}' if first_point is not None else '?'} | Second: Entity 2"
highlight_path()
elif keycode == 67 or keycode == 99: # 'C' or 'c'
second_point = 2
status_text.text = f"First: {f'Entity {first_point+1}' if first_point is not None else '?'} | Second: Entity 3"
highlight_path()
# Movement control
elif keycode == 77 or keycode == 109: # 'M' or 'm'
if current_path and first_point is not None:
animating = True
animation_progress = 0.0
control_text.text = "Animation: MOVING (press P to pause)"
# Pause/Resume
elif keycode == 80 or keycode == 112: # 'P' or 'p'
animating = not animating
control_text.text = f"Animation: {'MOVING' if animating else 'PAUSED'} (press P to {'pause' if animating else 'resume'})"
# Reset positions
elif keycode == 82 or keycode == 114: # 'R' or 'r'
animating = False
animation_progress = 0.0
for i, entity in enumerate(entities):
entity.x, entity.y = original_positions[i]
control_text.text = "Entities reset to original positions"
highlight_path() # Re-highlight path after reset
# Clear selection
elif keycode == 32: # Space
first_point = None
second_point = None
animating = False
animation_progress = 0.0
clear_path_highlight()
status_text.text = "Press 1/2/3 for first entity, A/B/C for second"
info_text.text = "Space to clear, Q to quit"
control_text.text = "Press M to move, P to pause, R to reset"
# Quit
elif keycode == 81 or keycode == 113 or keycode == 256: # Q/q/ESC
print("\nExiting enhanced Dijkstra demo...")
sys.exit(0)
# Timer callback for animation
def update_animation(dt):
"""Update animation state"""
animate_movement(dt / 1000.0) # Convert ms to seconds
# Create the visualization
print("Enhanced Dijkstra Pathfinding Demo")
print("==================================")
print("Controls:")
print(" 1/2/3 - Select first entity")
print(" A/B/C - Select second entity")
print(" M - Move first entity along path")
print(" P - Pause/Resume animation")
print(" R - Reset entity positions")
print(" Space - Clear selection")
print(" Q/ESC - Quit")
# Create map
grid = create_map()
# Set up UI
ui = mcrfpy.sceneUI("dijkstra_enhanced")
ui.append(grid)
# Scale and position grid for better visibility
grid.size = (560, 400) # 14*40, 10*40
grid.position = (120, 60)
# Add title
title = mcrfpy.Caption("Enhanced Dijkstra Pathfinding", 250, 10)
title.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(title)
# Add status text
status_text = mcrfpy.Caption("Press 1/2/3 for first entity, A/B/C for second", 120, 480)
status_text.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(status_text)
# Add info text
info_text = mcrfpy.Caption("Space to clear, Q to quit", 120, 500)
info_text.fill_color = mcrfpy.Color(200, 200, 200)
ui.append(info_text)
# Add control text
control_text = mcrfpy.Caption("Press M to move, P to pause, R to reset", 120, 520)
control_text.fill_color = mcrfpy.Color(150, 200, 150)
ui.append(control_text)
# Add legend
legend1 = mcrfpy.Caption("Entities: 1=Red 2=Green 3=Blue", 120, 560)
legend1.fill_color = mcrfpy.Color(150, 150, 150)
ui.append(legend1)
legend2 = mcrfpy.Caption("Colors: Dark=Wall Light=Floor Cyan=Path", 120, 580)
legend2.fill_color = mcrfpy.Color(150, 150, 150)
ui.append(legend2)
# Mark entity positions with colored indicators
for i, entity in enumerate(entities):
marker = mcrfpy.Caption(str(i+1),
120 + int(entity.x) * 40 + 15,
60 + int(entity.y) * 40 + 10)
marker.fill_color = ENTITY_COLORS[i]
marker.outline = 1
marker.outline_color = mcrfpy.Color(0, 0, 0)
ui.append(marker)
# Set up input handling
mcrfpy.keypressScene(handle_keypress)
# Set up animation timer (60 FPS)
mcrfpy.setTimer("animation", update_animation, 16)
# Show the scene
mcrfpy.setScene("dijkstra_enhanced")
print("\nVisualization ready!")
print("Entities are at:")
for i, entity in enumerate(entities):
print(f" Entity {i+1}: ({int(entity.x)}, {int(entity.y)})")