Spaces:
Sleeping
Sleeping
File size: 6,887 Bytes
9d033ac 8cb2e6b 963106e 9d033ac ebed37f bf961f2 ebed37f 8cb2e6b bf961f2 8cb2e6b bf961f2 ebed37f 8cb2e6b 963106e bf961f2 8cb2e6b ae06dab ebed37f 8cb2e6b ae06dab 8cb2e6b bf961f2 ebed37f bf961f2 ae06dab 8cb2e6b bf961f2 ae06dab bf961f2 8cb2e6b bf961f2 ae06dab bf961f2 8cb2e6b ebed37f bf961f2 33d0d9c bf961f2 963106e ae06dab bf961f2 33d0d9c bf961f2 33d0d9c bf961f2 33d0d9c ebed37f bf961f2 33d0d9c bf961f2 33d0d9c bf961f2 33d0d9c 8cb2e6b 93fbe51 963106e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | import gymnasium as gym
import numpy as np
import gradio as gr
from PIL import Image, ImageDraw
import collections
import time
class GridWorldEnv(gym.Env):
def __init__(self, size=8, density=0.2):
super().__init__()
self.size = size
self.density = density
self.q_table = np.zeros([size * size, 4])
self.reset_maze()
def _pos_to_state(self, pos): return int(pos[0] * self.size + pos[1])
def reset_maze(self):
while True:
coords = [(r, c) for r in range(self.size) for c in range(self.size)]
np.random.shuffle(coords)
self.start_pos, self.goal_pos = np.array(coords[0]), np.array(coords[1])
self.obstacles = {coords[i] for i in range(2, len(coords)) if np.random.random() < self.density}
if self._is_solvable(): break
self.reset_agent()
def _is_solvable(self):
q = collections.deque([tuple(self.start_pos)])
v = {tuple(self.start_pos)}
while q:
curr = q.popleft()
if curr == tuple(self.goal_pos): return True
for d in [(-1,0), (1,0), (0,-1), (0,1)]:
nxt = (curr[0]+d[0], curr[1]+d[1])
if 0 <= nxt[0] < self.size and 0 <= nxt[1] < self.size and nxt not in self.obstacles and nxt not in v:
v.add(nxt); q.append(nxt)
return False
def reset_agent(self):
self.agent_pos = self.start_pos.copy()
self.steps, self.total_reward = 0, 0
return self._pos_to_state(self.agent_pos)
def step(self, action):
old_dist = np.sum(np.abs(self.agent_pos - self.goal_pos))
move = [(-1, 0), (0, 1), (1, 0), (0, -1)][action]
new_pos = self.agent_pos + move
reward, term, trunc = -0.1, False, False
if (0 <= new_pos[0] < self.size and 0 <= new_pos[1] < self.size and tuple(new_pos) not in self.obstacles):
self.agent_pos = new_pos
reward += (old_dist - np.sum(np.abs(self.agent_pos - self.goal_pos))) * 2
else: reward -= 1.0
self.steps += 1
if np.array_equal(self.agent_pos, self.goal_pos): reward += 50.0; term = True
elif self.steps >= self.size * self.size: trunc = True
self.total_reward += reward
return self._pos_to_state(self.agent_pos), reward, term, trunc
def render(self):
cell = 60
img = Image.new("RGB", (self.size*cell, self.size*cell), "white")
d = ImageDraw.Draw(img)
for r in range(self.size):
for c in range(self.size):
x, y = c*cell, r*cell
color = "#F0F0F0"
if (r,c) == tuple(self.start_pos): color = "#D0E0FF"
if (r,c) == tuple(self.goal_pos): color = "#D0FFD0"
if (r,c) in self.obstacles: color = "#444444"
d.rectangle([x, y, x+cell, y+cell], fill=color, outline="#CCC")
if (r,c) not in self.obstacles and (r,c) != tuple(self.goal_pos):
st = r*self.size + c
if np.max(self.q_table[st]) != 0:
cx, cy = x+cell//2, y+cell//2
ar = [[(0,-10),(-5,-5),(5,-5)],[(10,0),(5,-5),(5,5)],[(0,10),(-5,5),(5,5)],[(-10,0),(-5,-5),(-5,5)]]
d.polygon([(cx+px, cy+py) for px, py in ar[np.argmax(self.q_table[st])]], fill="#06F")
ar, ac = self.agent_pos
d.ellipse([ac*cell+15, ar*cell+15, (ac+1)*cell-15, (ar+1)*cell-15], fill="#F44")
return img
env = GridWorldEnv()
def get_stats():
return [["Score", f"{env.total_reward:.1f}"], ["Steps", f"{env.steps}"], ["Goal", str(tuple(env.goal_pos))]]
def solve_logic(mode, lr, df, eps, episodes, progress=gr.Progress()):
if mode == "New Brain": env.q_table.fill(0)
for ep in progress.tqdm(range(int(episodes)), desc="Training"):
state = env.reset_agent()
done = False
while not done:
action = np.random.randint(4) if np.random.rand() < eps else np.argmax(env.q_table[state])
next_s, reward, term, trunc = env.step(action)
env.q_table[state, action] += lr * (reward + df * np.max(env.q_table[next_s]) - env.q_table[state, action])
state, done = next_s, term or trunc
if ep % 10 == 0: yield env.render(), get_stats()
yield env.render(), get_stats()
def run_best_path():
state = env.reset_agent()
done = False
while not done:
action = np.argmax(env.q_table[state])
state, _, term, trunc = env.step(action)
done = term or trunc
yield env.render(), get_stats()
time.sleep(0.1)
with gr.Blocks(css=".gradio-container {background-color: #f8f9fa}") as demo:
gr.Markdown("# 🤖 AI Warehouse Navigator")
with gr.Row():
with gr.Column(scale=2):
board = gr.Image(label="Warehouse Grid", interactive=False)
with gr.Row():
up = gr.Button("⬆️"); left = gr.Button("⬅️"); down = gr.Button("⬇️"); right = gr.Button("➡️")
with gr.Column(scale=1):
stats_view = gr.Dataframe(headers=["Metric", "Value"], col_count=2, interactive=False)
with gr.Accordion("⚙️ Hyperparameters", open=False):
lr_sl = gr.Slider(0.01, 1.0, 0.1, label="Alpha (Learning Rate)")
df_sl = gr.Slider(0.1, 1.0, 0.9, label="Gamma (Discount Factor)")
eps_sl = gr.Slider(0.0, 1.0, 0.1, label="Epsilon (Exploration)")
ep_num = gr.Number(100, label="Episodes")
mode_st = gr.State("New Brain")
solve_btn = gr.Button("🧠 Train New Brain", variant="primary")
cont_btn = gr.Button("📈 Continue Training")
best_btn = gr.Button("🎯 Run Best Path", variant="secondary")
reset_btn = gr.Button("🔄 New Maze")
# Manual Actions
up.click(lambda: (env.step(0), env.render(), get_stats())[1:], None, [board, stats_view])
right.click(lambda: (env.step(1), env.render(), get_stats())[1:], None, [board, stats_view])
down.click(lambda: (env.step(2), env.render(), get_stats())[1:], None, [board, stats_view])
left.click(lambda: (env.step(3), env.render(), get_stats())[1:], None, [board, stats_view])
# RL Actions
solve_btn.click(fn=solve_logic, inputs=[gr.State("New Brain"), lr_sl, df_sl, eps_sl, ep_num], outputs=[board, stats_view])
cont_btn.click(fn=solve_logic, inputs=[gr.State("Keep Brain"), lr_sl, df_sl, eps_sl, ep_num], outputs=[board, stats_view])
best_btn.click(fn=run_best_path, outputs=[board, stats_view])
reset_btn.click(lambda: (env.reset_maze(), env.render(), get_stats()), None, [board, board, stats_view])
demo.load(lambda: (env.render(), get_stats()), None, [board, stats_view])
if __name__ == "__main__":
demo.queue().launch() # demo.queue() is mandatory for generators
|