Score Board
Total Score
{info["total_score"]:.1f}
Last Reward
{info["last_reward"]:+.1f}
Steps
{info["steps"]} / {env.max_steps}
Manhattan Distance
{info["manhattan_distance"]}
Agent Position: ({agent_row}, {agent_col})
Goal Position: ({goal_row}, {goal_col})
Goal Reached: {goal_text}
Status: {html.escape(info["status"])}
Last Action: {html.escape(info["last_action"])}
Rule Fired:
{html.escape(info["rule_fired"])}
Reward Rules
-5.0 wall/obstacle
+1.0 closer to goal
-0.5 farther from goal
-0.1 same distance
+0.3 new-cell bonus
+50.0 goal reached
-10.0 timeout
"""
# ============================================================
# Gradio Event Functions
# ============================================================
def new_game(grid_size):
env = WarehouseGridWorldEnv(
grid_size=int(grid_size),
obstacle_density=0.20,
max_steps=100,
)
return env, render_grid(env), render_scoreboard(env)
def move_agent(env, action):
if env is None:
env = WarehouseGridWorldEnv(grid_size=9, obstacle_density=0.20, max_steps=100)
env.step(action)
return env, render_grid(env), render_scoreboard(env)
def move_up(env):
return move_agent(env, 0)
def move_right(env):
return move_agent(env, 1)
def move_down(env):
return move_agent(env, 2)
def move_left(env):
return move_agent(env, 3)
# ============================================================
# Styling and Keyboard Script
# ============================================================
APP_CSS = """
body {
background: #f7f9fc;
}
.main-title {
text-align: center;
margin-bottom: 0px;
}
.subtitle {
text-align: center;
color: #555;
margin-top: 0px;
}
.grid-panel {
display: flex;
justify-content: center;
align-items: center;
padding: 12px;
}
.warehouse-grid {
border-collapse: collapse;
border: 3px solid #2f4858;
background: white;
}
.warehouse-cell {
width: 42px;
height: 42px;
border: 2px solid #607d8b;
text-align: center;
vertical-align: middle;
font-weight: 800;
font-size: 16px;
font-family: Arial, sans-serif;
}
.cell-start {
background: #b9d7ff;
color: #0b3d91;
}
.cell-goal {
background: #2ecc71;
color: #063b1d;
}
.cell-obstacle {
background: #2f3e46;
color: #dce3e8;
}
.cell-empty {
background: #edf6fb;
color: #607d8b;
}
.agent-circle {
width: 30px;
height: 30px;
background: #e53935;
color: white;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
margin: auto;
font-size: 15px;
font-weight: 900;
box-shadow: 0 2px 5px rgba(0,0,0,0.35);
}
.score-card {
background: white;
border: 1px solid #d9e2ec;
border-radius: 14px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.score-card h3 {
margin-top: 0px;
}
.metric-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.metric-box {
background: #f1f5f9;
border-radius: 10px;
padding: 10px;
}
.metric-label {
color: #64748b;
font-size: 12px;
}
.metric-value {
color: #111827;
font-size: 20px;
font-weight: 800;
}
.detail-line {
margin-top: 8px;
font-size: 14px;
}
.rule-box {
margin-top: 12px;
padding: 10px;
background: #fff7ed;
border-left: 5px solid #fb923c;
border-radius: 8px;
font-size: 14px;
}
.reward-list {
margin-top: 6px;
}
.reward-list li {
margin-bottom: 5px;
}
.reward-list code {
background: #e5e7eb;
padding: 3px 6px;
border-radius: 6px;
font-weight: 800;
}
.control-note {
text-align: center;
color: #555;
font-size: 14px;
}
button {
font-weight: 700 !important;
}
"""
KEYBOARD_SCRIPT = """
"""
# ============================================================
# Gradio App
# ============================================================
with gr.Blocks(
title="Warehouse GridWorld Game",
css=APP_CSS,
head=KEYBOARD_SCRIPT,
) as demo:
env_state = gr.State()
gr.Markdown(
"""
# 🏗️ Warehouse GridWorld Game
Use the keyboard arrow keys or the on-screen buttons to move the red agent from S to G.
Obstacles are randomized at approximately 20% density on every reset.
"""
)
with gr.Row():
with gr.Column(scale=2):
grid_output = gr.HTML()
gr.Markdown(
"""
Controls: ↑ ↓ ← → arrow keys, or use the buttons below.
"""
)
with gr.Row():
up_btn = gr.Button("↑ Up", elem_id="up_btn")
with gr.Row():
left_btn = gr.Button("← Left", elem_id="left_btn")
down_btn = gr.Button("↓ Down", elem_id="down_btn")
right_btn = gr.Button("→ Right", elem_id="right_btn")
with gr.Column(scale=1):
grid_size = gr.Slider(
minimum=5,
maximum=15,
value=9,
step=1,
label="Grid Size",
)
reset_btn = gr.Button("🔄 Reset / Randomize Grid", variant="primary")
scoreboard_output = gr.HTML()
reset_btn.click(
fn=new_game,
inputs=[grid_size],
outputs=[env_state, grid_output, scoreboard_output],
)
up_btn.click(
fn=move_up,
inputs=[env_state],
outputs=[env_state, grid_output, scoreboard_output],
)
right_btn.click(
fn=move_right,
inputs=[env_state],
outputs=[env_state, grid_output, scoreboard_output],
)
down_btn.click(
fn=move_down,
inputs=[env_state],
outputs=[env_state, grid_output, scoreboard_output],
)
left_btn.click(
fn=move_left,
inputs=[env_state],
outputs=[env_state, grid_output, scoreboard_output],
)
demo.load(
fn=new_game,
inputs=[grid_size],
outputs=[env_state, grid_output, scoreboard_output],
)
if __name__ == "__main__":
demo.launch()