import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
import numpy as np
# Initialize the Dash app
app = dash.Dash(__name__)
# Maze generation using a simple algorithm (DFS)
def generate_maze(n, m):
maze = np.zeros((n, m), dtype=int)
visited = np.zeros((n, m), dtype=int)
def visit(x, y):
directions = [(1,0), (-1,0), (0,1), (0,-1)]
np.random.shuffle(directions)
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < m and not visited[nx, ny]:
maze[x, y] |= (1 << directions.index((dx, dy)))
maze[nx, ny] |= (1 << directions.index((-dx, -dy)))
visited[nx, ny] = 1
visit(nx, ny)
visited[0, 0] = 1
visit(0, 0)
return maze
# Maze-solving algorithm using DFS
def solve_maze(maze):
n, m = maze.shape
path = []
visited = np.zeros((n, m), dtype=int)
def dfs(x, y):
if x == n-1 and y == m-1:
path.append((x, y))
return True
visited[x, y] = 1
path.append((x, y))
directions = [(1,0), (-1,0), (0,1), (0,-1)]
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < m and not visited[nx, ny]:
if (dx == 1 and (maze[x, y] & 1)) or (dx == -1 and (maze[nx, ny] & 1)) or (dy == 1 and (maze[x, y] & 2)) or (dy == -1 and (maze[nx, ny] & 2)):
if dfs(nx, ny):
return True
path.pop()
return False
dfs(0, 0)
return path
# Convert maze to polar coordinates
def maze_to_polar(maze):
n, m = maze.shape
theta_step = 2 * np.pi / m
r_step = 1 / n
walls = []
for i in range(n):
for j in range(m):
if not (maze[i, j] & 1): # wall to the right
theta = (j + 1) * theta_step
walls.append(((i * r_step, theta), (i * r_step + r_step, theta)))
if not (maze[i, j] & 2): # wall down
r = (i + 1) * r_step
walls.append(((r, j * theta_step), (r, (j + 1) * theta_step)))
return walls
# Convert path to polar coordinates
def path_to_polar(path, n, m):
theta_step = 2 * np.pi / m
r_step = 1 / n
r_coords = []
theta_coords = []
for x, y in path:
r_coords.append((x + 0.5) * r_step)
theta_coords.append((y + 0.5) * theta_step)
return r_coords, theta_coords
# Ensure the path does not touch the walls
def adjust_path(path, maze):
n, m = maze.shape
adjusted_path = []
for i, (x, y) in enumerate(path):
if i > 0:
prev_x, prev_y = path[i - 1]
if x == prev_x:
if y > prev_y:
y -= 0.2
else:
y += 0.2
else:
if x > prev_x:
x -= 0.2
else:
x += 0.2
adjusted_path.append((x, y))
return adjusted_path
# Define the layout of the Dash app
app.layout = html.Div([
html.H1("Polar Maze Generator", style={'color': 'black', 'textAlign': 'center'}),
dcc.Graph(id='polar-maze-graph'),
dcc.Slider(id='maze-size-slider', min=5, max=50, step=1, value=30, marks={i: f'{i}' for i in range(5, 51, 5)}),
html.Div(id='maze-size-display', style={'textAlign': 'center'}),
html.Button('Generate New Maze', id='generate-button', n_clicks=0)
], style={'backgroundColor': 'white', 'padding': '20px'})
# Callback to update the maze size display
@app.callback(
Output('maze-size-display', 'children'),
[Input('maze-size-slider', 'value')]
)
def update_maze_size_display(value):
return f'Maze Size: {value}x{value}'
# Callback to generate a new maze
@app.callback(
Output('polar-maze-graph', 'figure'),
[Input('generate-button', 'n_clicks')],
[State('maze-size-slider', 'value')]
)
def update_maze_graph(n_clicks, maze_size):
maze = generate_maze(maze_size, maze_size)
walls = maze_to_polar(maze)
path = solve_maze(maze)
adjusted_path = adjust_path(path, maze)
r_coords, theta_coords = path_to_polar(adjusted_path, maze_size, maze_size)
fig = go.Figure()
for (r1, theta1), (r2, theta2) in walls:
fig.add_trace(go.Scatterpolar(
r=[r1, r2], theta=np.degrees([theta1, theta2]),
mode='lines', line=dict(color='blue')
))
fig.add_trace(go.Scatterpolar(
r=r_coords, theta=np.degrees(theta_coords),
mode='lines', line=dict(color='red', width=3),
name='Solution'
))
fig.update_layout(
polar=dict(
radialaxis=dict(visible=True, range=[0, 1]),
angularaxis=dict(visible=True)
),
showlegend=False,
margin=dict(l=0, r=0, t=0, b=0)
)
return fig
# Run the app
if __name__ == '__main__':
app.run_server(debug=True)