amped4power commited on
Commit
9d033ac
·
verified ·
1 Parent(s): 93fbe51

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +213 -126
app.py CHANGED
@@ -1,147 +1,234 @@
1
- import gymnasium as gym
2
- from gymnasium import spaces
3
  import numpy as np
4
  import gradio as gr
5
- from PIL import Image, ImageDraw
6
- import collections
7
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  class GridWorldEnv(gym.Env):
9
- def __init__(self, size=10, obstacle_density=0.2):
10
  super().__init__()
11
- self.size = size
12
- self.obstacle_density = obstacle_density
13
  self.observation_space = spaces.Box(low=0, high=1, shape=(4,), dtype=np.float32)
14
- self.action_space = spaces.Discrete(4) # 0:UP, 1:RIGHT, 2:DOWN, 3:LEFT
15
- self.reset()
16
 
17
- def _is_solvable(self, start, goal, obstacles):
18
- queue = collections.deque([tuple(start)])
19
- visited = {tuple(start)}
20
- while queue:
21
- curr = queue.popleft()
22
- if curr == tuple(goal): return True
23
- for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
24
- nxt = (curr[0] + dx, curr[1] + dy)
25
- if 0 <= nxt[0] < self.size and 0 <= nxt[1] < self.size:
26
- if nxt not in obstacles and nxt not in visited:
27
- visited.add(nxt)
28
- queue.append(nxt)
29
- return False
30
 
31
  def reset(self, seed=None, options=None):
32
- super().reset(seed=seed)
33
  while True:
34
- coords = [(r, c) for r in range(self.size) for c in range(self.size)]
35
- np.random.shuffle(coords)
36
- self.start_pos = np.array(coords[0])
37
- self.goal_pos = np.array(coords[1])
38
- self.obstacles = {coords[i] for i in range(2, len(coords)) if np.random.random() < self.obstacle_density}
39
- if self._is_solvable(self.start_pos, self.goal_pos, self.obstacles): break
40
-
41
- self.agent_pos = self.start_pos.copy()
42
- self.visited_cells = {tuple(self.agent_pos)}
43
- self.steps, self.total_reward, self.last_reward = 0, 0, 0
44
- self.max_steps = self.size * self.size
 
 
 
 
 
 
 
 
 
 
 
 
45
  return self._get_obs(), {}
46
 
47
  def _get_obs(self):
48
- return np.array([self.agent_pos[0]/self.size, self.agent_pos[1]/self.size,
49
- self.goal_pos[0]/self.size, self.goal_pos[1]/self.size], dtype=np.float32)
 
 
 
 
50
 
51
  def step(self, action):
52
- old_dist = np.sum(np.abs(self.agent_pos - self.goal_pos))
53
- move = [(-1, 0), (0, 1), (1, 0), (0, -1)][action]
54
- new_pos = self.agent_pos + move
55
- reward, term, trunc = 0, False, False
56
-
57
- if (0 <= new_pos[0] < self.size and 0 <= new_pos[1] < self.size and tuple(new_pos) not in self.obstacles):
58
- self.agent_pos = new_pos
59
- new_dist = np.sum(np.abs(self.agent_pos - self.goal_pos))
60
- reward += (old_dist - new_dist) * 2
61
- if tuple(self.agent_pos) not in self.visited_cells:
62
- reward += 0.5
63
- self.visited_cells.add(tuple(self.agent_pos))
 
 
 
 
 
 
 
64
  else:
65
- reward -= 1.0
66
 
 
 
 
 
 
 
 
 
 
 
67
  self.steps += 1
68
- if np.array_equal(self.agent_pos, self.goal_pos):
69
- reward += 20.0
70
- term = True
71
- elif self.steps >= self.max_steps:
72
- reward -= 5.0
73
- trunc = True
74
-
75
- self.last_reward, self.total_reward = reward, self.total_reward + reward
76
- return self._get_obs(), reward, term, trunc, {}
77
-
78
- def render_image(self):
79
- cell = 40
80
- img = Image.new("RGB", (self.size * cell, self.size * cell), "#F0F0F0")
81
- draw = ImageDraw.Draw(img)
82
- for r in range(self.size):
83
- for c in range(self.size):
84
- x0, y0, x1, y1 = c*cell, r*cell, (c+1)*cell, (r+1)*cell
85
- if (r, c) == tuple(self.start_pos): draw.rectangle([x0, y0, x1, y1], fill="blue")
86
- elif (r, c) == tuple(self.goal_pos): draw.rectangle([x0, y0, x1, y1], fill="green")
87
- elif (r, c) in self.obstacles: draw.rectangle([x0, y0, x1, y1], fill="#333333")
88
- draw.rectangle([x0, y0, x1, y1], outline="#CCCCCC")
89
- r, c = self.agent_pos
90
- draw.ellipse([c*cell+5, r*cell+5, (c+1)*cell-5, (r+1)*cell-5], fill="red")
91
- return img
92
-
93
- # --- Gradio UI ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  env = GridWorldEnv()
95
 
96
- def format_stats():
97
- dist = np.sum(np.abs(env.agent_pos - env.goal_pos))
98
- data = [
99
- ["Total Score", f"{env.total_reward:.2f}"],
100
- ["Last Reward", f"{env.last_reward:.2f}"],
101
- ["Step Counter", f"{env.steps} / {env.max_steps}"],
102
- ["Agent Position", str(tuple(env.agent_pos))],
103
- ["Goal Position", str(tuple(env.goal_pos))],
104
- ["Dist to Goal", str(int(dist))],
105
- ["Status", "GOAL REACHED" if np.array_equal(env.agent_pos, env.goal_pos) else "In Progress"]
106
- ]
107
- return env.render_image(), data
108
-
109
- def play(action):
110
- _, _, term, trunc, _ = env.step(action)
111
- img, data = format_stats()
112
- msg = "🎉 Success!" if term else ("⏰ Timeout!" if trunc else "Navigate to the green cell.")
113
- return img, data, msg
114
-
115
- with gr.Blocks(css=".gradio-container {background-color: #f8f9fa}") as demo:
116
- gr.Markdown("## 📦 Warehouse GridWorld\n**Goal:** Move the red agent to the green exit. Avoid dark obstacles.")
117
-
118
  with gr.Row():
119
- with gr.Column(scale=2):
120
- board = gr.Image(label="Warehouse Map", interactive=False)
121
- msg_box = gr.Markdown("Click buttons to move.")
122
-
123
- with gr.Column(scale=1):
124
- gr.Markdown("### 📊 Live Scoreboard")
125
- stats_table = gr.Dataframe(headers=["Metric", "Value"], col_count=2, interactive=False)
126
-
127
- with gr.Group():
128
- with gr.Row(): gr.Button("").click(None) # Spacer
129
- with gr.Row(): up = gr.Button("⬆️ UP")
130
- with gr.Row():
131
- left = gr.Button("⬅️ LEFT")
132
- down = gr.Button("⬇️ DOWN")
133
- right = gr.Button("➡️ RIGHT")
134
-
135
- reset = gr.Button("🔄 Reset Random Maze", variant="stop")
136
-
137
- # Bindings
138
- demo.load(format_stats, None, [board, stats_table])
139
- up.click(lambda: play(0), None, [board, stats_table, msg_box])
140
- right.click(lambda: play(1), None, [board, stats_table, msg_box])
141
- down.click(lambda: play(2), None, [board, stats_table, msg_box])
142
- left.click(lambda: play(3), None, [board, stats_table, msg_box])
143
- reset.click(lambda: (env.reset(), format_stats())[1], None, [board, stats_table])
144
-
145
- if __name__ == "__main__":
146
- demo.launch()
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import numpy as np
2
  import gradio as gr
3
+ import gymnasium as gym
4
+ from gymnasium import spaces
5
+ from collections import deque
6
+
7
+ GRID_SIZE = 8
8
+ OBSTACLE_DENSITY = 0.2
9
+ STEP_LIMIT = 100
10
+
11
+ # =========================
12
+ # Utility: BFS solvability
13
+ # =========================
14
+ def is_solvable(grid, start, goal):
15
+ q = deque([start])
16
+ visited = set([start])
17
+
18
+ while q:
19
+ x, y = q.popleft()
20
+ if (x, y) == goal:
21
+ return True
22
+
23
+ for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
24
+ nx, ny = x+dx, y+dy
25
+ if (
26
+ 0 <= nx < GRID_SIZE and
27
+ 0 <= ny < GRID_SIZE and
28
+ grid[nx, ny] != 1 and
29
+ (nx, ny) not in visited
30
+ ):
31
+ visited.add((nx, ny))
32
+ q.append((nx, ny))
33
+ return False
34
+
35
+
36
+ # =========================
37
+ # Gym Environment
38
+ # =========================
39
  class GridWorldEnv(gym.Env):
40
+ def __init__(self):
41
  super().__init__()
42
+
43
+ self.action_space = spaces.Discrete(4)
44
  self.observation_space = spaces.Box(low=0, high=1, shape=(4,), dtype=np.float32)
 
 
45
 
46
+ self.reset()
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  def reset(self, seed=None, options=None):
 
49
  while True:
50
+ self.grid = np.zeros((GRID_SIZE, GRID_SIZE), dtype=int)
51
+
52
+ # obstacles
53
+ for i in range(GRID_SIZE):
54
+ for j in range(GRID_SIZE):
55
+ if np.random.rand() < OBSTACLE_DENSITY:
56
+ self.grid[i, j] = 1
57
+
58
+ # start & goal
59
+ self.start = (np.random.randint(GRID_SIZE), np.random.randint(GRID_SIZE))
60
+ self.goal = (np.random.randint(GRID_SIZE), np.random.randint(GRID_SIZE))
61
+
62
+ self.grid[self.start] = 0
63
+ self.grid[self.goal] = 0
64
+
65
+ if self.start != self.goal and is_solvable(self.grid, self.start, self.goal):
66
+ break
67
+
68
+ self.agent = self.start
69
+ self.steps = 0
70
+ self.total_score = 0
71
+ self.visited = set([self.agent])
72
+
73
  return self._get_obs(), {}
74
 
75
  def _get_obs(self):
76
+ return np.array([
77
+ self.agent[0] / GRID_SIZE,
78
+ self.agent[1] / GRID_SIZE,
79
+ self.goal[0] / GRID_SIZE,
80
+ self.goal[1] / GRID_SIZE
81
+ ], dtype=np.float32)
82
 
83
  def step(self, action):
84
+ x, y = self.agent
85
+ old_dist = abs(x - self.goal[0]) + abs(y - self.goal[1])
86
+
87
+ moves = {
88
+ 0: (-1, 0), # up
89
+ 1: (0, 1), # right
90
+ 2: (1, 0), # down
91
+ 3: (0, -1) # left
92
+ }
93
+
94
+ dx, dy = moves[action]
95
+ nx, ny = x + dx, y + dy
96
+
97
+ reward = 0
98
+
99
+ # invalid move
100
+ if not (0 <= nx < GRID_SIZE and 0 <= ny < GRID_SIZE) or self.grid[nx, ny] == 1:
101
+ reward -= 2
102
+ nx, ny = x, y
103
  else:
104
+ new_dist = abs(nx - self.goal[0]) + abs(ny - self.goal[1])
105
 
106
+ if new_dist < old_dist:
107
+ reward += 1
108
+ elif new_dist > old_dist:
109
+ reward -= 1
110
+
111
+ if (nx, ny) not in self.visited:
112
+ reward += 0.3
113
+
114
+ self.agent = (nx, ny)
115
+ self.visited.add(self.agent)
116
  self.steps += 1
117
+
118
+ done = False
119
+
120
+ if self.agent == self.goal:
121
+ reward += 10
122
+ done = True
123
+
124
+ if self.steps >= STEP_LIMIT:
125
+ reward -= 5
126
+ done = True
127
+
128
+ self.total_score += reward
129
+
130
+ return self._get_obs(), reward, done, False, {}
131
+
132
+ # =========================
133
+ # Rendering
134
+ # =========================
135
+ def render(env):
136
+ grid = np.ones((GRID_SIZE, GRID_SIZE, 3)) # white
137
+
138
+ for i in range(GRID_SIZE):
139
+ for j in range(GRID_SIZE):
140
+ if env.grid[i, j] == 1:
141
+ grid[i, j] = [0.2, 0.2, 0.2] # obstacle
142
+
143
+ sx, sy = env.start
144
+ gx, gy = env.goal
145
+ ax, ay = env.agent
146
+
147
+ grid[sx, sy] = [0, 0, 1] # blue start
148
+ grid[gx, gy] = [0, 1, 0] # green goal
149
+ grid[ax, ay] = [1, 0, 0] # red agent
150
+
151
+ return grid
152
+
153
+ def scoreboard(env, last_reward):
154
+ ax, ay = env.agent
155
+ gx, gy = env.goal
156
+ dist = abs(ax - gx) + abs(ay - gy)
157
+
158
+ return f"""
159
+ **Score:** {env.total_score:.2f}
160
+ **Last Reward:** {last_reward:.2f}
161
+ **Steps:** {env.steps}
162
+ **Agent:** {env.agent}
163
+ **Goal:** {env.goal}
164
+ **Manhattan Distance:** {dist}
165
+ **Goal Reached:** {"✅" if env.agent == env.goal else "❌"}
166
+ """
167
+
168
+ # =========================
169
+ # App State
170
+ # =========================
171
  env = GridWorldEnv()
172
 
173
+ # =========================
174
+ # Controls
175
+ # =========================
176
+ def step_env(action):
177
+ obs, reward, done, _, _ = env.step(action)
178
+ img = render(env)
179
+ board = scoreboard(env, reward)
180
+ return img, board
181
+
182
+ def reset_env():
183
+ env.reset()
184
+ return render(env), scoreboard(env, 0)
185
+
186
+ # =========================
187
+ # Gradio UI
188
+ # =========================
189
+ with gr.Blocks() as demo:
190
+ gr.Markdown("# 🏭 GridWorld Warehouse Navigation")
191
+
 
 
 
192
  with gr.Row():
193
+ image = gr.Image(value=render(env), interactive=False)
194
+ board = gr.Markdown(scoreboard(env, 0))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
+ with gr.Row():
197
+ up = gr.Button("⬆️")
198
+ with gr.Row():
199
+ left = gr.Button("⬅️")
200
+ down = gr.Button("⬇️")
201
+ right = gr.Button("➡️")
202
+
203
+ reset = gr.Button("🔄 Reset")
204
+
205
+ up.click(lambda: step_env(0), outputs=[image, board])
206
+ right.click(lambda: step_env(1), outputs=[image, board])
207
+ down.click(lambda: step_env(2), outputs=[image, board])
208
+ left.click(lambda: step_env(3), outputs=[image, board])
209
+ reset.click(reset_env, outputs=[image, board])
210
+
211
+ # Keyboard support
212
+ demo.load(
213
+ None,
214
+ None,
215
+ None,
216
+ _js="""
217
+ () => {
218
+ document.addEventListener("keydown", (e) => {
219
+ const map = {
220
+ ArrowUp: 0,
221
+ ArrowRight: 1,
222
+ ArrowDown: 2,
223
+ ArrowLeft: 3
224
+ };
225
+ if (map[e.key] !== undefined) {
226
+ const buttons = document.querySelectorAll("button");
227
+ buttons[map[e.key]].click();
228
+ }
229
+ });
230
+ }
231
+ """
232
+ )
233
+
234
+ demo.launch()