344 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			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)})") |