"""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/', 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/', 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/', 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/', 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)