236 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
#!/usr/bin/env python3
 | 
						|
"""
 | 
						|
A* vs Dijkstra Visual Comparison
 | 
						|
=================================
 | 
						|
 | 
						|
Shows the difference between A* (single target) and Dijkstra (multi-target).
 | 
						|
"""
 | 
						|
 | 
						|
import mcrfpy
 | 
						|
import sys
 | 
						|
 | 
						|
# Colors
 | 
						|
WALL_COLOR = mcrfpy.Color(40, 20, 20)
 | 
						|
FLOOR_COLOR = mcrfpy.Color(60, 60, 80)
 | 
						|
ASTAR_COLOR = mcrfpy.Color(0, 255, 0)      # Green for A*
 | 
						|
DIJKSTRA_COLOR = mcrfpy.Color(0, 150, 255) # Blue for Dijkstra
 | 
						|
START_COLOR = mcrfpy.Color(255, 100, 100)  # Red for start
 | 
						|
END_COLOR = mcrfpy.Color(255, 255, 100)    # Yellow for end
 | 
						|
 | 
						|
# Global state
 | 
						|
grid = None
 | 
						|
mode = "ASTAR"
 | 
						|
start_pos = (5, 10)
 | 
						|
end_pos = (27, 10)  # Changed from 25 to 27 to avoid the wall
 | 
						|
 | 
						|
def create_map():
 | 
						|
    """Create a map with obstacles to show pathfinding differences"""
 | 
						|
    global grid
 | 
						|
    
 | 
						|
    mcrfpy.createScene("pathfinding_comparison")
 | 
						|
    
 | 
						|
    # Create grid
 | 
						|
    grid = mcrfpy.Grid(grid_x=30, grid_y=20)
 | 
						|
    grid.fill_color = mcrfpy.Color(0, 0, 0)
 | 
						|
    
 | 
						|
    # Initialize all as floor
 | 
						|
    for y in range(20):
 | 
						|
        for x in range(30):
 | 
						|
            grid.at(x, y).walkable = True
 | 
						|
            grid.at(x, y).color = FLOOR_COLOR
 | 
						|
    
 | 
						|
    # Create obstacles that make A* and Dijkstra differ
 | 
						|
    obstacles = [
 | 
						|
        # Vertical wall with gaps
 | 
						|
        [(15, y) for y in range(3, 17) if y not in [8, 12]],
 | 
						|
        # Horizontal walls
 | 
						|
        [(x, 5) for x in range(10, 20)],
 | 
						|
        [(x, 15) for x in range(10, 20)],
 | 
						|
        # Maze-like structure
 | 
						|
        [(x, 10) for x in range(20, 25)],
 | 
						|
        [(25, y) for y in range(5, 15)],
 | 
						|
    ]
 | 
						|
    
 | 
						|
    for obstacle_group in obstacles:
 | 
						|
        for x, y in obstacle_group:
 | 
						|
            grid.at(x, y).walkable = False
 | 
						|
            grid.at(x, y).color = WALL_COLOR
 | 
						|
    
 | 
						|
    # Mark start and end
 | 
						|
    grid.at(start_pos[0], start_pos[1]).color = START_COLOR
 | 
						|
    grid.at(end_pos[0], end_pos[1]).color = END_COLOR
 | 
						|
 | 
						|
def clear_paths():
 | 
						|
    """Clear path highlighting"""
 | 
						|
    for y in range(20):
 | 
						|
        for x in range(30):
 | 
						|
            cell = grid.at(x, y)
 | 
						|
            if cell.walkable:
 | 
						|
                cell.color = FLOOR_COLOR
 | 
						|
    
 | 
						|
    # Restore start and end colors
 | 
						|
    grid.at(start_pos[0], start_pos[1]).color = START_COLOR
 | 
						|
    grid.at(end_pos[0], end_pos[1]).color = END_COLOR
 | 
						|
 | 
						|
def show_astar():
 | 
						|
    """Show A* path"""
 | 
						|
    clear_paths()
 | 
						|
    
 | 
						|
    # Compute A* path
 | 
						|
    path = grid.compute_astar_path(start_pos[0], start_pos[1], end_pos[0], end_pos[1])
 | 
						|
    
 | 
						|
    # Color the path
 | 
						|
    for i, (x, y) in enumerate(path):
 | 
						|
        if (x, y) != start_pos and (x, y) != end_pos:
 | 
						|
            grid.at(x, y).color = ASTAR_COLOR
 | 
						|
    
 | 
						|
    status_text.text = f"A* Path: {len(path)} steps (optimized for single target)"
 | 
						|
    status_text.fill_color = ASTAR_COLOR
 | 
						|
 | 
						|
def show_dijkstra():
 | 
						|
    """Show Dijkstra exploration"""
 | 
						|
    clear_paths()
 | 
						|
    
 | 
						|
    # Compute Dijkstra from start
 | 
						|
    grid.compute_dijkstra(start_pos[0], start_pos[1])
 | 
						|
    
 | 
						|
    # Color cells by distance (showing exploration)
 | 
						|
    max_dist = 40.0
 | 
						|
    for y in range(20):
 | 
						|
        for x in range(30):
 | 
						|
            if grid.at(x, y).walkable:
 | 
						|
                dist = grid.get_dijkstra_distance(x, y)
 | 
						|
                if dist is not None and dist < max_dist:
 | 
						|
                    # Color based on distance
 | 
						|
                    intensity = int(255 * (1 - dist / max_dist))
 | 
						|
                    grid.at(x, y).color = mcrfpy.Color(0, intensity // 2, intensity)
 | 
						|
    
 | 
						|
    # Get the actual path
 | 
						|
    path = grid.get_dijkstra_path(end_pos[0], end_pos[1])
 | 
						|
    
 | 
						|
    # Highlight the actual path more brightly
 | 
						|
    for x, y in path:
 | 
						|
        if (x, y) != start_pos and (x, y) != end_pos:
 | 
						|
            grid.at(x, y).color = DIJKSTRA_COLOR
 | 
						|
    
 | 
						|
    # Restore start and end
 | 
						|
    grid.at(start_pos[0], start_pos[1]).color = START_COLOR
 | 
						|
    grid.at(end_pos[0], end_pos[1]).color = END_COLOR
 | 
						|
    
 | 
						|
    status_text.text = f"Dijkstra: {len(path)} steps (explores all directions)"
 | 
						|
    status_text.fill_color = DIJKSTRA_COLOR
 | 
						|
 | 
						|
def show_both():
 | 
						|
    """Show both paths overlaid"""
 | 
						|
    clear_paths()
 | 
						|
    
 | 
						|
    # Get both paths
 | 
						|
    astar_path = grid.compute_astar_path(start_pos[0], start_pos[1], end_pos[0], end_pos[1])
 | 
						|
    grid.compute_dijkstra(start_pos[0], start_pos[1])
 | 
						|
    dijkstra_path = grid.get_dijkstra_path(end_pos[0], end_pos[1])
 | 
						|
    
 | 
						|
    print(astar_path, dijkstra_path)
 | 
						|
 | 
						|
    # Color Dijkstra path first (blue)
 | 
						|
    for x, y in dijkstra_path:
 | 
						|
        if (x, y) != start_pos and (x, y) != end_pos:
 | 
						|
            grid.at(x, y).color = DIJKSTRA_COLOR
 | 
						|
    
 | 
						|
    # Then A* path (green) - will overwrite shared cells
 | 
						|
    for x, y in astar_path:
 | 
						|
        if (x, y) != start_pos and (x, y) != end_pos:
 | 
						|
            grid.at(x, y).color = ASTAR_COLOR
 | 
						|
    
 | 
						|
    # Mark differences
 | 
						|
    different_cells = []
 | 
						|
    for cell in dijkstra_path:
 | 
						|
        if cell not in astar_path:
 | 
						|
            different_cells.append(cell)
 | 
						|
    
 | 
						|
    status_text.text = f"Both paths: A*={len(astar_path)} steps, Dijkstra={len(dijkstra_path)} steps"
 | 
						|
    if different_cells:
 | 
						|
        info_text.text = f"Paths differ at {len(different_cells)} cells"
 | 
						|
    else:
 | 
						|
        info_text.text = "Paths are identical"
 | 
						|
 | 
						|
def handle_keypress(key_str, state):
 | 
						|
    """Handle keyboard input"""
 | 
						|
    global mode
 | 
						|
    if state == "end": return
 | 
						|
    print(key_str)
 | 
						|
    if key_str == "Esc" or key_str == "Q":
 | 
						|
        print("\nExiting...")
 | 
						|
        sys.exit(0)
 | 
						|
    elif key_str == "A" or key_str == "1":
 | 
						|
        mode = "ASTAR"
 | 
						|
        show_astar()
 | 
						|
    elif key_str == "D" or key_str == "2":
 | 
						|
        mode = "DIJKSTRA"
 | 
						|
        show_dijkstra()
 | 
						|
    elif key_str == "B" or key_str == "3":
 | 
						|
        mode = "BOTH"
 | 
						|
        show_both()
 | 
						|
    elif key_str == "Space":
 | 
						|
        # Refresh current mode
 | 
						|
        if mode == "ASTAR":
 | 
						|
            show_astar()
 | 
						|
        elif mode == "DIJKSTRA":
 | 
						|
            show_dijkstra()
 | 
						|
        else:
 | 
						|
            show_both()
 | 
						|
 | 
						|
# Create the demo
 | 
						|
print("A* vs Dijkstra Pathfinding Comparison")
 | 
						|
print("=====================================")
 | 
						|
print("Controls:")
 | 
						|
print("  A or 1 - Show A* path (green)")
 | 
						|
print("  D or 2 - Show Dijkstra (blue gradient)")
 | 
						|
print("  B or 3 - Show both paths")
 | 
						|
print("  Q/ESC  - Quit")
 | 
						|
print()
 | 
						|
print("A* is optimized for single-target pathfinding")
 | 
						|
print("Dijkstra explores in all directions (good for multiple targets)")
 | 
						|
 | 
						|
create_map()
 | 
						|
 | 
						|
# Set up UI
 | 
						|
ui = mcrfpy.sceneUI("pathfinding_comparison")
 | 
						|
ui.append(grid)
 | 
						|
 | 
						|
# Scale and position
 | 
						|
grid.size = (600, 400)  # 30*20, 20*20
 | 
						|
grid.position = (100, 100)
 | 
						|
 | 
						|
# Add title
 | 
						|
title = mcrfpy.Caption("A* vs Dijkstra Pathfinding", 250, 20)
 | 
						|
title.fill_color = mcrfpy.Color(255, 255, 255)
 | 
						|
ui.append(title)
 | 
						|
 | 
						|
# Add status
 | 
						|
status_text = mcrfpy.Caption("Press A for A*, D for Dijkstra, B for Both", 100, 60)
 | 
						|
status_text.fill_color = mcrfpy.Color(200, 200, 200)
 | 
						|
ui.append(status_text)
 | 
						|
 | 
						|
# Add info
 | 
						|
info_text = mcrfpy.Caption("", 100, 520)
 | 
						|
info_text.fill_color = mcrfpy.Color(200, 200, 200)
 | 
						|
ui.append(info_text)
 | 
						|
 | 
						|
# Add legend
 | 
						|
legend1 = mcrfpy.Caption("Red=Start, Yellow=End, Green=A*, Blue=Dijkstra", 100, 540)
 | 
						|
legend1.fill_color = mcrfpy.Color(150, 150, 150)
 | 
						|
ui.append(legend1)
 | 
						|
 | 
						|
legend2 = mcrfpy.Caption("Dark=Walls, Light=Floor", 100, 560)
 | 
						|
legend2.fill_color = mcrfpy.Color(150, 150, 150)
 | 
						|
ui.append(legend2)
 | 
						|
 | 
						|
# Set scene and input
 | 
						|
mcrfpy.setScene("pathfinding_comparison")
 | 
						|
mcrfpy.keypressScene(handle_keypress)
 | 
						|
 | 
						|
# Show initial A* path
 | 
						|
show_astar()
 | 
						|
 | 
						|
print("\nDemo ready!")
 |