Initial commit

This commit is contained in:
2025-11-20 22:58:11 -05:00
commit 6d75c8e94e
51 changed files with 5141 additions and 0 deletions

1
api/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Web API for maze generation and solving."""

296
api/app.py Normal file
View File

@@ -0,0 +1,296 @@
"""Main Flask application for the Maze Generator API."""
import os
import sys
from pathlib import Path
# Add parent directory to path to import src modules
sys.path.insert(0, str(Path(__file__).parent.parent))
from flask import Flask, jsonify, request, send_file, render_template
from flask_cors import CORS
from src.generators import *
from src.solvers import *
from src.core.maze import Maze
from src.storage.file_handler import FileHandler
from src.visualization.image_renderer import ImageRenderer
from src.visualization.web_renderer import WebRenderer
from src.analysis.analyzer import MazeAnalyzer
from src.analysis.benchmark import Benchmark
app = Flask(__name__,
template_folder='../web/templates',
static_folder='../web/static')
CORS(app)
# Generator mapping
GENERATORS = {
'recursive_backtracking': RecursiveBacktrackingGenerator(),
'kruskal': KruskalGenerator(),
'prim': PrimGenerator(),
'sidewinder': SidewinderGenerator(),
'hunt_and_kill': HuntAndKillGenerator(),
'eller': EllerGenerator(),
'wilson': WilsonGenerator(),
'aldous_broder': AldousBroderGenerator()
}
# Solver mapping
SOLVERS = {
'dfs': DFSSolver(),
'bfs': BFSSolver()
}
# Store mazes in memory (keyed by ID)
mazes = {}
maze_counter = 0
@app.route('/')
def index():
"""Serve the main web interface."""
return render_template('index.html')
@app.route('/api/algorithms', methods=['GET'])
def get_algorithms():
"""Get list of available algorithms."""
return jsonify({
'generators': list(GENERATORS.keys()),
'solvers': list(SOLVERS.keys())
})
@app.route('/api/generate', methods=['POST'])
def generate_maze():
"""Generate a new maze."""
global maze_counter
data = request.json
# Validate input
algorithm = data.get('algorithm', 'recursive_backtracking')
rows = data.get('rows', 10)
cols = data.get('cols', 10)
seed = data.get('seed')
if algorithm not in GENERATORS:
return jsonify({'error': f'Unknown algorithm: {algorithm}'}), 400
if not (5 <= rows <= 50) or not (5 <= cols <= 50):
return jsonify({'error': 'Dimensions must be between 5 and 50'}), 400
try:
# Generate maze
generator = GENERATORS[algorithm]
maze = generator.generate(rows, cols, seed)
# Store maze
maze_id = maze_counter
mazes[maze_id] = maze
maze_counter += 1
# Return maze data
return jsonify({
'id': maze_id,
'maze': WebRenderer.to_json_format(maze),
'success': True
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/maze/<int:maze_id>', methods=['GET'])
def get_maze(maze_id):
"""Get maze by ID."""
if maze_id not in mazes:
return jsonify({'error': 'Maze not found'}), 404
maze = mazes[maze_id]
return jsonify({
'id': maze_id,
'maze': WebRenderer.to_json_format(maze)
})
@app.route('/api/solve', methods=['POST'])
def solve_maze():
"""Solve a maze."""
data = request.json
maze_id = data.get('maze_id')
algorithm = data.get('algorithm', 'bfs')
if maze_id is None or maze_id not in mazes:
return jsonify({'error': 'Maze not found'}), 404
if algorithm not in SOLVERS:
return jsonify({'error': f'Unknown solver: {algorithm}'}), 400
try:
maze = mazes[maze_id]
solver = SOLVERS[algorithm]
result = solver.solve(maze)
return jsonify({
'success': result['success'],
'path': result['path'],
'visited': result['visited'],
'time_ms': result['time_ms'],
'path_length': result['path_length'],
'algorithm': result['algorithm']
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/analyze/<int:maze_id>', methods=['GET'])
def analyze_maze(maze_id):
"""Analyze a maze."""
if maze_id not in mazes:
return jsonify({'error': 'Maze not found'}), 404
try:
maze = mazes[maze_id]
analysis = MazeAnalyzer.analyze(maze)
return jsonify(analysis)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/benchmark', methods=['POST'])
def benchmark():
"""Run algorithm benchmarks."""
data = request.json or {}
benchmark_type = data.get('type', 'quick')
try:
if benchmark_type == 'quick':
results = Benchmark.quick_benchmark()
elif benchmark_type == 'generators':
sizes = data.get('sizes', [(10, 10), (25, 25)])
iterations = data.get('iterations', 3)
results = Benchmark.benchmark_generators(sizes, iterations)
elif benchmark_type == 'solvers':
sizes = data.get('sizes', [(10, 10), (25, 25)])
iterations = data.get('iterations', 3)
results = Benchmark.benchmark_solvers(sizes, iterations)
else:
return jsonify({'error': 'Invalid benchmark type'}), 400
return jsonify(results)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/download/<int:maze_id>', methods=['GET'])
def download_maze_image(maze_id):
"""Download maze as an image."""
if maze_id not in mazes:
return jsonify({'error': 'Maze not found'}), 404
try:
maze = mazes[maze_id]
# Get optional parameters
include_solution = request.args.get('solution', 'false').lower() == 'true'
solver_algorithm = request.args.get('solver', 'bfs')
solution_path = None
visited_cells = None
if include_solution and solver_algorithm in SOLVERS:
solver = SOLVERS[solver_algorithm]
result = solver.solve(maze)
if result['success']:
solution_path = result['path']
visited_cells = result['visited']
# Render image
renderer = ImageRenderer(cell_size=20, wall_thickness=2)
filename = f"maze_{maze_id}_{maze.algorithm_used.replace(' ', '_')}"
filepath = renderer.render(
maze,
filename,
solution_path=solution_path,
visited_cells=visited_cells
)
return send_file(filepath, mimetype='image/png', as_attachment=True)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/save/<int:maze_id>', methods=['POST'])
def save_maze(maze_id):
"""Save a maze to file."""
if maze_id not in mazes:
return jsonify({'error': 'Maze not found'}), 404
data = request.json or {}
filename = data.get('filename', f'maze_{maze_id}')
try:
maze = mazes[maze_id]
filepath = FileHandler.save_maze(maze, filename)
return jsonify({
'success': True,
'filepath': filepath
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/load', methods=['POST'])
def load_maze():
"""Load a maze from file."""
global maze_counter
data = request.json
filename = data.get('filename')
if not filename:
return jsonify({'error': 'Filename required'}), 400
try:
maze = FileHandler.load_maze(filename)
# Store maze
maze_id = maze_counter
mazes[maze_id] = maze
maze_counter += 1
return jsonify({
'id': maze_id,
'maze': WebRenderer.to_json_format(maze),
'success': True
})
except FileNotFoundError:
return jsonify({'error': 'File not found'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/saved-mazes', methods=['GET'])
def list_saved_mazes():
"""List all saved maze files."""
try:
files = FileHandler.list_saved_mazes()
return jsonify({'files': files})
except Exception as e:
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)