sunbal7 commited on
Commit
ef25b51
ยท
verified ยท
1 Parent(s): 9d8b484

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +404 -573
app.py CHANGED
@@ -1,49 +1,44 @@
1
- # app.py - Learn Python Through Interactive Games
2
  import streamlit as st
3
  import os
4
  import time
5
  import random
6
- import numpy as np
7
- import pandas as pd
8
- import plotly.express as px
9
- import plotly.graph_objects as go
10
- from gtts import gTTS
11
  import base64
12
  import json
13
  import requests
14
- from io import BytesIO
15
- import pygame
16
- import sys
17
- import math
18
- from PIL import Image, ImageDraw
19
  import io
 
 
 
 
 
20
 
21
  # Configure Streamlit page
22
  st.set_page_config(
23
- page_title="StoryCoder - Learn Python Through Games",
24
- page_icon="๐ŸŽฎ",
25
  layout="wide",
26
  initial_sidebar_state="expanded"
27
  )
28
 
29
- # Custom CSS with purple/grey color scheme
30
  st.markdown("""
31
  <style>
32
- @import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Fredoka+One&display=swap');
33
 
34
  :root {
35
- --primary: #7E57C2;
36
- --secondary: #B39DDB;
37
- --accent: #D1C4E9;
38
- --dark: #4527A0;
39
- --light: #F3E5F5;
40
- --neutral: #E0E0E0;
41
  }
42
 
43
  body {
44
- background: linear-gradient(135deg, var(--light) 0%, #EDE7F6 100%);
45
  font-family: 'Comic Neue', cursive;
46
- color: #333;
47
  }
48
 
49
  .stApp {
@@ -54,7 +49,7 @@ st.markdown("""
54
  background-color: white;
55
  border-radius: 20px;
56
  padding: 25px;
57
- box-shadow: 0 8px 16px rgba(69, 39, 160, 0.15);
58
  border: 3px solid var(--accent);
59
  margin-bottom: 25px;
60
  }
@@ -62,40 +57,37 @@ st.markdown("""
62
  .header {
63
  color: var(--dark);
64
  text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
65
- font-family: 'Fredoka One', cursive;
66
  }
67
 
68
  .concept-card {
69
- background: linear-gradient(145deg, #ffffff, #f5f5f5);
70
  border-radius: 15px;
71
- padding: 20px;
72
- margin: 15px 0;
73
- border-left: 5px solid var(--primary);
74
- box-shadow: 0 4px 12px rgba(126, 87, 194, 0.1);
75
  }
76
 
77
  .stButton>button {
78
  background: linear-gradient(45deg, var(--primary), var(--secondary));
79
  color: white;
80
- border-radius: 20px;
81
- padding: 12px 28px;
82
  font-weight: bold;
83
  font-size: 18px;
84
  border: none;
85
  transition: all 0.3s;
86
- font-family: 'Fredoka One', cursive;
87
  }
88
 
89
  .stButton>button:hover {
90
  transform: scale(1.05);
91
- box-shadow: 0 6px 12px rgba(126, 87, 194, 0.2);
92
  }
93
 
94
  .stTextInput>div>div>input {
95
- border-radius: 15px;
96
- padding: 14px;
97
  border: 2px solid var(--accent);
98
- font-size: 16px;
99
  }
100
 
101
  .tabs {
@@ -106,24 +98,17 @@ st.markdown("""
106
  }
107
 
108
  .tab {
109
- padding: 12px 24px;
110
  background-color: var(--accent);
111
- border-radius: 15px;
112
  cursor: pointer;
113
  font-weight: bold;
114
  white-space: nowrap;
115
- font-family: 'Fredoka One', cursive;
116
- transition: all 0.3s;
117
- }
118
-
119
- .tab:hover {
120
- background-color: var(--secondary);
121
  }
122
 
123
  .tab.active {
124
- background-color: var(--primary);
125
  color: white;
126
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
127
  }
128
 
129
  @media (max-width: 768px) {
@@ -133,24 +118,21 @@ st.markdown("""
133
  }
134
 
135
  .game-container {
136
- background-color: #f9f5ff;
137
- border-radius: 20px;
138
- padding: 25px;
139
- box-shadow: 0 8px 32px rgba(126, 87, 194, 0.15);
140
- margin-bottom: 30px;
141
  position: relative;
142
  overflow: hidden;
143
- border: 3px solid var(--accent);
144
  }
145
 
146
- .game-canvas {
147
- border-radius: 15px;
148
  overflow: hidden;
149
  margin: 0 auto;
150
  display: block;
151
  max-width: 100%;
152
- background: #fff;
153
- box-shadow: 0 6px 16px rgba(0,0,0,0.1);
154
  }
155
 
156
  .character {
@@ -159,54 +141,19 @@ st.markdown("""
159
  margin: 10px 0;
160
  }
161
 
162
- .audio-player {
163
- width: 100%;
164
- margin: 20px 0;
165
- border-radius: 20px;
166
- background: #f0f8ff;
167
- padding: 15px;
168
- }
169
-
170
- .game-preview {
171
- max-width: 100%;
172
  border-radius: 15px;
173
- box-shadow: 0 8px 24px rgba(0,0,0,0.15);
174
- border: 3px solid var(--accent);
175
- }
176
-
177
- .explanation-box {
178
- background: linear-gradient(135deg, #f3e5f5, #e8daf0);
179
- border-radius: 20px;
180
- padding: 25px;
181
- margin: 25px 0;
182
- border-left: 5px solid var(--primary);
183
- box-shadow: 0 6px 20px rgba(0,0,0,0.08);
184
- }
185
-
186
- .explanation-header {
187
- color: var(--dark);
188
- display: flex;
189
- align-items: center;
190
- gap: 15px;
191
- margin-bottom: 15px;
192
- }
193
-
194
- .code-block {
195
- background: #2d2d2d;
196
- color: #f8f8f2;
197
- border-radius: 10px;
198
  padding: 15px;
199
- font-family: 'Courier New', monospace;
200
  margin: 15px 0;
201
- overflow-x: auto;
202
  }
203
 
204
- .concept-highlight {
205
- background: linear-gradient(120deg, #e0d6f0, #d1c4e9);
206
- padding: 5px 10px;
207
- border-radius: 8px;
208
- font-weight: bold;
209
- color: var(--dark);
210
  }
211
  </style>
212
  """, unsafe_allow_html=True)
@@ -218,474 +165,387 @@ CONCEPTS = {
218
  "emoji": "๐Ÿ”„",
219
  "description": "Loops repeat actions multiple times",
220
  "example": "for i in range(5):\n print('Hello!')",
221
- "color": "#7E57C2",
222
- "explanation": "A loop is like a magic spell that makes something happen again and again. In programming, we use loops when we want to repeat an action multiple times without writing the same code over and over."
223
  },
224
  "conditional": {
225
  "name": "Conditional",
226
  "emoji": "โ“",
227
  "description": "Conditionals make decisions in code",
228
  "example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
229
- "color": "#5E35B1",
230
- "explanation": "Conditionals are like crossroads in a story where you choose which path to take. In programming, we use 'if' statements to make decisions based on certain conditions, just like choosing what to wear based on the weather."
231
  },
232
  "function": {
233
  "name": "Function",
234
  "emoji": "โœจ",
235
  "description": "Functions are reusable blocks of code",
236
  "example": "def greet(name):\n print(f'Hello {name}!')",
237
- "color": "#4527A0",
238
- "explanation": "Functions are like magic spells you can create and reuse whenever you need them. They help us organize code into reusable blocks so we don't have to write the same thing multiple times."
239
  },
240
  "variable": {
241
  "name": "Variable",
242
  "emoji": "๐Ÿ“ฆ",
243
  "description": "Variables store information",
244
  "example": "score = 10\nplayer = 'Alex'",
245
- "color": "#7B1FA2",
246
- "explanation": "Variables are like labeled boxes where you can store information. They help us remember values and use them later in our code, just like remembering a character's name in a story."
247
  },
248
  "list": {
249
  "name": "List",
250
  "emoji": "๐Ÿ“",
251
  "description": "Lists store collections of items",
252
  "example": "fruits = ['apple', 'banana', 'orange']",
253
- "color": "#6A1B9A",
254
- "explanation": "Lists are like magical scrolls that can hold multiple items. In programming, we use lists to store collections of related things, like a wizard's inventory of potions."
255
  }
256
  }
257
 
258
- # Game templates
259
- GAME_TEMPLATES = {
260
  "loop": {
261
- "name": "Loop Adventure",
262
- "description": "Help the character repeat actions multiple times to achieve a goal",
263
- "color": "#7E57C2",
264
- "instructions": "Press the action button multiple times to complete the task!"
265
  },
266
  "conditional": {
267
- "name": "Decision Quest",
268
- "description": "Make choices based on conditions to progress through the story",
269
- "color": "#5E35B1",
270
- "instructions": "Choose the correct path based on the conditions you see!"
271
  },
272
  "function": {
273
- "name": "Magic Function",
274
- "description": "Create and use reusable actions to solve challenges",
275
- "color": "#4527A0",
276
- "instructions": "Create your function then use it whenever needed!"
277
  }
278
  }
279
 
280
- # Game assets
281
- GAME_ASSETS = {
282
- "player": "๐Ÿ‘ฆ",
283
- "obstacle": "๐ŸŒต",
284
- "goal": "๐Ÿ†",
285
- "enemy": "๐Ÿ‰",
286
- "item": "โญ",
287
- "path": "๐ŸŸฉ",
288
- "wall": "โฌ›"
289
- }
 
 
 
 
 
290
 
 
291
  def analyze_story(story):
292
- """Analyze story and identify programming concepts"""
293
  story_lower = story.lower()
294
  detected_concepts = []
295
 
296
  # Check for loops
297
- if any(word in story_lower for word in ["times", "repeat", "again", "multiple"]):
298
  detected_concepts.append("loop")
299
 
300
  # Check for conditionals
301
- if any(word in story_lower for word in ["if", "when", "unless", "whether"]):
302
  detected_concepts.append("conditional")
303
 
304
  # Check for functions
305
- if any(word in story_lower for word in ["make", "create", "do", "perform", "cast"]):
306
  detected_concepts.append("function")
307
 
308
  # Check for variables
309
- if any(word in story_lower for word in ["is", "has", "set to", "value"]):
310
  detected_concepts.append("variable")
311
 
312
  # Check for lists
313
- if any(word in story_lower for word in ["and", "many", "several", "collection", "items"]):
314
  detected_concepts.append("list")
315
 
316
  return list(set(detected_concepts))
317
 
318
- def extract_count_from_story(story):
319
- """Extract a number from the story to use in games"""
320
- for word in story.split():
321
- if word.isdigit():
322
- return min(int(word), 10)
323
- return 3 # Default value
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
- def create_game_preview(concept):
326
- """Create a preview image for the game"""
327
- width, height = 400, 300
328
- img = Image.new('RGB', (width, height), color=(243, 229, 245))
329
- draw = ImageDraw.Draw(img)
330
-
331
- # Draw game elements based on concept
332
- if concept == "loop":
333
- # Draw a path with repeated obstacles
334
- for i in range(5):
335
- x = 80 + i * 60
336
- y = 150
337
- draw.rectangle([x, y, x+30, y+30], fill=(126, 87, 194))
338
- draw.text((x+10, y+5), GAME_ASSETS["obstacle"], fill=(255, 255, 255), font_size=20)
339
-
340
- # Draw player and goal
341
- draw.text((50, 150), GAME_ASSETS["player"], fill=(69, 39, 160), font_size=30)
342
- draw.text((350, 150), GAME_ASSETS["goal"], fill=(255, 193, 7), font_size=30)
343
-
344
- # Draw title
345
- draw.text((100, 50), "Loop Adventure", fill=(69, 39, 160), font_size=25)
346
 
347
- elif concept == "conditional":
348
- # Draw two paths
349
- draw.rectangle([100, 100, 180, 180], fill=(93, 64, 155))
350
- draw.rectangle([220, 100, 300, 180], fill=(93, 64, 155))
351
 
352
- # Draw weather symbols
353
- draw.text((130, 110), "โ˜€๏ธ", fill=(255, 255, 0), font_size=30)
354
- draw.text((250, 110), "๐ŸŒง๏ธ", fill=(100, 181, 246), font_size=30)
355
 
356
- # Draw items
357
- draw.text((130, 150), "๐Ÿ˜Ž", fill=(255, 255, 255), font_size=30)
358
- draw.text((250, 150), "โ˜‚๏ธ", fill=(255, 255, 255), font_size=30)
359
 
360
- # Draw title
361
- draw.text((120, 50), "Decision Quest", fill=(69, 39, 160), font_size=25)
362
-
363
- else: # function
364
- # Draw magic wand and effects
365
- draw.text((100, 150), "๐Ÿช„", fill=(126, 87, 194), font_size=40)
366
 
367
- # Draw magic effects
368
- for i in range(3):
369
- x = 180 + i * 60
370
- y = 150
371
- draw.ellipse([x, y, x+40, y+40], fill=(179, 157, 219))
372
- draw.text((x+10, y+5), "โœจ", fill=(255, 255, 255), font_size=30)
 
373
 
374
- # Draw title
375
- draw.text((130, 50), "Magic Function", fill=(69, 39, 160), font_size=25)
376
-
377
- # Convert to bytes
378
- buf = io.BytesIO()
379
- img.save(buf, format='PNG')
380
- buf.seek(0)
381
- return buf
382
-
383
- def text_to_speech(text, filename="explanation.wav"):
384
- """Convert text to speech using gTTS"""
385
- try:
386
- tts = gTTS(text=text, lang='en')
387
- tts.save(filename)
388
- return filename
389
  except Exception as e:
390
- st.error(f"Audio creation error: {str(e)}")
391
- return None
392
-
393
- def autoplay_audio(file_path):
394
- """Autoplay audio in Streamlit"""
395
- with open(file_path, "rb") as f:
396
- data = f.read()
397
- b64 = base64.b64encode(data).decode()
398
- md = f"""
399
- <audio autoplay>
400
- <source src="data:audio/wav;base64,{b64}" type="audio/wav">
401
- </audio>
402
- """
403
- st.markdown(md, unsafe_allow_html=True)
404
 
405
- def generate_concept_explanation(story, concept):
406
- """Generate a detailed explanation of the programming concept with examples"""
407
- concept_info = CONCEPTS.get(concept, {})
408
- count = extract_count_from_story(story)
 
409
 
410
- explanation = f"""
411
- Let me explain what's happening in your story and how it relates to programming!
412
-
413
- Your story: "{story[:100]}..."
414
-
415
- This story demonstrates the programming concept of: <span class="concept-highlight">{concept_info.get('name', 'Programming')}</span>
416
-
417
- {concept_info.get('explanation', 'This is a fundamental programming concept.')}
418
-
419
- In your game:
420
- - You'll see how {concept_info.get('name', 'this concept')} works in action
421
- - The game is designed around the idea from your story
422
-
423
- How it works in code:
424
- We use {concept_info.get('name', 'this concept')} like this:
425
- """
426
-
427
- # Add code example
428
- explanation += f"""<div class="code-block">{concept_info.get('example', 'Example code will appear here')}</div>"""
429
-
430
- explanation += f"""
431
- In real life, you might use this concept when:
432
- - Creating games with repeating actions (loops)
433
- - Making decisions in apps (conditionals)
434
- - Building reusable components (functions)
435
-
436
- As you play the game, think about how the concept is being used!
437
- """
 
 
 
 
 
 
 
 
 
 
438
 
439
- return explanation
 
440
 
441
- def create_loop_game(story):
442
- """Create a loop-based game"""
443
- actions_needed = extract_count_from_story(story)
444
-
445
- # Initialize session state for game
446
- if 'loop_count' not in st.session_state:
447
- st.session_state.loop_count = 0
448
- st.session_state.game_complete = False
449
-
450
- # Game layout
451
- st.subheader("๐Ÿ”„ Loop Adventure Game")
452
- st.write(f"**Story:** {story[:100]}{'...' if len(story) > 100 else ''}")
453
- st.write("**Instructions:** Press the action button repeatedly to complete the task!")
454
-
455
- # Create game grid
456
- grid_html = "<div style='display: flex; justify-content: center; margin: 20px 0;'>"
457
-
458
- # Player position
459
- player_pos = min(st.session_state.loop_count, actions_needed)
460
- player_offset = player_pos * (100 // actions_needed)
461
-
462
- # Draw game path
463
- grid_html += f"<div style='position: relative; width: 400px; height: 100px; background: #EDE7F6; border-radius: 10px; border: 2px solid {CONCEPTS['loop']['color']}'>"
464
-
465
- # Draw path
466
- grid_html += f"<div style='position: absolute; top: 40px; left: 0; width: 100%; height: 20px; background: {CONCEPTS['loop']['color']}; border-radius: 10px;'></div>"
467
-
468
- # Draw obstacles
469
- for i in range(actions_needed):
470
- grid_html += f"<div style='position: absolute; top: 30px; left: {i * (400 // actions_needed)}px; font-size: 24px;'>๐ŸŒต</div>"
471
-
472
- # Draw player
473
- grid_html += f"<div style='position: absolute; top: 20px; left: {player_offset}px; font-size: 36px;'>๐Ÿ‘ฆ</div>"
474
-
475
- # Draw goal
476
- grid_html += f"<div style='position: absolute; top: 20px; left: 380px; font-size: 36px;'>๐Ÿ†</div>"
477
-
478
- grid_html += "</div></div>"
479
-
480
- st.markdown(grid_html, unsafe_allow_html=True)
481
 
482
- # Action button
483
- if st.session_state.game_complete:
484
- st.success("๐ŸŽ‰ You completed the game! Great job using loops!")
485
- if st.button("Play Again", key="loop_restart"):
486
- st.session_state.loop_count = 0
487
- st.session_state.game_complete = False
488
- st.rerun()
489
- else:
490
- if st.button(f"Perform Action ({st.session_state.loop_count}/{actions_needed})", key="loop_action"):
491
- st.session_state.loop_count += 1
492
- if st.session_state.loop_count >= actions_needed:
493
- st.session_state.game_complete = True
494
- st.rerun()
495
 
496
- def create_conditional_game(story):
497
- """Create a conditional-based game"""
498
- # Initialize session state for game
499
- if 'conditional_choice' not in st.session_state:
500
- st.session_state.conditional_choice = None
501
- st.session_state.game_complete = False
502
- st.session_state.weather = random.choice(["sunny", "rainy"])
503
-
504
- # Game layout
505
- st.subheader("โ“ Decision Quest Game")
506
- st.write(f"**Story:** {story[:100]}{'...' if len(story) > 100 else ''}")
507
- st.write("**Instructions:** Choose the correct item based on the weather condition!")
508
-
509
- # Display weather
510
- col1, col2 = st.columns([1, 2])
511
- with col1:
512
- st.subheader("Current Weather:")
513
- if st.session_state.weather == "sunny":
514
- st.markdown("<div style='font-size: 48px; text-align: center;'>โ˜€๏ธ</div>", unsafe_allow_html=True)
515
- st.write("It's a sunny day!")
516
- else:
517
- st.markdown("<div style='font-size: 48px; text-align: center;'>๐ŸŒง๏ธ</div>", unsafe_allow_html=True)
518
- st.write("It's a rainy day!")
519
-
520
- with col2:
521
- st.subheader("Choose Your Item:")
522
 
523
- if not st.session_state.game_complete:
524
- if st.button("๐Ÿ˜Ž Sunglasses", key="sunglasses", use_container_width=True):
525
- st.session_state.conditional_choice = "sunglasses"
526
- st.session_state.game_complete = True
527
- st.rerun()
528
-
529
- if st.button("โ˜‚๏ธ Umbrella", key="umbrella", use_container_width=True):
530
- st.session_state.conditional_choice = "umbrella"
531
- st.session_state.game_complete = True
532
- st.rerun()
533
- else:
534
- if st.session_state.weather == "sunny" and st.session_state.conditional_choice == "sunglasses":
535
- st.success("๐ŸŽ‰ Correct choice! You wore sunglasses on a sunny day!")
536
- elif st.session_state.weather == "rainy" and st.session_state.conditional_choice == "umbrella":
537
- st.success("๐ŸŽ‰ Correct choice! You took an umbrella on a rainy day!")
538
- else:
539
- st.error("โŒ Oops! That wasn't the right choice for this weather.")
540
-
541
- if st.button("Play Again", key="conditional_restart"):
542
- st.session_state.conditional_choice = None
543
- st.session_state.game_complete = False
544
- st.session_state.weather = random.choice(["sunny", "rainy"])
545
- st.rerun()
546
-
547
- def create_function_game(story):
548
- """Create a function-based game"""
549
- actions_needed = extract_count_from_story(story)
550
-
551
- # Initialize session state for game
552
- if 'function_created' not in st.session_state:
553
- st.session_state.function_created = False
554
- st.session_state.function_used = 0
555
- st.session_state.game_complete = False
556
-
557
- # Game layout
558
- st.subheader("โœจ Magic Function Game")
559
- st.write(f"**Story:** {story[:100]}{'...' if len(story) > 100 else ''}")
560
- st.write("**Instructions:** Create your function then use it to solve the challenge!")
561
-
562
- # Game area
563
- col1, col2 = st.columns(2)
564
-
565
- with col1:
566
- st.subheader("Your Function")
567
- if not st.session_state.function_created:
568
- st.write("Create your magic function:")
569
- if st.button("โœจ Create Magic Function", key="create_func", use_container_width=True):
570
- st.session_state.function_created = True
571
- st.rerun()
572
- else:
573
- st.success("Function created!")
574
- st.code("def magic_spell():\n print('โœจ Magic happens!')", language="python")
575
-
576
- with col2:
577
- st.subheader("Cast Your Spell")
578
- if st.session_state.function_created:
579
- st.write(f"Use your function to complete the task ({st.session_state.function_used}/{actions_needed}):")
580
- if st.button("๐Ÿ”ฎ Cast Spell", key="cast_spell", use_container_width=True):
581
- st.session_state.function_used += 1
582
- if st.session_state.function_used >= actions_needed:
583
- st.session_state.game_complete = True
584
- st.rerun()
585
-
586
- # Visual effects
587
- effect_html = "<div style='text-align: center; margin-top: 20px;'>"
588
- for i in range(st.session_state.function_used):
589
- effect_html += "<span style='font-size: 36px; margin: 5px;'>โœจ</span>"
590
- effect_html += "</div>"
591
- st.markdown(effect_html, unsafe_allow_html=True)
592
-
593
- if st.session_state.game_complete:
594
- st.success("๐ŸŽ‰ You completed the game by reusing your function! Great job!")
595
- if st.button("Play Again", key="function_restart"):
596
- st.session_state.function_created = False
597
- st.session_state.function_used = 0
598
- st.session_state.game_complete = False
599
- st.rerun()
600
 
601
  def main():
602
  """Main application function"""
603
- st.title("๐ŸŽฎ StoryCoder - Learn Python Through Games!")
604
- st.subheader("Turn your story into a game and discover coding secrets with interactive gameplay!")
605
 
606
  # Initialize session state
607
  if 'story' not in st.session_state:
608
  st.session_state.story = ""
609
  if 'concepts' not in st.session_state:
610
  st.session_state.concepts = []
611
- if 'game_type' not in st.session_state:
612
- st.session_state.game_type = None
613
- if 'concept_explanation' not in st.session_state:
614
- st.session_state.concept_explanation = ""
615
- if 'audio_file' not in st.session_state:
616
- st.session_state.audio_file = None
617
- if 'explanation_audio' not in st.session_state:
618
- st.session_state.explanation_audio = None
619
  if 'active_tab' not in st.session_state:
620
  st.session_state.active_tab = "story"
621
 
622
  # Create tabs
623
- tabs = st.empty()
624
  tab_cols = st.columns(5)
625
  with tab_cols[0]:
626
  if st.button("๐Ÿ“– Create Story"):
627
  st.session_state.active_tab = "story"
628
  with tab_cols[1]:
629
- if st.button("๐ŸŽฎ Play Game"):
630
  st.session_state.active_tab = "game"
631
  with tab_cols[2]:
632
  if st.button("๐Ÿ” Concepts"):
633
  st.session_state.active_tab = "concepts"
634
  with tab_cols[3]:
635
- if st.button("๐Ÿ’ก Explain"):
636
- st.session_state.active_tab = "explain"
637
  with tab_cols[4]:
638
  if st.button("๐Ÿ”„ Reset"):
639
- st.session_state.story = ""
640
- st.session_state.concepts = []
641
- st.session_state.game_type = None
642
- st.session_state.concept_explanation = ""
643
- st.session_state.audio_file = None
644
- st.session_state.explanation_audio = None
645
  st.session_state.active_tab = "story"
 
646
 
647
  # Story creation tab
648
  if st.session_state.active_tab == "story":
649
  with st.container():
650
  st.header("๐Ÿ“– Create Your Story")
651
- st.write("Write a short story (2-5 sentences) and I'll turn it into an interactive game!")
652
 
653
  story = st.text_area(
654
  "Your story:",
655
  height=200,
656
- placeholder="Once upon a time, a rabbit hopped 3 times to reach a carrot...",
657
  value=st.session_state.story,
658
  key="story_input"
659
  )
660
 
661
  if st.button("Create Game!", use_container_width=True):
662
- if len(story) < 10:
663
- st.error("Your story needs to be at least 10 characters long!")
664
  else:
665
  st.session_state.story = story
666
- with st.spinner("๐Ÿง  Analyzing your story for coding concepts..."):
667
  st.session_state.concepts = analyze_story(story)
 
668
 
669
- if not st.session_state.concepts:
670
- st.warning("No coding concepts detected! Using a default game.")
671
- st.session_state.game_type = "loop"
672
- else:
673
- st.session_state.game_type = st.session_state.concepts[0]
674
-
675
- with st.spinner("๐ŸŽฎ Creating your interactive game..."):
676
- # Just a delay for effect
677
- time.sleep(1)
678
-
679
- with st.spinner("๐Ÿง  Generating concept explanation..."):
680
- st.session_state.concept_explanation = generate_concept_explanation(
681
  story,
682
- st.session_state.game_type
 
683
  )
684
 
685
- with st.spinner("๐Ÿ”Š Creating audio narration..."):
686
- st.session_state.audio_file = text_to_speech(
687
- f"Your story: {story}. Let's play a game to learn programming!"
 
 
 
 
 
 
688
  )
 
 
689
 
690
  st.session_state.active_tab = "game"
691
  st.rerun()
@@ -695,173 +555,144 @@ def main():
695
  col1, col2, col3 = st.columns(3)
696
  with col1:
697
  st.caption("Loop Example")
698
- st.code('"A dragon breathes fire 5 times at the castle"', language="text")
699
- st.image(create_game_preview("loop"),
700
- use_container_width=True,
701
- caption="Loop Adventure Game Preview")
702
  with col2:
703
  st.caption("Conditional Example")
704
- st.code('"If it rains, the cat stays inside, else it goes out"', language="text")
705
- st.image(create_game_preview("conditional"),
706
- use_container_width=True,
707
- caption="Decision Quest Game Preview")
708
  with col3:
709
  st.caption("Function Example")
710
- st.code('"A wizard casts a spell to make flowers grow"', language="text")
711
- st.image(create_game_preview("function"),
712
- use_container_width=True,
713
- caption="Magic Function Game Preview")
714
 
715
  # Game tab
716
  elif st.session_state.active_tab == "game":
717
- st.header(f"๐ŸŽฎ Your Story Game: {GAME_TEMPLATES.get(st.session_state.game_type, {}).get('name', 'Coding Adventure')}")
718
 
719
- if not st.session_state.story:
720
  st.warning("Please create a story first!")
721
  st.session_state.active_tab = "story"
722
  st.rerun()
723
 
724
- # Display game based on concept
725
- if st.session_state.game_type == "loop":
726
- create_loop_game(st.session_state.story)
727
- elif st.session_state.game_type == "conditional":
728
- create_conditional_game(st.session_state.story)
729
- elif st.session_state.game_type == "function":
730
- create_function_game(st.session_state.story)
 
731
  else:
732
- # Default to loop game
733
- create_loop_game(st.session_state.story)
 
 
734
 
735
- # Concept explanation section
736
- if st.session_state.concept_explanation:
737
- st.subheader("๐ŸŽ“ What You're Learning")
738
- concept = st.session_state.game_type
739
- details = CONCEPTS.get(concept, {})
740
-
741
- st.markdown(f"""
742
- <div class="explanation-box">
743
- <div class="explanation-header">
744
- <span style="font-size:36px;">{details.get('emoji', 'โœจ')}</span>
745
- <h3>Learning {details.get('name', 'Programming')} Concept</h3>
746
- </div>
747
- <p>{st.session_state.concept_explanation}</p>
748
- </div>
749
- """, unsafe_allow_html=True)
750
 
751
- # Generate explanation audio if not already generated
752
- if not st.session_state.explanation_audio:
753
- with st.spinner("๐Ÿ”Š Creating concept explanation audio..."):
754
- st.session_state.explanation_audio = text_to_speech(
755
- st.session_state.concept_explanation.replace('<span class="concept-highlight">', '')
756
- .replace('</span>', '')
757
- .replace('<div class="code-block">', '')
758
- .replace('</div>', ''),
759
- "concept_explanation.wav"
760
- )
761
 
762
- if st.session_state.explanation_audio:
763
- st.markdown("### ๐Ÿ”Š Concept Explanation")
764
- with open(st.session_state.explanation_audio, "rb") as f:
765
- audio_bytes = f.read()
766
- st.audio(audio_bytes, format='audio/wav')
767
-
768
- if st.button("โ–ถ๏ธ Play Explanation", use_container_width=True):
769
- autoplay_audio(st.session_state.explanation_audio)
770
 
771
  # Concepts tab
772
  elif st.session_state.active_tab == "concepts":
773
- st.header("๐Ÿ” Coding Concepts in Your Story")
 
774
 
775
  if not st.session_state.concepts:
776
  st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'make'.")
777
  else:
778
- st.subheader("We used these programming concepts in your game:")
779
  for concept in st.session_state.concepts:
780
  if concept in CONCEPTS:
781
  details = CONCEPTS[concept]
782
  st.markdown(f"""
783
- <div class="concept-card">
784
  <div style="display:flex; align-items:center; gap:15px;">
785
  <span style="font-size:36px;">{details['emoji']}</span>
786
  <h3 style="color:{details['color']};">{details['name']}</h3>
787
  </div>
788
  <p>{details['description']}</p>
789
- <div class="code-block">{details['example']}</div>
790
- <p><strong>Explanation:</strong> {details.get('explanation', 'Learn how this concept works!')}</p>
791
  </div>
792
  """, unsafe_allow_html=True)
793
-
794
- # Play explanation if available
795
- if st.session_state.explanation_audio:
796
- st.subheader("๐Ÿ”Š Concept Explanation")
797
- with open(st.session_state.explanation_audio, "rb") as f:
798
- audio_bytes = f.read()
799
- st.audio(audio_bytes, format='audio/wav')
800
 
801
- if st.button("โ–ถ๏ธ Play Full Explanation", use_container_width=True):
802
- autoplay_audio(st.session_state.explanation_audio)
803
-
804
- # Explanation tab
805
- elif st.session_state.active_tab == "explain":
806
- st.header("๐Ÿ’ก Interactive Concept Explanation")
807
 
808
- if not st.session_state.concept_explanation:
809
- st.warning("Please create a story first to generate explanations!")
810
- st.session_state.active_tab = "story"
811
  st.rerun()
 
 
 
 
 
812
 
813
- st.subheader("๐Ÿง  Deep Dive into Programming Concepts")
814
-
815
- if st.session_state.concepts:
816
- concept = st.session_state.concepts[0]
817
- details = CONCEPTS.get(concept, {})
818
 
819
- st.markdown(f"""
820
- <div class="explanation-box">
821
- <div class="explanation-header">
822
- <span style="font-size:36px;">{details.get('emoji', 'โœจ')}</span>
823
- <h3>Understanding {details.get('name', 'Programming')} Concept</h3>
824
- </div>
825
- <p>{st.session_state.concept_explanation}</p>
826
- </div>
827
- """, unsafe_allow_html=True)
828
 
829
- # Visual example
830
- st.subheader("๐ŸŽฎ How It Works in Your Game")
831
- if concept == "loop":
832
- st.image("https://media.giphy.com/media/3o7abKhOpu0NwenH3O/giphy.gif",
833
- use_column_width=True, caption="Loop Concept in Action")
834
- elif concept == "conditional":
835
- st.image("https://media.giphy.com/media/l0HlG8vJXW0X5yX4s/giphy.gif",
836
- use_column_width=True, caption="Conditional Concept in Action")
837
- else:
838
- st.image("https://media.giphy.com/media/3o7TKsQ8UQ4l4LhGz6/giphy.gif",
839
- use_column_width=True, caption="Function Concept in Action")
840
 
841
- # Real-world examples
842
- st.subheader("๐ŸŒ Real-World Examples")
843
- if concept == "loop":
844
- st.write("- Video games that have repeating levels or enemies")
845
- st.write("- Apps that process multiple items (like photos in a gallery)")
846
- st.write("- Animations that need to run continuously")
847
- elif concept == "conditional":
848
- st.write("- Weather apps that show different outfits based on temperature")
849
- st.write("- Games that change difficulty based on player skill")
850
- st.write("- Apps that display different content based on user preferences")
851
- else:
852
- st.write("- Calculator apps with reusable operations")
853
- st.write("- Games with special moves that can be used repeatedly")
854
- st.write("- Websites with reusable components like buttons and menus")
855
 
856
- # Play explanation audio
857
- if st.session_state.explanation_audio:
858
- st.subheader("๐Ÿ”Š Listen to the Explanation")
859
- with open(st.session_state.explanation_audio, "rb") as f:
860
- audio_bytes = f.read()
861
- st.audio(audio_bytes, format='audio/wav')
862
-
863
- if st.button("โ–ถ๏ธ Play Explanation", use_container_width=True):
864
- autoplay_audio(st.session_state.explanation_audio)
 
 
 
 
865
 
866
  if __name__ == "__main__":
867
  main()
 
1
+ # app.py - Game-Based Learning Version
2
  import streamlit as st
3
  import os
4
  import time
5
  import random
 
 
 
 
 
6
  import base64
7
  import json
8
  import requests
9
+ import re
10
+ from PIL import Image
 
 
 
11
  import io
12
+ import matplotlib.pyplot as plt
13
+ import numpy as np
14
+ import pandas as pd
15
+ import plotly.express as px
16
+ import plotly.graph_objects as go
17
 
18
  # Configure Streamlit page
19
  st.set_page_config(
20
+ page_title="StoryCoder - Learn Coding Through Games",
21
+ page_icon="๐Ÿง™โ€โ™‚๏ธ",
22
  layout="wide",
23
  initial_sidebar_state="expanded"
24
  )
25
 
26
+ # Custom CSS for colorful UI
27
  st.markdown("""
28
  <style>
29
+ @import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
30
 
31
  :root {
32
+ --primary: #FF6B6B;
33
+ --secondary: #4ECDC4;
34
+ --accent: #FFD166;
35
+ --dark: #1A535C;
36
+ --light: #F7FFF7;
 
37
  }
38
 
39
  body {
40
+ background: linear-gradient(135deg, var(--light) 0%, #E8F4F8 100%);
41
  font-family: 'Comic Neue', cursive;
 
42
  }
43
 
44
  .stApp {
 
49
  background-color: white;
50
  border-radius: 20px;
51
  padding: 25px;
52
+ box-shadow: 0 8px 16px rgba(26, 83, 92, 0.15);
53
  border: 3px solid var(--accent);
54
  margin-bottom: 25px;
55
  }
 
57
  .header {
58
  color: var(--dark);
59
  text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
 
60
  }
61
 
62
  .concept-card {
63
+ background: linear-gradient(145deg, #ffffff, #f0f0f0);
64
  border-radius: 15px;
65
+ padding: 15px;
66
+ margin: 10px 0;
67
+ border-left: 5px solid var(--secondary);
68
+ box-shadow: 0 4px 8px rgba(0,0,0,0.05);
69
  }
70
 
71
  .stButton>button {
72
  background: linear-gradient(45deg, var(--primary), var(--secondary));
73
  color: white;
74
+ border-radius: 12px;
75
+ padding: 10px 24px;
76
  font-weight: bold;
77
  font-size: 18px;
78
  border: none;
79
  transition: all 0.3s;
 
80
  }
81
 
82
  .stButton>button:hover {
83
  transform: scale(1.05);
84
+ box-shadow: 0 6px 12px rgba(0,0,0,0.15);
85
  }
86
 
87
  .stTextInput>div>div>input {
88
+ border-radius: 12px;
89
+ padding: 12px;
90
  border: 2px solid var(--accent);
 
91
  }
92
 
93
  .tabs {
 
98
  }
99
 
100
  .tab {
101
+ padding: 10px 20px;
102
  background-color: var(--accent);
103
+ border-radius: 10px;
104
  cursor: pointer;
105
  font-weight: bold;
106
  white-space: nowrap;
 
 
 
 
 
 
107
  }
108
 
109
  .tab.active {
110
+ background-color: var(--secondary);
111
  color: white;
 
112
  }
113
 
114
  @media (max-width: 768px) {
 
118
  }
119
 
120
  .game-container {
121
+ background-color: #1a1a2e;
122
+ border-radius: 15px;
123
+ padding: 20px;
124
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
125
+ margin-bottom: 25px;
126
  position: relative;
127
  overflow: hidden;
 
128
  }
129
 
130
+ .game-preview {
131
+ border-radius: 10px;
132
  overflow: hidden;
133
  margin: 0 auto;
134
  display: block;
135
  max-width: 100%;
 
 
136
  }
137
 
138
  .character {
 
141
  margin: 10px 0;
142
  }
143
 
144
+ .game-instructions {
145
+ background-color: #f0f8ff;
 
 
 
 
 
 
 
 
146
  border-radius: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  padding: 15px;
 
148
  margin: 15px 0;
 
149
  }
150
 
151
+ .ai-game {
152
+ border: 3px solid var(--accent);
153
+ border-radius: 15px;
154
+ padding: 15px;
155
+ background: white;
156
+ margin: 20px 0;
157
  }
158
  </style>
159
  """, unsafe_allow_html=True)
 
165
  "emoji": "๐Ÿ”„",
166
  "description": "Loops repeat actions multiple times",
167
  "example": "for i in range(5):\n print('Hello!')",
168
+ "color": "#FF9E6D"
 
169
  },
170
  "conditional": {
171
  "name": "Conditional",
172
  "emoji": "โ“",
173
  "description": "Conditionals make decisions in code",
174
  "example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
175
+ "color": "#4ECDC4"
 
176
  },
177
  "function": {
178
  "name": "Function",
179
  "emoji": "โœจ",
180
  "description": "Functions are reusable blocks of code",
181
  "example": "def greet(name):\n print(f'Hello {name}!')",
182
+ "color": "#FFD166"
 
183
  },
184
  "variable": {
185
  "name": "Variable",
186
  "emoji": "๐Ÿ“ฆ",
187
  "description": "Variables store information",
188
  "example": "score = 10\nplayer = 'Alex'",
189
+ "color": "#FF6B6B"
 
190
  },
191
  "list": {
192
  "name": "List",
193
  "emoji": "๐Ÿ“",
194
  "description": "Lists store collections of items",
195
  "example": "fruits = ['apple', 'banana', 'orange']",
196
+ "color": "#1A535C"
 
197
  }
198
  }
199
 
200
+ # Pre-generated game examples
201
+ GAME_EXAMPLES = {
202
  "loop": {
203
+ "image": "https://i.imgur.com/7zQY1eE.gif",
204
+ "description": "Collect coins in a looping maze"
 
 
205
  },
206
  "conditional": {
207
+ "image": "https://i.imgur.com/5X8jYAy.gif",
208
+ "description": "Avoid obstacles by making decisions"
 
 
209
  },
210
  "function": {
211
+ "image": "https://i.imgur.com/9zJkQ7P.gif",
212
+ "description": "Cast spells using reusable functions"
 
 
213
  }
214
  }
215
 
216
+ # Initialize Groq client
217
+ def get_groq_client():
218
+ api_key = st.secrets.get("GROQ_API_KEY", "")
219
+ if not api_key:
220
+ st.warning("Groq API key not found. Some features may be limited.")
221
+ return None
222
+ try:
223
+ from groq import Groq
224
+ return Groq(api_key=api_key)
225
+ except ImportError:
226
+ st.error("Groq library not installed. Please install with `pip install groq`")
227
+ return None
228
+ except Exception as e:
229
+ st.error(f"Error initializing Groq client: {str(e)}")
230
+ return None
231
 
232
+ # Analyze story and identify programming concepts
233
  def analyze_story(story):
 
234
  story_lower = story.lower()
235
  detected_concepts = []
236
 
237
  # Check for loops
238
+ if any(word in story_lower for word in ["times", "repeat", "again", "multiple", "many"]):
239
  detected_concepts.append("loop")
240
 
241
  # Check for conditionals
242
+ if any(word in story_lower for word in ["if", "when", "unless", "whether", "decision"]):
243
  detected_concepts.append("conditional")
244
 
245
  # Check for functions
246
+ if any(word in story_lower for word in ["make", "create", "do", "perform", "cast", "spell"]):
247
  detected_concepts.append("function")
248
 
249
  # Check for variables
250
+ if any(word in story_lower for word in ["is", "has", "set to", "value", "store"]):
251
  detected_concepts.append("variable")
252
 
253
  # Check for lists
254
+ if any(word in story_lower for word in ["and", "many", "several", "collection", "items", "group"]):
255
  detected_concepts.append("list")
256
 
257
  return list(set(detected_concepts))
258
 
259
+ # Extract entities from story using regex
260
+ def extract_entities(story):
261
+ entities = {
262
+ "characters": [],
263
+ "objects": [],
264
+ "actions": [],
265
+ "locations": []
266
+ }
267
+
268
+ # Extract characters (proper nouns)
269
+ entities["characters"] = list(set(re.findall(r'\b[A-Z][a-z]+\b', story)))
270
+
271
+ # Extract objects (nouns after "the")
272
+ entities["objects"] = list(set(re.findall(r'\bthe\s+(\w+)', story, re.I)))
273
+
274
+ # Extract actions (verbs)
275
+ entities["actions"] = list(set(re.findall(r'\b(\w+ed|\w+ing)\b', story)))
276
+
277
+ # Extract locations (nouns after "in", "on", "at")
278
+ entities["locations"] = list(set(re.findall(r'\b(in|on|at)\s+the?\s?(\w+)', story)))
279
+
280
+ # Filter out common words
281
+ common_words = ["the", "a", "an", "and", "or", "but", "if", "then", "when", "where"]
282
+ for key in entities:
283
+ entities[key] = [word for word in entities[key] if word.lower() not in common_words and len(word) > 2]
284
+
285
+ return entities
286
 
287
+ # Generate game concept using Groq
288
+ def generate_game_concept(story, concepts, entities):
289
+ groq_client = get_groq_client()
290
+ if not groq_client:
291
+ return {
292
+ "title": "Adventure Game",
293
+ "description": "An exciting game based on your story!",
294
+ "mechanics": "Collect items and avoid obstacles",
295
+ "code_concepts": ", ".join(concepts),
296
+ "instructions": "Use arrow keys to move and space to jump"
297
+ }
 
 
 
 
 
 
 
 
 
 
298
 
299
+ try:
300
+ prompt = f"""
301
+ You are a game designer creating educational games for kids aged 6-12.
302
+ Create a simple 2D game concept based on the following story:
303
 
304
+ Story: "{story}"
 
 
305
 
306
+ Programming concepts to include: {', '.join(concepts)}
 
 
307
 
308
+ Extracted entities:
309
+ - Characters: {', '.join(entities['characters'])}
310
+ - Objects: {', '.join(entities['objects'])}
311
+ - Actions: {', '.join(entities['actions'])}
312
+ - Locations: {', '.join([loc[1] for loc in entities['locations']])}
 
313
 
314
+ Respond in JSON format with these keys:
315
+ - title: Game title (max 5 words)
316
+ - description: Game description (1-2 sentences)
317
+ - mechanics: Core game mechanics (1 sentence)
318
+ - code_concepts: How programming concepts are used in the game
319
+ - instructions: Simple game controls (max 10 words)
320
+ """
321
 
322
+ response = groq_client.chat.completions.create(
323
+ model="llama3-8b-8192",
324
+ messages=[{"role": "user", "content": prompt}],
325
+ temperature=0.7,
326
+ max_tokens=512,
327
+ response_format={"type": "json_object"}
328
+ )
329
+
330
+ game_data = json.loads(response.choices[0].message.content)
331
+ return game_data
332
+
 
 
 
 
333
  except Exception as e:
334
+ st.error(f"Game concept generation error: {str(e)}")
335
+ return {
336
+ "title": "Adventure Game",
337
+ "description": "An exciting game based on your story!",
338
+ "mechanics": "Collect items and avoid obstacles",
339
+ "code_concepts": ", '.join(concepts)",
340
+ "instructions": "Use arrow keys to move and space to jump"
341
+ }
 
 
 
 
 
 
342
 
343
+ # Generate game code using Groq
344
+ def generate_game_code(story, game_concept):
345
+ groq_client = get_groq_client()
346
+ if not groq_client:
347
+ return "# Error: Groq client not available", ""
348
 
349
+ try:
350
+ prompt = f"""
351
+ You are a Python game developer creating educational games for kids using Pygame.
352
+ Create a simple 2D game based on the following concept:
353
+
354
+ Game Title: {game_concept['title']}
355
+ Description: {game_concept['description']}
356
+ Mechanics: {game_concept['mechanics']}
357
+ Instructions: {game_concept['instructions']}
358
+
359
+ Requirements:
360
+ - Use Pygame library
361
+ - Simple graphics (shapes and colors)
362
+ - Include at least one character the player controls
363
+ - Include collectible items
364
+ - Include obstacles to avoid
365
+ - Score tracking
366
+ - Game over condition
367
+
368
+ Output ONLY the Python code with no additional text or explanations.
369
+ """
370
+
371
+ response = groq_client.chat.completions.create(
372
+ model="llama3-70b-8192",
373
+ messages=[{"role": "user", "content": prompt}],
374
+ temperature=0.5,
375
+ max_tokens=2048
376
+ )
377
+
378
+ game_code = response.choices[0].message.content
379
+
380
+ # Clean up the code
381
+ if "```python" in game_code:
382
+ game_code = game_code.split("```python")[1].split("```")[0]
383
+ elif "```" in game_code:
384
+ game_code = game_code.split("```")[1]
385
+
386
+ return game_code, "Success"
387
 
388
+ except Exception as e:
389
+ return f"# Error generating game code\nprint('{str(e)}')", str(e)
390
 
391
+ # Generate game preview image using SDXL
392
+ def generate_game_preview(game_concept):
393
+ try:
394
+ prompt = f"Cartoon style 2D game scene: {game_concept['description']}, bright colors, children's book illustration"
395
+ api_url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
396
+ headers = {"Authorization": f"Bearer {st.secrets['HF_API_KEY']}"}
397
+ payload = {"inputs": prompt}
398
+
399
+ response = requests.post(api_url, headers=headers, json=payload)
400
+ image = Image.open(BytesIO(response.content))
401
+
402
+ # Convert to bytes
403
+ buf = io.BytesIO()
404
+ image.save(buf, format='PNG')
405
+ buf.seek(0)
406
+ return buf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
+ except Exception as e:
409
+ st.error(f"Game preview generation error: {str(e)}")
410
+ return None
 
 
 
 
 
 
 
 
 
 
411
 
412
+ # Create interactive visualization of game mechanics
413
+ def create_game_visualization(game_concept):
414
+ try:
415
+ # Create a simple visualization using Plotly
416
+ fig = go.Figure()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
+ # Add player character
419
+ fig.add_trace(go.Scatter(
420
+ x=[0.5], y=[0.5],
421
+ mode='markers+text',
422
+ marker=dict(size=50, color='blue'),
423
+ text='Player',
424
+ textposition='bottom center',
425
+ name='Player'
426
+ ))
427
+
428
+ # Add collectibles
429
+ for i in range(5):
430
+ fig.add_trace(go.Scatter(
431
+ x=[random.uniform(0.2, 0.8)], y=[random.uniform(0.2, 0.8)],
432
+ mode='markers',
433
+ marker=dict(size=30, color='gold', symbol='star'),
434
+ name='Collectible'
435
+ ))
436
+
437
+ # Add obstacles
438
+ for i in range(3):
439
+ fig.add_trace(go.Scatter(
440
+ x=[random.uniform(0.1, 0.9)], y=[random.uniform(0.1, 0.9)],
441
+ mode='markers',
442
+ marker=dict(size=40, color='red', symbol='x'),
443
+ name='Obstacle'
444
+ ))
445
+
446
+ # Update layout
447
+ fig.update_layout(
448
+ title=f"Game Visualization: {game_concept['title']}",
449
+ xaxis=dict(showgrid=False, zeroline=False, visible=False, range=[0, 1]),
450
+ yaxis=dict(showgrid=False, zeroline=False, visible=False, range=[0, 1]),
451
+ showlegend=False,
452
+ margin=dict(l=20, r=20, t=40, b=20),
453
+ height=400,
454
+ paper_bgcolor='rgba(0,0,0,0)',
455
+ plot_bgcolor='rgba(0,0,0,0)'
456
+ )
457
+
458
+ return fig
459
+
460
+ except Exception as e:
461
+ st.error(f"Visualization error: {str(e)}")
462
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
 
464
  def main():
465
  """Main application function"""
466
+ st.title("๐Ÿง™โ€โ™‚๏ธ StoryCoder - Learn Coding Through Games!")
467
+ st.subheader("Turn your story into a game and discover coding secrets!")
468
 
469
  # Initialize session state
470
  if 'story' not in st.session_state:
471
  st.session_state.story = ""
472
  if 'concepts' not in st.session_state:
473
  st.session_state.concepts = []
474
+ if 'entities' not in st.session_state:
475
+ st.session_state.entities = {}
476
+ if 'game_concept' not in st.session_state:
477
+ st.session_state.game_concept = {}
478
+ if 'game_code' not in st.session_state:
479
+ st.session_state.game_code = ""
480
+ if 'game_preview' not in st.session_state:
481
+ st.session_state.game_preview = None
482
  if 'active_tab' not in st.session_state:
483
  st.session_state.active_tab = "story"
484
 
485
  # Create tabs
 
486
  tab_cols = st.columns(5)
487
  with tab_cols[0]:
488
  if st.button("๐Ÿ“– Create Story"):
489
  st.session_state.active_tab = "story"
490
  with tab_cols[1]:
491
+ if st.button("๐ŸŽฎ Game"):
492
  st.session_state.active_tab = "game"
493
  with tab_cols[2]:
494
  if st.button("๐Ÿ” Concepts"):
495
  st.session_state.active_tab = "concepts"
496
  with tab_cols[3]:
497
+ if st.button("๐Ÿ’ป Code"):
498
+ st.session_state.active_tab = "code"
499
  with tab_cols[4]:
500
  if st.button("๐Ÿ”„ Reset"):
501
+ for key in list(st.session_state.keys()):
502
+ if key != 'active_tab':
503
+ del st.session_state[key]
 
 
 
504
  st.session_state.active_tab = "story"
505
+ st.rerun()
506
 
507
  # Story creation tab
508
  if st.session_state.active_tab == "story":
509
  with st.container():
510
  st.header("๐Ÿ“– Create Your Story")
511
+ st.write("Write a short story (2-5 sentences) and I'll turn it into a game!")
512
 
513
  story = st.text_area(
514
  "Your story:",
515
  height=200,
516
+ placeholder="Once upon a time, a rabbit named Ruby needed to collect 5 magical carrots in the enchanted forest while avoiding mischievous squirrels...",
517
  value=st.session_state.story,
518
  key="story_input"
519
  )
520
 
521
  if st.button("Create Game!", use_container_width=True):
522
+ if len(story) < 20:
523
+ st.error("Your story needs to be at least 20 characters long!")
524
  else:
525
  st.session_state.story = story
526
+ with st.spinner("๐Ÿง  Analyzing your story..."):
527
  st.session_state.concepts = analyze_story(story)
528
+ st.session_state.entities = extract_entities(story)
529
 
530
+ with st.spinner("๐ŸŽฎ Designing your game..."):
531
+ st.session_state.game_concept = generate_game_concept(
 
 
 
 
 
 
 
 
 
 
532
  story,
533
+ st.session_state.concepts,
534
+ st.session_state.entities
535
  )
536
 
537
+ with st.spinner("๐Ÿ‘พ Generating game preview..."):
538
+ st.session_state.game_preview = generate_game_preview(
539
+ st.session_state.game_concept
540
+ )
541
+
542
+ with st.spinner("๐Ÿ’ป Creating game code..."):
543
+ st.session_state.game_code, error = generate_game_code(
544
+ story,
545
+ st.session_state.game_concept
546
  )
547
+ if error:
548
+ st.error(f"Code generation error: {error}")
549
 
550
  st.session_state.active_tab = "game"
551
  st.rerun()
 
555
  col1, col2, col3 = st.columns(3)
556
  with col1:
557
  st.caption("Loop Example")
558
+ st.code('"Ruby the rabbit needs to collect 5 magical carrots that appear every 10 seconds in the enchanted forest"', language="text")
559
+ st.image(GAME_EXAMPLES["loop"]["image"],
560
+ use_column_width=True,
561
+ caption=GAME_EXAMPLES["loop"]["description"])
562
  with col2:
563
  st.caption("Conditional Example")
564
+ st.code('"If Alex the wizard sees a dragon, he casts a fire spell, otherwise he walks forward to find treasures"', language="text")
565
+ st.image(GAME_EXAMPLES["conditional"]["image"],
566
+ use_column_width=True,
567
+ caption=GAME_EXAMPLES["conditional"]["description"])
568
  with col3:
569
  st.caption("Function Example")
570
+ st.code('"Wizard Lily creates magic spells to solve puzzles - each spell is a special function"', language="text")
571
+ st.image(GAME_EXAMPLES["function"]["image"],
572
+ use_column_width=True,
573
+ caption=GAME_EXAMPLES["function"]["description"])
574
 
575
  # Game tab
576
  elif st.session_state.active_tab == "game":
577
+ st.header("๐ŸŽฎ Your Story Game")
578
 
579
+ if not st.session_state.get('story'):
580
  st.warning("Please create a story first!")
581
  st.session_state.active_tab = "story"
582
  st.rerun()
583
 
584
+ # Display game concept
585
+ st.subheader(f"โœจ {st.session_state.game_concept.get('title', 'Your Adventure Game')}")
586
+ st.write(st.session_state.game_concept.get('description', 'An exciting game based on your story!'))
587
+
588
+ # Display game preview
589
+ st.subheader("๐Ÿ–ผ๏ธ Game Preview")
590
+ if st.session_state.game_preview:
591
+ st.image(st.session_state.game_preview, use_container_width=True)
592
  else:
593
+ concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
594
+ st.image(GAME_EXAMPLES[concept]["image"],
595
+ use_container_width=True,
596
+ caption="Example game preview")
597
 
598
+ # Game mechanics visualization
599
+ st.subheader("๐ŸŽฎ Game Mechanics")
600
+ if st.session_state.game_concept:
601
+ fig = create_game_visualization(st.session_state.game_concept)
602
+ if fig:
603
+ st.plotly_chart(fig, use_container_width=True)
604
+ else:
605
+ st.info("Game mechanics visualization would appear here")
606
+
607
+ # Game instructions
608
+ st.subheader("๐Ÿ“œ How to Play")
609
+ st.markdown(f"""
610
+ <div class="game-instructions">
611
+ <h4>Game Controls:</h4>
612
+ <p>{st.session_state.game_concept.get('instructions', 'Use arrow keys to move and space to jump')}</p>
613
 
614
+ <h4>Game Mechanics:</h4>
615
+ <p>{st.session_state.game_concept.get('mechanics', 'Collect items and avoid obstacles')}</p>
 
 
 
 
 
 
 
 
616
 
617
+ <h4>Coding Concepts:</h4>
618
+ <p>{st.session_state.game_concept.get('code_concepts', 'Loops, conditionals, and functions')}</p>
619
+ </div>
620
+ """, unsafe_allow_html=True)
621
+
622
+ if st.button("Show Coding Secrets!", use_container_width=True):
623
+ st.session_state.active_tab = "concepts"
624
+ st.rerun()
625
 
626
  # Concepts tab
627
  elif st.session_state.active_tab == "concepts":
628
+ st.header("๐Ÿ” Coding Concepts in Your Game")
629
+ st.subheader("We used these programming concepts in your game:")
630
 
631
  if not st.session_state.concepts:
632
  st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'make'.")
633
  else:
 
634
  for concept in st.session_state.concepts:
635
  if concept in CONCEPTS:
636
  details = CONCEPTS[concept]
637
  st.markdown(f"""
638
+ <div class="concept-card" style="border-left: 5px solid {details['color']};">
639
  <div style="display:flex; align-items:center; gap:15px;">
640
  <span style="font-size:36px;">{details['emoji']}</span>
641
  <h3 style="color:{details['color']};">{details['name']}</h3>
642
  </div>
643
  <p>{details['description']}</p>
644
+ <pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
 
645
  </div>
646
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
647
 
648
+ st.subheader("๐ŸŽฎ How Concepts Are Used in Your Game")
649
+ st.write(st.session_state.game_concept.get('code_concepts', 'These concepts power the mechanics of your game'))
 
 
 
 
650
 
651
+ if st.button("See the Game Code!", use_container_width=True):
652
+ st.session_state.active_tab = "code"
 
653
  st.rerun()
654
+
655
+ # Code tab
656
+ elif st.session_state.active_tab == "code":
657
+ st.header("๐Ÿ’ป Game Code")
658
+ st.write("Here's the Python code for your game. You can run it on your computer!")
659
 
660
+ if st.session_state.game_code:
661
+ st.subheader("๐ŸŽฎ Game Implementation")
662
+ st.code(st.session_state.game_code, language="python")
 
 
663
 
664
+ # Download button
665
+ st.download_button(
666
+ label="Download Game Code",
667
+ data=st.session_state.game_code,
668
+ file_name="story_game.py",
669
+ mime="text/python",
670
+ use_container_width=True
671
+ )
 
672
 
673
+ st.info("๐Ÿ’ก How to run your game:")
674
+ st.markdown("""
675
+ 1. Install Python from [python.org](https://python.org)
676
+ 2. Install Pygame: `pip install pygame`
677
+ 3. Download the code above
678
+ 4. Run it with: `python story_game.py`
679
+ """)
 
 
 
 
680
 
681
+ st.success("๐ŸŽ‰ When you run this code, you'll see your story come to life as a playable game!")
 
 
 
 
 
 
 
 
 
 
 
 
 
682
 
683
+ # Game preview
684
+ st.subheader("๐ŸŽฎ What Your Game Looks Like")
685
+ concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
686
+ st.image(GAME_EXAMPLES[concept]["image"],
687
+ caption="Your game will look similar to this",
688
+ use_column_width=True)
689
+ else:
690
+ st.warning("No game code generated yet!")
691
+
692
+ if st.button("Create Another Game!", use_container_width=True):
693
+ st.session_state.active_tab = "story"
694
+ st.session_state.story = ""
695
+ st.rerun()
696
 
697
  if __name__ == "__main__":
698
  main()