sunbal7 commited on
Commit
d575992
·
verified ·
1 Parent(s): 8e6eb71

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +341 -143
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - Enhanced Version with Playable Online Game
2
  import streamlit as st
3
  import random
4
  import re
@@ -9,7 +9,13 @@ import requests
9
  from PIL import Image
10
  import io
11
  import matplotlib.pyplot as plt
 
12
  from groq import Groq
 
 
 
 
 
13
 
14
  # Configure Streamlit page
15
  st.set_page_config(
@@ -19,7 +25,7 @@ st.set_page_config(
19
  initial_sidebar_state="expanded"
20
  )
21
 
22
- # Custom CSS with modern gradient background
23
  st.markdown("""
24
  <style>
25
  @import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Comic+Neue:wght@700&display=swap');
@@ -32,13 +38,19 @@ st.markdown("""
32
  --light: #F0F0F0;
33
  --game-blue: #1A8CD8;
34
  --game-purple: #9B5DE5;
 
 
 
35
  }
36
 
37
  body {
38
- background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
39
  background-size: 400% 400%;
40
  animation: gradientBG 15s ease infinite;
41
  font-family: 'Comic Neue', cursive;
 
 
 
42
  }
43
 
44
  @keyframes gradientBG {
@@ -48,13 +60,13 @@ st.markdown("""
48
  }
49
 
50
  .stApp {
51
- background: rgba(255, 255, 255, 0.1);
52
- backdrop-filter: blur(10px);
53
- -webkit-backdrop-filter: blur(10px);
54
  }
55
 
56
  .game-card {
57
- background: rgba(255, 255, 255, 0.85);
58
  border-radius: 20px;
59
  padding: 25px;
60
  box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
@@ -75,7 +87,7 @@ st.markdown("""
75
  }
76
 
77
  .concept-card {
78
- background: rgba(255, 255, 255, 0.9);
79
  border-radius: 15px;
80
  padding: 15px;
81
  margin: 10px 0;
@@ -93,11 +105,12 @@ st.markdown("""
93
  border: none;
94
  transition: all 0.3s;
95
  font-family: 'Fredoka One', cursive;
 
96
  }
97
 
98
  .stButton>button:hover {
99
  transform: scale(1.05);
100
- box-shadow: 0 8px 16px rgba(106, 103, 206, 0.3);
101
  }
102
 
103
  .stTextInput>div>div>input {
@@ -113,7 +126,7 @@ st.markdown("""
113
  gap: 10px;
114
  margin-bottom: 20px;
115
  overflow-x: auto;
116
- background: rgba(255, 255, 255, 0.2);
117
  padding: 10px;
118
  border-radius: 20px;
119
  backdrop-filter: blur(5px);
@@ -121,7 +134,7 @@ st.markdown("""
121
 
122
  .tab {
123
  padding: 12px 24px;
124
- background: rgba(255, 255, 255, 0.3);
125
  border-radius: 15px;
126
  cursor: pointer;
127
  font-weight: bold;
@@ -134,56 +147,7 @@ st.markdown("""
134
 
135
  .tab.active {
136
  background: linear-gradient(45deg, var(--primary), var(--game-blue));
137
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
138
- }
139
-
140
- .game-preview {
141
- border: 4px solid var(--primary);
142
- border-radius: 20px;
143
- overflow: hidden;
144
- background: linear-gradient(135deg, #d0e5ff, #e0d1ff);
145
- padding: 20px;
146
- margin: 20px 0;
147
- }
148
-
149
- .character {
150
- font-size: 64px;
151
- text-align: center;
152
- margin: 20px 0;
153
- text-shadow: 4px 4px 8px rgba(0,0,0,0.2);
154
- }
155
-
156
- .game-container {
157
- background: rgba(255, 255, 255, 0.9);
158
- border-radius: 20px;
159
- padding: 30px;
160
- box-shadow: 0 8px 32px rgba(0,0,0,0.1);
161
- margin: 20px 0;
162
- }
163
-
164
- .code-block {
165
- background: #2b2d42;
166
- color: #f8f9fa;
167
- border-radius: 15px;
168
- padding: 20px;
169
- font-family: 'Courier New', monospace;
170
- overflow-x: auto;
171
- margin: 20px 0;
172
- }
173
-
174
- .concept-emoji {
175
- font-size: 36px;
176
- margin-right: 15px;
177
- vertical-align: middle;
178
- }
179
-
180
- .game-title {
181
- font-family: 'Fredoka One', cursive;
182
- color: white;
183
- text-align: center;
184
- font-size: 32px;
185
- margin-bottom: 20px;
186
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
187
  }
188
 
189
  .game-board {
@@ -205,9 +169,10 @@ st.markdown("""
205
  align-items: center;
206
  border-radius: 8px;
207
  font-size: 30px;
208
- background: rgba(255, 255, 255, 0.8);
209
  transition: all 0.2s;
210
  cursor: pointer;
 
211
  }
212
 
213
  .cell:hover {
@@ -218,18 +183,27 @@ st.markdown("""
218
  .player {
219
  background: linear-gradient(45deg, #6A67CE, #1A8CD8);
220
  color: white;
 
221
  }
222
 
223
  .goal {
224
  background: linear-gradient(45deg, #FDD85D, #FFB347);
 
225
  }
226
 
227
  .obstacle {
228
  background: linear-gradient(45deg, #FF7C7C, #FF5252);
 
229
  }
230
 
231
- .empty {
232
- background: rgba(255, 255, 255, 0.9);
 
 
 
 
 
 
233
  }
234
 
235
  .game-controls {
@@ -261,7 +235,7 @@ st.markdown("""
261
  }
262
 
263
  .score-board {
264
- background: rgba(255, 255, 255, 0.85);
265
  border-radius: 15px;
266
  padding: 15px;
267
  text-align: center;
@@ -269,16 +243,71 @@ st.markdown("""
269
  font-size: 24px;
270
  margin: 20px auto;
271
  width: 300px;
 
 
 
 
 
 
 
 
 
 
 
 
272
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
273
  }
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  @media (max-width: 768px) {
276
  .tabs {
277
  flex-wrap: wrap;
278
  }
279
 
280
- .character {
281
- font-size: 48px;
 
282
  }
283
 
284
  .control-btn {
@@ -286,11 +315,25 @@ st.markdown("""
286
  height: 60px;
287
  font-size: 24px;
288
  }
289
-
290
- .game-board {
291
- width: 90vw;
292
- height: 90vw;
293
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  }
295
  </style>
296
  """, unsafe_allow_html=True)
@@ -320,19 +363,21 @@ def init_session_state():
320
  if 'player_char' not in st.session_state:
321
  st.session_state.player_char = "🦸"
322
  if 'goal_char' not in st.session_state:
323
- st.session_state.goal_char = ""
324
  if 'obstacle_char' not in st.session_state:
325
  st.session_state.obstacle_char = "🪨"
326
-
327
- def reset_game_state():
328
- st.session_state.game_state = {
329
- "player_pos": [0, 0],
330
- "goal_pos": [7, 7],
331
- "obstacles": [[2, 2], [3, 4], [5, 3], [4, 6], [6, 5]],
332
- "score": 0,
333
- "moves": 0,
334
- "game_over": False
335
- }
 
 
336
 
337
  # Concept database
338
  CONCEPTS = {
@@ -340,41 +385,46 @@ CONCEPTS = {
340
  "name": "Loop",
341
  "emoji": "🔄",
342
  "description": "Loops repeat actions multiple times",
343
- "example": "for i in range(5):\n print('Jump!')",
344
  "color": "#6A67CE",
345
- "game_example": "Repeating jumps to cross a river"
 
346
  },
347
  "conditional": {
348
  "name": "Conditional",
349
  "emoji": "❓",
350
  "description": "Conditionals make decisions in code",
351
- "example": "if key_found:\n open_door()\nelse:\n keep_searching()",
352
  "color": "#FF7C7C",
353
- "game_example": "Choosing paths based on obstacles"
 
354
  },
355
  "function": {
356
  "name": "Function",
357
  "emoji": "✨",
358
  "description": "Functions are reusable blocks of code",
359
- "example": "def collect_star():\n score += 1\n play_sound()",
360
  "color": "#FDD85D",
361
- "game_example": "Creating a jump function used multiple times"
 
362
  },
363
  "variable": {
364
  "name": "Variable",
365
  "emoji": "📦",
366
  "description": "Variables store information",
367
- "example": "score = 0\nplayer_health = 100",
368
  "color": "#1A8CD8",
369
- "game_example": "Tracking collected stars"
 
370
  },
371
  "list": {
372
  "name": "List",
373
  "emoji": "📝",
374
  "description": "Lists store collections of items",
375
- "example": "inventory = ['sword', 'shield', 'potion']",
376
  "color": "#4CAF50",
377
- "game_example": "Storing collected treasures"
 
378
  }
379
  }
380
 
@@ -580,6 +630,13 @@ def generate_game_preview(story):
580
  ax.plot(x, y, 's', markersize=15, color=obstacle_color)
581
  ax.text(x, y+0.4, 'Obstacle', ha='center', color='white', fontsize=8)
582
 
 
 
 
 
 
 
 
583
  # Path
584
  ax.plot([2, 8], [2, 2], 'y--', linewidth=1, alpha=0.5)
585
 
@@ -595,6 +652,21 @@ def generate_game_preview(story):
595
  st.error(f"Preview generation error: {str(e)}")
596
  return None
597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
  # Handle player movement
599
  def move_player(direction):
600
  """Update player position based on movement direction"""
@@ -602,6 +674,8 @@ def move_player(direction):
602
  player_pos = state["player_pos"]
603
  goal_pos = state["goal_pos"]
604
  obstacles = state["obstacles"]
 
 
605
 
606
  new_pos = player_pos.copy()
607
 
@@ -621,13 +695,21 @@ def move_player(direction):
621
  state["player_pos"] = new_pos
622
  state["moves"] += 1
623
 
 
 
 
 
 
 
 
624
  # Check for goal collision
625
  if new_pos == goal_pos:
626
- state["score"] += 10
627
  state["game_over"] = True
628
  # Check for star collection
629
- elif new_pos in [[3, 3], [4, 4], [5, 5]]:
630
  state["score"] += 5
 
631
 
632
  st.session_state.game_state = state
633
 
@@ -640,6 +722,15 @@ def create_playable_game():
640
  player_pos = state["player_pos"]
641
  goal_pos = state["goal_pos"]
642
  obstacles = state["obstacles"]
 
 
 
 
 
 
 
 
 
643
 
644
  # Score board
645
  st.markdown(f"""
@@ -657,22 +748,27 @@ def create_playable_game():
657
  for col in range(8):
658
  pos = [row, col]
659
  cell_class = "cell"
 
660
 
661
  if pos == player_pos:
662
  cell_class += " player"
663
  cell_content = st.session_state.player_char
664
  elif pos == goal_pos:
665
  cell_class += " goal"
666
- cell_content = "🏁"
667
  elif pos in obstacles:
668
  cell_class += " obstacle"
669
  cell_content = st.session_state.obstacle_char
670
- elif pos in [[3, 3], [4, 4], [5, 5]]:
671
- cell_class += " goal"
672
  cell_content = "⭐"
673
  else:
674
- cell_class += " empty"
675
- cell_content = ""
 
 
 
 
676
 
677
  with cols[col]:
678
  st.markdown(f"<div class='{cell_class}'>{cell_content}</div>", unsafe_allow_html=True)
@@ -695,16 +791,77 @@ def create_playable_game():
695
  # Reset button
696
  st.button("🔄 Reset Game", on_click=reset_game_state, use_container_width=True)
697
 
698
- # Game over message
699
  if state["game_over"]:
700
  st.balloons()
701
  st.success(f"🎉 You won! Final Score: {state['score']} in {state['moves']} moves!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
702
 
703
  # Main application function
704
  def main():
705
  init_session_state()
706
 
707
- # Sidebar for API key
708
  with st.sidebar:
709
  st.header("⚙️ Settings")
710
  st.session_state.groq_api_key = st.text_input(
@@ -737,12 +894,21 @@ def main():
737
  index=0
738
  )
739
 
 
 
 
 
 
 
 
 
740
  st.divider()
741
  st.caption("Made with ❤️ for kids learning to code")
742
- st.caption("v3.0 | StoryCoder")
743
 
744
  st.title("🎮 StoryCoder - Play & Learn Coding!")
745
- st.subheader("Create your story, play the game, and learn programming concepts!")
 
746
 
747
  # Create tabs
748
  st.markdown('<div class="tabs">', unsafe_allow_html=True)
@@ -760,15 +926,8 @@ def main():
760
  if st.button("💻 Game Code", use_container_width=True):
761
  st.session_state.active_tab = "code"
762
  with col5:
763
- if st.button("🔄 New Story", use_container_width=True):
764
- st.session_state.story = ""
765
- st.session_state.concepts = []
766
- st.session_state.game_scenario = ""
767
- st.session_state.game_code = ""
768
- st.session_state.game_explanation = ""
769
- st.session_state.game_preview = None
770
- st.session_state.active_tab = "story"
771
- reset_game_state()
772
  st.markdown('</div>', unsafe_allow_html=True)
773
 
774
  # Story creation tab
@@ -785,7 +944,7 @@ def main():
785
  key="story_input"
786
  )
787
 
788
- if st.button("Create Game!", use_container_width=True):
789
  if len(story) < 10:
790
  st.error("Your story needs to be at least 10 characters long!")
791
  else:
@@ -881,20 +1040,16 @@ def main():
881
  if not st.session_state.concepts:
882
  st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'collect'.")
883
  else:
884
- for concept in st.session_state.concepts:
885
- if concept in CONCEPTS:
886
- details = CONCEPTS[concept]
887
- st.markdown(f"""
888
- <div class="concept-card" style="border-left: 5px solid {details['color']};">
889
- <div style="display:flex; align-items:center; gap:15px;">
890
- <span style="font-size:36px;">{details['emoji']}</span>
891
- <h3 style="color:{details['color']};">{details['name']}</h3>
892
- </div>
893
- <p>{details['description']}</p>
894
- <p><b>In your game:</b> {details['game_example']}</p>
895
- <pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
896
- </div>
897
- """, unsafe_allow_html=True)
898
 
899
  if st.button("See the Game Code", use_container_width=True):
900
  st.session_state.active_tab = "code"
@@ -939,6 +1094,7 @@ PLAYER_COLOR = (106, 103, 206) # Purple
939
  GOAL_COLOR = (253, 216, 93) # Yellow
940
  OBSTACLE_COLOR = (255, 124, 124) # Coral
941
  TEXT_COLOR = (45, 50, 80) # Dark blue
 
942
 
943
  # Player setup
944
  player_size = 40
@@ -965,6 +1121,15 @@ for i in range(5):
965
  random.randint(20, 50)
966
  ])
967
 
 
 
 
 
 
 
 
 
 
968
  # Game loop
969
  running = True
970
  while running:
@@ -995,11 +1160,18 @@ while running:
995
  # Conditional concept: Check for collision
996
  if player_rect.colliderect(goal_rect):
997
  # Function concept: Increase score
998
- score += 1
999
  # Move goal to new position
1000
  goal_x = random.randint(100, WIDTH - 100)
1001
  goal_y = random.randint(50, HEIGHT - 100)
1002
 
 
 
 
 
 
 
 
1003
  # Drawing
1004
  screen.fill(BACKGROUND)
1005
 
@@ -1008,6 +1180,11 @@ while running:
1008
  pygame.draw.rect(screen, OBSTACLE_COLOR,
1009
  (obstacle[0], obstacle[1], obstacle[2], obstacle[3]))
1010
 
 
 
 
 
 
1011
  # Draw player and goal
1012
  pygame.draw.rect(screen, PLAYER_COLOR,
1013
  (player_x, player_y, player_size, player_size))
@@ -1027,7 +1204,7 @@ while running:
1027
  screen.blit(concepts_text, (20, HEIGHT - 40))
1028
 
1029
  # Display instructions
1030
- help_text = font.render("Arrow keys to move - Collect the yellow circles!", True, TEXT_COLOR)
1031
  screen.blit(help_text, (WIDTH // 2 - 200, HEIGHT - 80))
1032
 
1033
  # Update display
@@ -1050,26 +1227,47 @@ sys.exit()
1050
  mime="text/python",
1051
  use_container_width=True
1052
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1053
  else:
1054
  st.warning("No game code generated yet!")
1055
 
1056
- # Game running instructions
1057
- st.subheader("🖥️ How to Run Your Game")
1058
- st.markdown("""
1059
- <div class="game-card">
1060
- <ol>
1061
- <li>Install Python from <a href="https://python.org" target="_blank">python.org</a></li>
1062
- <li>Install PyGame: Open command prompt and type <code>pip install pygame</code></li>
1063
- <li>Save the game code to a file named <code>my_game.py</code></li>
1064
- <li>Run the game: <code>python my_game.py</code></li>
1065
- <li>Use arrow keys or WASD to play!</li>
1066
- </ol>
1067
- </div>
1068
- """, unsafe_allow_html=True)
1069
 
1070
  if st.button("Create Another Story!", use_container_width=True):
1071
  st.session_state.active_tab = "story"
1072
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
 
1074
  if __name__ == "__main__":
1075
  main()
 
1
+ # app.py - Ultimate Enhanced Version
2
  import streamlit as st
3
  import random
4
  import re
 
9
  from PIL import Image
10
  import io
11
  import matplotlib.pyplot as plt
12
+ import numpy as np
13
  from groq import Groq
14
+ import pygame
15
+ import sys
16
+ from io import StringIO
17
+ import contextlib
18
+ import traceback
19
 
20
  # Configure Streamlit page
21
  st.set_page_config(
 
25
  initial_sidebar_state="expanded"
26
  )
27
 
28
+ # Modern gradient background with animation
29
  st.markdown("""
30
  <style>
31
  @import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Comic+Neue:wght@700&display=swap');
 
38
  --light: #F0F0F0;
39
  --game-blue: #1A8CD8;
40
  --game-purple: #9B5DE5;
41
+ --gradient-start: #1a2a6c;
42
+ --gradient-mid: #b21f1f;
43
+ --gradient-end: #1a2a6c;
44
  }
45
 
46
  body {
47
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-mid), var(--gradient-end));
48
  background-size: 400% 400%;
49
  animation: gradientBG 15s ease infinite;
50
  font-family: 'Comic Neue', cursive;
51
+ margin: 0;
52
+ padding: 0;
53
+ min-height: 100vh;
54
  }
55
 
56
  @keyframes gradientBG {
 
60
  }
61
 
62
  .stApp {
63
+ background: rgba(255, 255, 255, 0.05);
64
+ backdrop-filter: blur(12px);
65
+ -webkit-backdrop-filter: blur(12px);
66
  }
67
 
68
  .game-card {
69
+ background: rgba(255, 255, 255, 0.9);
70
  border-radius: 20px;
71
  padding: 25px;
72
  box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
 
87
  }
88
 
89
  .concept-card {
90
+ background: rgba(255, 255, 255, 0.95);
91
  border-radius: 15px;
92
  padding: 15px;
93
  margin: 10px 0;
 
105
  border: none;
106
  transition: all 0.3s;
107
  font-family: 'Fredoka One', cursive;
108
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
109
  }
110
 
111
  .stButton>button:hover {
112
  transform: scale(1.05);
113
+ box-shadow: 0 8px 16px rgba(106, 103, 206, 0.4);
114
  }
115
 
116
  .stTextInput>div>div>input {
 
126
  gap: 10px;
127
  margin-bottom: 20px;
128
  overflow-x: auto;
129
+ background: rgba(255, 255, 255, 0.15);
130
  padding: 10px;
131
  border-radius: 20px;
132
  backdrop-filter: blur(5px);
 
134
 
135
  .tab {
136
  padding: 12px 24px;
137
+ background: rgba(255, 255, 255, 0.25);
138
  border-radius: 15px;
139
  cursor: pointer;
140
  font-weight: bold;
 
147
 
148
  .tab.active {
149
  background: linear-gradient(45deg, var(--primary), var(--game-blue));
150
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
152
 
153
  .game-board {
 
169
  align-items: center;
170
  border-radius: 8px;
171
  font-size: 30px;
172
+ background: rgba(255, 255, 255, 0.85);
173
  transition: all 0.2s;
174
  cursor: pointer;
175
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
176
  }
177
 
178
  .cell:hover {
 
183
  .player {
184
  background: linear-gradient(45deg, #6A67CE, #1A8CD8);
185
  color: white;
186
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
187
  }
188
 
189
  .goal {
190
  background: linear-gradient(45deg, #FDD85D, #FFB347);
191
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
192
  }
193
 
194
  .obstacle {
195
  background: linear-gradient(45deg, #FF7C7C, #FF5252);
196
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
197
  }
198
 
199
+ .star {
200
+ background: linear-gradient(45deg, #FFD700, #FFA500);
201
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
202
+ }
203
+
204
+ .portal {
205
+ background: linear-gradient(45deg, #9B5DE5, #6A0DAD);
206
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
207
  }
208
 
209
  .game-controls {
 
235
  }
236
 
237
  .score-board {
238
+ background: rgba(255, 255, 255, 0.9);
239
  border-radius: 15px;
240
  padding: 15px;
241
  text-align: center;
 
243
  font-size: 24px;
244
  margin: 20px auto;
245
  width: 300px;
246
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
247
+ }
248
+
249
+ .level-indicator {
250
+ background: rgba(255, 255, 255, 0.9);
251
+ border-radius: 15px;
252
+ padding: 10px 20px;
253
+ text-align: center;
254
+ font-family: 'Fredoka One', cursive;
255
+ font-size: 20px;
256
+ margin: 10px auto;
257
+ width: 200px;
258
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
259
  }
260
 
261
+ .concept-highlight {
262
+ background: rgba(253, 216, 93, 0.2);
263
+ border-radius: 10px;
264
+ padding: 15px;
265
+ margin: 10px 0;
266
+ border-left: 4px solid var(--accent);
267
+ }
268
+
269
+ .code-block {
270
+ background: #2b2d42;
271
+ color: #f8f9fa;
272
+ border-radius: 15px;
273
+ padding: 20px;
274
+ font-family: 'Courier New', monospace;
275
+ overflow-x: auto;
276
+ margin: 20px 0;
277
+ box-shadow: 0 8px 16px rgba(0,0,0,0.2);
278
+ }
279
+
280
+ .code-explanation {
281
+ background: rgba(255, 255, 255, 0.9);
282
+ border-radius: 15px;
283
+ padding: 15px;
284
+ margin: 15px 0;
285
+ }
286
+
287
+ .game-animation {
288
+ text-align: center;
289
+ margin: 20px 0;
290
+ }
291
+
292
+ .animated-char {
293
+ font-size: 80px;
294
+ display: inline-block;
295
+ animation: bounce 2s infinite;
296
+ }
297
+
298
+ @keyframes bounce {
299
+ 0%, 100% {transform: translateY(0);}
300
+ 50% {transform: translateY(-20px);}
301
+ }
302
+
303
  @media (max-width: 768px) {
304
  .tabs {
305
  flex-wrap: wrap;
306
  }
307
 
308
+ .game-board {
309
+ width: 90vw;
310
+ height: 90vw;
311
  }
312
 
313
  .control-btn {
 
315
  height: 60px;
316
  font-size: 24px;
317
  }
318
+ }
319
+
320
+ .typewriter h2 {
321
+ overflow: hidden;
322
+ border-right: .15em solid var(--accent);
323
+ white-space: nowrap;
324
+ margin: 0 auto;
325
+ letter-spacing: .15em;
326
+ animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite;
327
+ }
328
+
329
+ @keyframes typing {
330
+ from { width: 0 }
331
+ to { width: 100% }
332
+ }
333
+
334
+ @keyframes blink-caret {
335
+ from, to { border-color: transparent }
336
+ 50% { border-color: var(--accent); }
337
  }
338
  </style>
339
  """, unsafe_allow_html=True)
 
363
  if 'player_char' not in st.session_state:
364
  st.session_state.player_char = "🦸"
365
  if 'goal_char' not in st.session_state:
366
+ st.session_state.goal_char = "🏁"
367
  if 'obstacle_char' not in st.session_state:
368
  st.session_state.obstacle_char = "🪨"
369
+ if 'current_level' not in st.session_state:
370
+ st.session_state.current_level = 1
371
+ if 'total_levels' not in st.session_state:
372
+ st.session_state.total_levels = 3
373
+ if 'show_concept_animation' not in st.session_state:
374
+ st.session_state.show_concept_animation = False
375
+ if 'concept_animation' not in st.session_state:
376
+ st.session_state.concept_animation = None
377
+ if 'pygame_running' not in st.session_state:
378
+ st.session_state.pygame_running = False
379
+ if 'pygame_output' not in st.session_state:
380
+ st.session_state.pygame_output = ""
381
 
382
  # Concept database
383
  CONCEPTS = {
 
385
  "name": "Loop",
386
  "emoji": "🔄",
387
  "description": "Loops repeat actions multiple times",
388
+ "example": "for i in range(5):\n print('Hello!')",
389
  "color": "#6A67CE",
390
+ "game_example": "Repeating jumps to cross a river",
391
+ "animation": "🔄➰➿"
392
  },
393
  "conditional": {
394
  "name": "Conditional",
395
  "emoji": "❓",
396
  "description": "Conditionals make decisions in code",
397
+ "example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
398
  "color": "#FF7C7C",
399
+ "game_example": "Choosing paths based on obstacles",
400
+ "animation": "❓👉👈"
401
  },
402
  "function": {
403
  "name": "Function",
404
  "emoji": "✨",
405
  "description": "Functions are reusable blocks of code",
406
+ "example": "def greet(name):\n print(f'Hello {name}!')",
407
  "color": "#FDD85D",
408
+ "game_example": "Creating a jump function used multiple times",
409
+ "animation": "✨🌟💫"
410
  },
411
  "variable": {
412
  "name": "Variable",
413
  "emoji": "📦",
414
  "description": "Variables store information",
415
+ "example": "score = 10\nplayer = 'Alex'",
416
  "color": "#1A8CD8",
417
+ "game_example": "Keeping track of collected stars",
418
+ "animation": "📊📈📉"
419
  },
420
  "list": {
421
  "name": "List",
422
  "emoji": "📝",
423
  "description": "Lists store collections of items",
424
+ "example": "fruits = ['apple', 'banana', 'orange']",
425
  "color": "#4CAF50",
426
+ "game_example": "Storing collected treasures in a backpack",
427
+ "animation": "📋📑📄"
428
  }
429
  }
430
 
 
630
  ax.plot(x, y, 's', markersize=15, color=obstacle_color)
631
  ax.text(x, y+0.4, 'Obstacle', ha='center', color='white', fontsize=8)
632
 
633
+ # Stars
634
+ for i in range(3):
635
+ x = random.uniform(2.5, 7.5)
636
+ y = random.uniform(1.2, 2.8)
637
+ ax.plot(x, y, '*', markersize=15, color='yellow')
638
+ ax.text(x, y+0.4, 'Star', ha='center', color='white', fontsize=8)
639
+
640
  # Path
641
  ax.plot([2, 8], [2, 2], 'y--', linewidth=1, alpha=0.5)
642
 
 
652
  st.error(f"Preview generation error: {str(e)}")
653
  return None
654
 
655
+ def reset_game_state():
656
+ st.session_state.game_state = {
657
+ "player_pos": [0, 0],
658
+ "goal_pos": [7, 7],
659
+ "obstacles": [[2, 2], [3, 4], [5, 3], [4, 6], [6, 5]],
660
+ "stars": [[1, 1], [3, 3], [5, 5], [7, 1]],
661
+ "score": 0,
662
+ "moves": 0,
663
+ "game_over": False,
664
+ "portals": {
665
+ "A": {"in": [6, 6], "out": [1, 7]},
666
+ "B": {"in": [0, 3], "out": [7, 3]}
667
+ }
668
+ }
669
+
670
  # Handle player movement
671
  def move_player(direction):
672
  """Update player position based on movement direction"""
 
674
  player_pos = state["player_pos"]
675
  goal_pos = state["goal_pos"]
676
  obstacles = state["obstacles"]
677
+ stars = state["stars"]
678
+ portals = state["portals"]
679
 
680
  new_pos = player_pos.copy()
681
 
 
695
  state["player_pos"] = new_pos
696
  state["moves"] += 1
697
 
698
+ # Check for portal
699
+ for portal, positions in portals.items():
700
+ if new_pos == positions["in"]:
701
+ state["player_pos"] = positions["out"].copy()
702
+ state["moves"] += 1 # Count portal as a move
703
+ break
704
+
705
  # Check for goal collision
706
  if new_pos == goal_pos:
707
+ state["score"] += 20
708
  state["game_over"] = True
709
  # Check for star collection
710
+ elif new_pos in stars:
711
  state["score"] += 5
712
+ state["stars"].remove(new_pos)
713
 
714
  st.session_state.game_state = state
715
 
 
722
  player_pos = state["player_pos"]
723
  goal_pos = state["goal_pos"]
724
  obstacles = state["obstacles"]
725
+ stars = state["stars"]
726
+ portals = state["portals"]
727
+
728
+ # Level indicator
729
+ st.markdown(f"""
730
+ <div class="level-indicator">
731
+ Level: {st.session_state.current_level}/{st.session_state.total_levels}
732
+ </div>
733
+ """, unsafe_allow_html=True)
734
 
735
  # Score board
736
  st.markdown(f"""
 
748
  for col in range(8):
749
  pos = [row, col]
750
  cell_class = "cell"
751
+ cell_content = ""
752
 
753
  if pos == player_pos:
754
  cell_class += " player"
755
  cell_content = st.session_state.player_char
756
  elif pos == goal_pos:
757
  cell_class += " goal"
758
+ cell_content = st.session_state.goal_char
759
  elif pos in obstacles:
760
  cell_class += " obstacle"
761
  cell_content = st.session_state.obstacle_char
762
+ elif pos in stars:
763
+ cell_class += " star"
764
  cell_content = "⭐"
765
  else:
766
+ # Check for portals
767
+ for portal, positions in portals.items():
768
+ if pos == positions["in"] or pos == positions["out"]:
769
+ cell_class += " portal"
770
+ cell_content = "🌀"
771
+ break
772
 
773
  with cols[col]:
774
  st.markdown(f"<div class='{cell_class}'>{cell_content}</div>", unsafe_allow_html=True)
 
791
  # Reset button
792
  st.button("🔄 Reset Game", on_click=reset_game_state, use_container_width=True)
793
 
794
+ # Next level button
795
  if state["game_over"]:
796
  st.balloons()
797
  st.success(f"🎉 You won! Final Score: {state['score']} in {state['moves']} moves!")
798
+ if st.session_state.current_level < st.session_state.total_levels:
799
+ if st.button("Next Level →", use_container_width=True):
800
+ st.session_state.current_level += 1
801
+ reset_game_state()
802
+ st.rerun()
803
+
804
+ # Run PyGame code in the browser
805
+ def run_pygame_in_browser(code):
806
+ """Simulate running PyGame code with output capture"""
807
+ st.subheader("🎮 Play the Full Game in Your Browser")
808
+
809
+ # Create a code runner
810
+ output = StringIO()
811
+ with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output):
812
+ try:
813
+ # Create a fake pygame module for simulation
814
+ class FakePygame:
815
+ def __init__(self):
816
+ self.quit = lambda: None
817
+ self.init = lambda: None
818
+ self.display = type('', (), {'set_mode': lambda *a, **kw: None, 'set_caption': lambda *a: None, 'flip': lambda: None})()
819
+ self.event = type('', (), {'get': lambda: []})()
820
+ self.key = type('', (), {'get_pressed': lambda: [0]*300})()
821
+ self.Rect = lambda *a: None
822
+ self.draw = type('', (), {'rect': lambda *a, **kw: None, 'circle': lambda *a, **kw: None})()
823
+ self.font = type('', (), {'SysFont': lambda *a, **kw: type('', (), {'render': lambda *a, **kw: None})})()
824
+ self.time = type('', (), {'Clock': lambda: type('', (), {'tick': lambda *a: None})()})()
825
+ self.Surface = lambda *a: type('', (), {'blit': lambda *a, **kw: None})()
826
+
827
+ sys.modules['pygame'] = FakePygame()
828
+
829
+ # Execute the code
830
+ exec(code, {'__name__': '__main__'})
831
+ st.session_state.pygame_output = "Game executed successfully!"
832
+ except Exception as e:
833
+ st.session_state.pygame_output = f"Error: {str(e)}\n{traceback.format_exc()}"
834
+
835
+ # Show output
836
+ st.code(st.session_state.pygame_output, language='python')
837
+ st.info("This is a simulation. To play the full game, download the code and run it on your computer!")
838
+
839
+ # Display concept animation
840
+ def show_concept_animation(concept):
841
+ """Display animated concept visualization"""
842
+ if concept in CONCEPTS:
843
+ st.subheader(f"{CONCEPTS[concept]['emoji']} {CONCEPTS[concept]['name']} Concept")
844
+
845
+ # Animation
846
+ st.markdown(f"<div class='game-animation'><span class='animated-char'>{CONCEPTS[concept]['animation']}</span></div>",
847
+ unsafe_allow_html=True)
848
+
849
+ # Explanation
850
+ st.markdown(f"""
851
+ <div class='concept-highlight'>
852
+ <p><strong>How it works:</strong> {CONCEPTS[concept]['description']}</p>
853
+ <p><strong>In your game:</strong> {CONCEPTS[concept]['game_example']}</p>
854
+ </div>
855
+ """, unsafe_allow_html=True)
856
+
857
+ # Code example
858
+ st.code(CONCEPTS[concept]['example'], language='python')
859
 
860
  # Main application function
861
  def main():
862
  init_session_state()
863
 
864
+ # Sidebar for API key and settings
865
  with st.sidebar:
866
  st.header("⚙️ Settings")
867
  st.session_state.groq_api_key = st.text_input(
 
894
  index=0
895
  )
896
 
897
+ st.divider()
898
+
899
+ st.header("📚 Learning Level")
900
+ st.session_state.current_level = st.slider(
901
+ "Select Difficulty Level",
902
+ 1, 5, st.session_state.current_level
903
+ )
904
+
905
  st.divider()
906
  st.caption("Made with ❤️ for kids learning to code")
907
+ st.caption("v4.0 | StoryCoder")
908
 
909
  st.title("🎮 StoryCoder - Play & Learn Coding!")
910
+ st.markdown("<div class='typewriter'><h2>Turn stories into games, and games into coding skills!</h2></div>",
911
+ unsafe_allow_html=True)
912
 
913
  # Create tabs
914
  st.markdown('<div class="tabs">', unsafe_allow_html=True)
 
926
  if st.button("💻 Game Code", use_container_width=True):
927
  st.session_state.active_tab = "code"
928
  with col5:
929
+ if st.button("🚀 PyGame", use_container_width=True):
930
+ st.session_state.active_tab = "pygame"
 
 
 
 
 
 
 
931
  st.markdown('</div>', unsafe_allow_html=True)
932
 
933
  # Story creation tab
 
944
  key="story_input"
945
  )
946
 
947
+ if st.button("Create Game!", use_container_width=True):
948
  if len(story) < 10:
949
  st.error("Your story needs to be at least 10 characters long!")
950
  else:
 
1040
  if not st.session_state.concepts:
1041
  st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'collect'.")
1042
  else:
1043
+ # Concept selector
1044
+ selected_concept = st.selectbox(
1045
+ "Select a concept to explore:",
1046
+ st.session_state.concepts,
1047
+ format_func=lambda x: CONCEPTS[x]["name"]
1048
+ )
1049
+
1050
+ # Show animation and explanation for selected concept
1051
+ if selected_concept:
1052
+ show_concept_animation(selected_concept)
 
 
 
 
1053
 
1054
  if st.button("See the Game Code", use_container_width=True):
1055
  st.session_state.active_tab = "code"
 
1094
  GOAL_COLOR = (253, 216, 93) # Yellow
1095
  OBSTACLE_COLOR = (255, 124, 124) # Coral
1096
  TEXT_COLOR = (45, 50, 80) # Dark blue
1097
+ STAR_COLOR = (255, 215, 0) # Gold
1098
 
1099
  # Player setup
1100
  player_size = 40
 
1121
  random.randint(20, 50)
1122
  ])
1123
 
1124
+ # List concept: Creating stars
1125
+ stars = []
1126
+ for i in range(5):
1127
+ stars.append([
1128
+ random.randint(100, WIDTH - 100),
1129
+ random.randint(50, HEIGHT - 100),
1130
+ 20
1131
+ ])
1132
+
1133
  # Game loop
1134
  running = True
1135
  while running:
 
1160
  # Conditional concept: Check for collision
1161
  if player_rect.colliderect(goal_rect):
1162
  # Function concept: Increase score
1163
+ score += 10
1164
  # Move goal to new position
1165
  goal_x = random.randint(100, WIDTH - 100)
1166
  goal_y = random.randint(50, HEIGHT - 100)
1167
 
1168
+ # Collision detection with stars
1169
+ for star in stars[:]:
1170
+ star_rect = pygame.Rect(star[0], star[1], star[2], star[2])
1171
+ if player_rect.colliderect(star_rect):
1172
+ score += 5
1173
+ stars.remove(star)
1174
+
1175
  # Drawing
1176
  screen.fill(BACKGROUND)
1177
 
 
1180
  pygame.draw.rect(screen, OBSTACLE_COLOR,
1181
  (obstacle[0], obstacle[1], obstacle[2], obstacle[3]))
1182
 
1183
+ # Draw stars
1184
+ for star in stars:
1185
+ pygame.draw.circle(screen, STAR_COLOR,
1186
+ (star[0] + star[2]//2, star[1] + star[2]//2), star[2]//2)
1187
+
1188
  # Draw player and goal
1189
  pygame.draw.rect(screen, PLAYER_COLOR,
1190
  (player_x, player_y, player_size, player_size))
 
1204
  screen.blit(concepts_text, (20, HEIGHT - 40))
1205
 
1206
  # Display instructions
1207
+ help_text = font.render("Arrow keys to move - Collect stars and reach the goal!", True, TEXT_COLOR)
1208
  screen.blit(help_text, (WIDTH // 2 - 200, HEIGHT - 80))
1209
 
1210
  # Update display
 
1227
  mime="text/python",
1228
  use_container_width=True
1229
  )
1230
+
1231
+ # Code explanation
1232
+ st.subheader("🧠 Code Explanation")
1233
+ st.markdown("""
1234
+ <div class="code-explanation">
1235
+ <p>This code creates a game based on your story:</p>
1236
+ <ul>
1237
+ <li><strong>Variables:</strong> Used to track score and positions</li>
1238
+ <li><strong>Conditionals:</strong> Check collisions and game rules</li>
1239
+ <li><strong>Functions:</strong> pygame functions create the game mechanics</li>
1240
+ <li><strong>Loops:</strong> The game loop runs continuously</li>
1241
+ <li><strong>Lists:</strong> Store obstacles and collectible stars</li>
1242
+ </ul>
1243
+ </div>
1244
+ """, unsafe_allow_html=True)
1245
  else:
1246
  st.warning("No game code generated yet!")
1247
 
1248
+ # Try PyGame button
1249
+ if st.session_state.story and st.session_state.concepts:
1250
+ if st.button("▶️ Try PyGame in Browser", use_container_width=True):
1251
+ st.session_state.active_tab = "pygame"
1252
+ st.session_state.pygame_running = True
1253
+ st.rerun()
 
 
 
 
 
 
 
1254
 
1255
  if st.button("Create Another Story!", use_container_width=True):
1256
  st.session_state.active_tab = "story"
1257
  st.rerun()
1258
+
1259
+ # PyGame tab
1260
+ elif st.session_state.active_tab == "pygame":
1261
+ st.header("🎮 Play PyGame in Browser")
1262
+
1263
+ if st.session_state.story and st.session_state.concepts:
1264
+ run_pygame_in_browser(st.session_state.game_code)
1265
+ else:
1266
+ st.warning("No game code generated yet!")
1267
+
1268
+ if st.button("Back to Game Code", use_container_width=True):
1269
+ st.session_state.active_tab = "code"
1270
+ st.rerun()
1271
 
1272
  if __name__ == "__main__":
1273
  main()