byte-vortex commited on
Commit
d8024be
ยท
verified ยท
1 Parent(s): e3eff02

Deploy Myco from CI

Browse files
Files changed (1) hide show
  1. ui/gradio_app.py +279 -44
ui/gradio_app.py CHANGED
@@ -11,98 +11,333 @@ from game.engine import (
11
  from game.state import welcome_history
12
  from models.mushroom import Mushroom
13
  from ui.renderers import (
14
- dex_markdown,
15
  game_status_markdown, hero_markdown, home_forest_markdown,
16
  mushroom_card, play_hook_markdown, progress_markdown,
17
  world_map_markdown,
18
  )
19
 
20
  EMPTY_COLLECTION = None
21
- START_POSITION = (1, 1)
22
 
23
  HOME_MARKDOWN = """
24
  ## Welcome to the Myco forest
 
25
  Myco is a tiny AI-powered forest game. Open **๐ŸŽฎ Play** to search for mushrooms,
26
  visit **๐Ÿ“– MycoDex** to review your collection, and use **๐Ÿงญ Guide** for help.
 
 
 
27
  """
28
 
29
  GUIDE_MARKDOWN = """
30
  ## How to play
31
- 1. Use **WASD** or Arrows to move.
32
- 2. **Q**=Study, **E**=Pick, **C**=Collect.
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  """
34
 
 
35
  def mushroom_card_from_state(current):
36
- if current is None: return mushroom_card(None)
37
- return mushroom_card(Mushroom.from_dict(current), current.get("clue"), current.get("secret"), current)
38
-
39
- def forest_scene(current, collection, position):
40
- px, py = _safe_pos(position)
41
- html = f"""
42
- <div style="background: #111122; padding: 20px; border-radius: 12px; border: 2px solid #2e2e4f; text-align: center;">
43
- <div style="color: #8a8aaf; font-weight: bold; margin-bottom: 12px; font-family: monospace;">๐ŸŒฒ MYCO FOREST VIEWPORT ๐ŸŒฒ</div>
44
- <div style="display: grid; grid-template-columns: repeat(3, 85px); gap: 10px; justify-content: center;">
45
- {''.join([f'<div style="width: 85px; height: 85px; background: #1b263b; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 32px; {"border: 3px solid #00b4d8;" if (i%3 == px and i//3 == py) else ""}">{ "๐Ÿง™โ€โ™‚๏ธ" if (i%3 == px and i//3 == py) else "๐ŸŒฒ" }</div>' for i in range(9)])}
46
- </div>
47
- </div>
48
- """
49
- return html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  def _safe_pos(position):
52
- if position is None: return START_POSITION
 
53
  x, y = position
54
  return (max(0, min(2, int(x))), max(0, min(2, int(y))))
55
 
 
56
  def build_app():
57
  with gr.Blocks(title="Myco โ€” AI Forest Game", theme=gr.themes.Soft()) as demo:
58
  gr.HTML("""
59
  <style>
60
  .gradio-container { background: #1a1a2e !important; }
61
- * { transition: none !important; }
62
- @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
63
- .emoji-float { display: inline-block; animation: float 2s infinite ease-in-out; font-size: 3rem; margin: 0 10px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  </style>
65
  """)
66
 
 
67
  current_mushroom = gr.State(None)
68
- collection = gr.State([])
69
- player_position = gr.State(START_POSITION)
70
 
 
71
  gr.HTML(hero_markdown())
72
 
73
  with gr.Tabs(selected="home") as tabs:
 
 
74
  with gr.Tab("๐Ÿ  Home", id="home"):
75
  gr.Markdown(HOME_MARKDOWN)
76
- start_play_button = gr.Button("๐Ÿ„ Enter the Forest", variant="primary")
77
-
 
 
 
 
 
78
  with gr.Tab("๐ŸŽฎ Play", id="play"):
79
- # ANIMATED EMOJIS AT THE TOP
80
- gr.HTML("""
81
- <div style="text-align: center; padding: 20px;">
82
- <span class="emoji-float">๐Ÿ„</span>
83
- <span class="emoji-float">โœจ</span>
84
- <span class="emoji-float">๐ŸŒฒ</span>
85
- </div>
86
- """)
87
-
88
  with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  with gr.Column(scale=2):
90
  gr.Markdown("### ๐Ÿ„ Current Discovery")
91
  discovery = gr.Markdown(mushroom_card(None))
92
  with gr.Row():
93
- study_btn = gr.Button("๐Ÿ” Study")
94
- collect_btn = gr.Button("๐Ÿ“– Collect")
 
 
 
 
95
  with gr.Column(scale=3):
96
- chat = gr.Chatbot(label="Myco", height=300)
97
- prompt = gr.Textbox(placeholder="Ask Myco...")
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
 
99
  with gr.Tab("๐Ÿ“– MycoDex"):
100
- gr.Markdown("Your collected spores and mushrooms.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- start_play_button.click(lambda: gr.update(selected="play"), outputs=[tabs])
103
  demo.queue()
104
  return demo
105
-
106
- if __name__ == "__main__":
107
- build_app().launch()
108
 
 
11
  from game.state import welcome_history
12
  from models.mushroom import Mushroom
13
  from ui.renderers import (
14
+ dex_markdown, forest_scene, game_intro_markdown,
15
  game_status_markdown, hero_markdown, home_forest_markdown,
16
  mushroom_card, play_hook_markdown, progress_markdown,
17
  world_map_markdown,
18
  )
19
 
20
  EMPTY_COLLECTION = None
21
+ START_POSITION = (1, 1)
22
 
23
  HOME_MARKDOWN = """
24
  ## Welcome to the Myco forest
25
+
26
  Myco is a tiny AI-powered forest game. Open **๐ŸŽฎ Play** to search for mushrooms,
27
  visit **๐Ÿ“– MycoDex** to review your collection, and use **๐Ÿงญ Guide** for help.
28
+
29
+ **Goal:** solve one mystery โ€” why does Myco remember a forest that vanished before it was born?
30
+ Follow clues until the Impossible Mushroom blooms inside the MycoDex.
31
  """
32
 
33
  GUIDE_MARKDOWN = """
34
  ## How to play
35
+
36
+ 1. Go to **๐ŸŽฎ Play** and click **๐ŸŽฎ Search Clearing** to discover a mushroom.
37
+ 2. **Myco (the AI) reacts** โ€” read what it senses about the mushroom.
38
+ 3. Choose your action:
39
+ - **๐Ÿ” Study** โ€” Myco examines the mushroom and updates its magic field.
40
+ - **๐Ÿ„ Pick** โ€” grab it as a game item. Poisonous picks = Game Over!
41
+ - **๐Ÿ“– Collect** โ€” add it to the MycoDex for safe keeping and score.
42
+ - **๐ŸŒŒ Follow Whisper** โ€” reveal mystery fragments. Risky on unstudied mushrooms!
43
+ - **๐Ÿฝ๏ธ Eat?** โ€” Myco will stop you every time (for good reason).
44
+ 4. **Ask Myco** anything in the chat โ€” it knows the forest, the mystery, and the mushrooms.
45
+ 5. Use **โฌ†๏ธโฌ‡๏ธโฌ…๏ธโžก๏ธ** to move through the 3ร—3 forest grid.
46
+
47
+ **Scoring:** Common = 10 spores ยท Rare = 35 ยท Legendary = 100 ยท Poison = โˆ’25 + Game Over
48
+
49
+ **Poisonous mushrooms:** Ghost Gill ยท Pepper Pixie ยท Ruby Knuckle ยท Clockwork Chanterelle
50
  """
51
 
52
+
53
  def mushroom_card_from_state(current):
54
+ if current is None:
55
+ return mushroom_card(None)
56
+ return mushroom_card(
57
+ Mushroom.from_dict(current),
58
+ current.get("clue"),
59
+ current.get("secret"),
60
+ current,
61
+ )
62
+
63
+
64
+ def search_forest(collection, position):
65
+ mushroom, current, history = discover_mushroom(collection)
66
+ pos = _safe_pos(position)
67
+ return (
68
+ forest_scene(current, collection, pos),
69
+ game_status_markdown(current, collection, pos),
70
+ mushroom_card_from_state(current),
71
+ current,
72
+ history,
73
+ dex_markdown(collection),
74
+ progress_markdown(collection),
75
+ )
76
+
77
+
78
+ def ask_companion(message, history, current, collection, position):
79
+ return myco_reply(message, history, current, collection, position)
80
+
81
+
82
+ def collect_discovery(current, collection, history, position):
83
+ updated_coll, updated_hist = collect_current(current, collection, history)
84
+ pos = _safe_pos(position)
85
+ return (
86
+ updated_coll,
87
+ forest_scene(current, updated_coll, pos),
88
+ game_status_markdown(current, updated_coll, pos),
89
+ dex_markdown(updated_coll),
90
+ world_map_markdown(updated_coll),
91
+ updated_hist,
92
+ progress_markdown(updated_coll),
93
+ )
94
+
95
+
96
+ def pick_discovery(current, collection, history, position):
97
+ updated_coll, updated_current, updated_hist = pick_current(current, collection, history)
98
+ pos = _safe_pos(position)
99
+ return (
100
+ updated_coll,
101
+ updated_current,
102
+ forest_scene(updated_current, updated_coll, pos),
103
+ game_status_markdown(updated_current, updated_coll, pos),
104
+ mushroom_card_from_state(updated_current),
105
+ dex_markdown(updated_coll),
106
+ world_map_markdown(updated_coll),
107
+ updated_hist,
108
+ progress_markdown(updated_coll),
109
+ )
110
+
111
+
112
+ def follow_story_whisper(current, collection, history, position):
113
+ updated_current, updated_hist = follow_whisper(current, collection, history)
114
+ pos = _safe_pos(position)
115
+ return (
116
+ updated_current,
117
+ forest_scene(updated_current, collection, pos),
118
+ game_status_markdown(updated_current, collection, pos),
119
+ mushroom_card_from_state(updated_current),
120
+ updated_hist,
121
+ )
122
+
123
+
124
+ def study_discovery(current, history, collection, position):
125
+ updated_current, updated_hist = study_current(current, history)
126
+ pos = _safe_pos(position)
127
+ return (
128
+ updated_current,
129
+ forest_scene(updated_current, collection, pos),
130
+ game_status_markdown(updated_current, collection, pos),
131
+ mushroom_card_from_state(updated_current),
132
+ updated_hist,
133
+ )
134
+
135
+
136
+ def move_player(position, direction, current, collection):
137
+ x, y = _safe_pos(position)
138
+ if direction == "north": y -= 1
139
+ elif direction == "south": y += 1
140
+ elif direction == "west": x -= 1
141
+ elif direction == "east": x += 1
142
+ pos = _safe_pos((x, y))
143
+ return pos, forest_scene(current, collection, pos), game_status_markdown(current, collection, pos)
144
+
145
+
146
+ def block_unsafe_eating(current, history):
147
+ return eat_current(current, history)
148
+
149
+
150
+ def open_play_tab():
151
+ return gr.update(selected="play")
152
+
153
 
154
  def _safe_pos(position):
155
+ if position is None:
156
+ return START_POSITION
157
  x, y = position
158
  return (max(0, min(2, int(x))), max(0, min(2, int(y))))
159
 
160
+
161
  def build_app():
162
  with gr.Blocks(title="Myco โ€” AI Forest Game", theme=gr.themes.Soft()) as demo:
163
  gr.HTML("""
164
  <style>
165
  .gradio-container { background: #1a1a2e !important; }
166
+ .gr-html, iframe, .html-container {
167
+ transition: none !important;
168
+ animation: none !important;
169
+ }
170
+ /* Prevent white flash on component updates */
171
+ * {
172
+ transition: opacity 0.15s ease !important;
173
+ }
174
+ .pending, .generating {
175
+ opacity: 0.85 !important;
176
+ background: transparent !important;
177
+ }
178
+ /* Hide the white overlay Gradio adds during updates */
179
+ .progress-level, .progress-level-inner {
180
+ display: none !important;
181
+ }
182
  </style>
183
  """)
184
 
185
+ # โ”€โ”€ State โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
186
  current_mushroom = gr.State(None)
187
+ collection = gr.State([])
188
+ player_position = gr.State(START_POSITION)
189
 
190
+ # โ”€โ”€ Hero โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
191
  gr.HTML(hero_markdown())
192
 
193
  with gr.Tabs(selected="home") as tabs:
194
+
195
+ # โ”€โ”€ HOME โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
196
  with gr.Tab("๐Ÿ  Home", id="home"):
197
  gr.Markdown(HOME_MARKDOWN)
198
+ gr.HTML(home_forest_markdown())
199
+ start_play_button = gr.Button(
200
+ "๐Ÿ„ Enter the Forest", variant="primary", size="lg"
201
+ )
202
+ gr.Markdown(f"*{hf_companion_status()}*")
203
+
204
+ # โ”€โ”€ PLAY โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
205
  with gr.Tab("๐ŸŽฎ Play", id="play"):
206
+ gr.HTML(game_intro_markdown())
207
+
208
+ # Forest scene + controls
209
+ scene = gr.HTML(
210
+ forest_scene(None, EMPTY_COLLECTION, START_POSITION),
211
+ elem_id="forest-scene",
212
+ )
213
+
 
214
  with gr.Row():
215
+ with gr.Column(scale=1):
216
+ search_button = gr.Button("๐ŸŽฎ Search Clearing", variant="primary", size="lg")
217
+ with gr.Column(scale=1):
218
+ gr.Markdown("**Move Myco**")
219
+ with gr.Row():
220
+ gr.HTML("<div></div>")
221
+ north_button = gr.Button("โฌ†๏ธ", size="sm")
222
+ gr.HTML("<div></div>")
223
+ with gr.Row():
224
+ west_button = gr.Button("โฌ…๏ธ", size="sm")
225
+ pick_button = gr.Button("๐Ÿ„ Pick", variant="primary", size="sm")
226
+ east_button = gr.Button("โžก๏ธ", size="sm")
227
+ with gr.Row():
228
+ gr.HTML("<div></div>")
229
+ south_button = gr.Button("โฌ‡๏ธ", size="sm")
230
+ gr.HTML("<div></div>")
231
+
232
+ # Quest log
233
+ status = gr.Markdown(
234
+ game_status_markdown(None, EMPTY_COLLECTION, START_POSITION)
235
+ )
236
+
237
+ # Discovery card + action buttons + chat
238
+ with gr.Row(equal_height=True):
239
  with gr.Column(scale=2):
240
  gr.Markdown("### ๐Ÿ„ Current Discovery")
241
  discovery = gr.Markdown(mushroom_card(None))
242
  with gr.Row():
243
+ study_button = gr.Button("๐Ÿ” Study", variant="secondary")
244
+ collect_button = gr.Button("๐Ÿ“– Collect", variant="secondary")
245
+ with gr.Row():
246
+ whisper_button = gr.Button("๐ŸŒŒ Follow Whisper", variant="secondary")
247
+ eat_button = gr.Button("๐Ÿฝ๏ธ Eat?", variant="stop")
248
+
249
  with gr.Column(scale=3):
250
+ gr.Markdown("### ๐Ÿ’ฌ Myco Chat")
251
+ chat = gr.Chatbot(
252
+ value=cast(Any, welcome_history()),
253
+ label="Myco",
254
+ height=400,
255
+ show_label=False,
256
+ )
257
+ with gr.Row():
258
+ prompt = gr.Textbox(
259
+ placeholder="Ask Myco anything about the forest...",
260
+ show_label=False,
261
+ scale=4,
262
+ lines=1,
263
+ )
264
+ ask_button = gr.Button("Ask ๐Ÿ„", variant="primary", scale=1)
265
 
266
+ # โ”€โ”€ MYCODEX โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
267
  with gr.Tab("๐Ÿ“– MycoDex"):
268
+ with gr.Row():
269
+ progress = gr.Markdown(progress_markdown(EMPTY_COLLECTION))
270
+ dex = gr.Markdown(dex_markdown(EMPTY_COLLECTION))
271
+
272
+ # โ”€โ”€ WORLD MAP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
273
+ with gr.Tab("๐Ÿ—บ๏ธ World Map"):
274
+ world_map = gr.HTML(world_map_markdown(EMPTY_COLLECTION))
275
+
276
+ # โ”€โ”€ GUIDE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
277
+ with gr.Tab("๐Ÿงญ Guide"):
278
+ gr.Markdown(GUIDE_MARKDOWN)
279
+
280
+ # โ”€โ”€ Event wiring โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
281
+ start_play_button.click(open_play_tab, outputs=[tabs])
282
+
283
+ search_button.click(
284
+ search_forest,
285
+ inputs=[collection, player_position],
286
+ outputs=[scene, status, discovery, current_mushroom, chat, dex, progress],
287
+ show_progress="hidden",
288
+ )
289
+
290
+ ask_button.click(
291
+ ask_companion,
292
+ inputs=[prompt, chat, current_mushroom, collection, player_position],
293
+ outputs=[prompt, chat],
294
+ show_progress="hidden",
295
+ )
296
+ prompt.submit(
297
+ ask_companion,
298
+ inputs=[prompt, chat, current_mushroom, collection, player_position],
299
+ outputs=[prompt, chat],
300
+ show_progress="hidden",
301
+ )
302
+
303
+ for direction, button in [
304
+ ("north", north_button), ("south", south_button),
305
+ ("west", west_button), ("east", east_button),
306
+ ]:
307
+ button.click(
308
+ move_player,
309
+ inputs=[player_position, gr.State(direction), current_mushroom, collection],
310
+ outputs=[player_position, scene, status],
311
+ show_progress="hidden",
312
+ )
313
+
314
+ study_button.click(
315
+ study_discovery,
316
+ inputs=[current_mushroom, chat, collection, player_position],
317
+ outputs=[current_mushroom, scene, status, discovery, chat],
318
+ )
319
+ collect_button.click(
320
+ collect_discovery,
321
+ inputs=[current_mushroom, collection, chat, player_position],
322
+ outputs=[collection, scene, status, dex, world_map, chat, progress],
323
+ )
324
+ pick_button.click(
325
+ pick_discovery,
326
+ inputs=[current_mushroom, collection, chat, player_position],
327
+ outputs=[collection, current_mushroom, scene, status, discovery, dex, world_map, chat, progress],
328
+ show_progress="hidden",
329
+ )
330
+ whisper_button.click(
331
+ follow_story_whisper,
332
+ inputs=[current_mushroom, collection, chat, player_position],
333
+ outputs=[current_mushroom, scene, status, discovery, chat],
334
+ )
335
+ eat_button.click(
336
+ block_unsafe_eating,
337
+ inputs=[current_mushroom, chat],
338
+ outputs=[chat],
339
+ )
340
 
 
341
  demo.queue()
342
  return demo
 
 
 
343