sunbal7 commited on
Commit
5af1252
·
verified ·
1 Parent(s): 7d0616b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +589 -266
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - Enhanced Version with New Design
2
  import streamlit as st
3
  import random
4
  import re
@@ -10,6 +10,7 @@ from PIL import Image
10
  import io
11
  import matplotlib.pyplot as plt
12
  import numpy as np
 
13
 
14
  # Configure Streamlit page
15
  st.set_page_config(
@@ -19,7 +20,15 @@ st.set_page_config(
19
  initial_sidebar_state="expanded"
20
  )
21
 
22
- # Custom CSS with white background
 
 
 
 
 
 
 
 
23
  st.markdown("""
24
  <style>
25
  @import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Comic+Neue:wght@700&display=swap');
@@ -29,107 +38,140 @@ st.markdown("""
29
  --secondary: #9370DB;
30
  --accent: #FFD700;
31
  --dark: #4B0082;
32
- --light: #FFFFFF;
33
  --game-blue: #6A5ACD;
34
  --game-purple: #9400D3;
 
 
 
35
  }
36
 
37
  body {
38
- background: #FFFFFF;
 
 
39
  font-family: 'Comic Neue', cursive;
40
  margin: 0;
41
  padding: 0;
42
  min-height: 100vh;
43
  }
44
 
 
 
 
 
 
 
45
  .stApp {
46
- background: #FFFFFF;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  .game-card {
50
- background: rgba(255, 255, 255, 0.95);
51
- border-radius: 15px;
52
- padding: 20px;
53
- box-shadow: 0 4px 16px rgba(138, 43, 226, 0.1);
54
- border: 1px solid #E0E0E0;
55
- margin-bottom: 20px;
56
  transition: all 0.3s;
57
  }
58
 
59
  .game-card:hover {
60
- transform: translateY(-3px);
61
- box-shadow: 0 8px 16px rgba(138, 43, 226, 0.15);
62
  }
63
 
64
  .header {
65
  color: var(--dark);
66
  font-family: 'Fredoka One', cursive;
 
67
  }
68
 
69
  .concept-card {
70
- background: rgba(255, 255, 255, 0.95);
71
- border-radius: 12px;
72
  padding: 15px;
73
  margin: 10px 0;
74
- border-left: 4px solid var(--accent);
75
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
76
  }
77
 
78
  .stButton>button {
79
  background: linear-gradient(45deg, var(--primary), var(--game-purple));
80
  color: white;
81
- border-radius: 12px;
82
- padding: 10px 24px;
83
  font-weight: bold;
84
- font-size: 16px;
85
  border: none;
86
  transition: all 0.3s;
87
  font-family: 'Fredoka One', cursive;
88
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
89
  }
90
 
91
  .stButton>button:hover {
92
- transform: scale(1.03);
93
- box-shadow: 0 6px 12px rgba(138, 43, 226, 0.2);
94
  }
95
 
96
  .stTextInput>div>div>input {
97
- border-radius: 12px;
98
- padding: 12px;
99
- border: 2px solid var(--accent);
100
- font-size: 16px;
101
- background: rgba(255, 255, 255, 0.95);
102
  }
103
 
104
  .tabs {
105
  display: flex;
106
- gap: 8px;
107
  margin-bottom: 20px;
108
  overflow-x: auto;
109
- background: rgba(255, 255, 255, 0.95);
110
- padding: 8px;
111
- border-radius: 12px;
112
- border: 1px solid #E0E0E0;
113
  }
114
 
115
  .tab {
116
- padding: 10px 20px;
117
- background: rgba(255, 255, 255, 0.95);
118
- border-radius: 12px;
119
  cursor: pointer;
120
  font-weight: bold;
121
  white-space: nowrap;
122
  font-family: 'Fredoka One', cursive;
123
- font-size: 14px;
124
  transition: all 0.3s;
125
  color: var(--dark);
126
- border: 1px solid #E0E0E0;
127
  }
128
 
129
  .tab.active {
130
  background: linear-gradient(45deg, var(--primary), var(--game-purple));
131
  color: white;
132
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
133
  }
134
 
135
  .game-board {
@@ -137,134 +179,132 @@ st.markdown("""
137
  grid-template-columns: repeat(8, 1fr);
138
  grid-template-rows: repeat(8, 1fr);
139
  gap: 4px;
140
- width: 450px;
141
- height: 450px;
142
  margin: 0 auto;
143
- background: #FFFFFF;
144
  padding: 10px;
145
- border-radius: 12px;
146
- border: 1px solid #E0E0E0;
147
  }
148
 
149
  .cell {
150
  display: flex;
151
  justify-content: center;
152
  align-items: center;
153
- border-radius: 6px;
154
- font-size: 28px;
155
- background: #FFFFFF;
156
  transition: all 0.2s;
157
  cursor: pointer;
158
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
159
- border: 1px solid #F0F0F0;
160
  }
161
 
162
  .cell:hover {
163
- transform: scale(1.03);
164
- background: #F8F8F8;
165
  }
166
 
167
  .player {
168
  background: linear-gradient(45deg, var(--primary), var(--game-blue));
169
  color: white;
170
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
171
  }
172
 
173
  .goal {
174
  background: linear-gradient(45deg, var(--accent), #FFA500);
175
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
176
  }
177
 
178
  .obstacle {
179
  background: linear-gradient(45deg, var(--secondary), #8A2BE2);
180
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
181
  }
182
 
183
  .star {
184
  background: linear-gradient(45deg, #FFD700, #FFA500);
185
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
186
  }
187
 
188
  .portal {
189
  background: linear-gradient(45deg, #9B5DE5, #6A0DAD);
190
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
191
  }
192
 
193
  .game-controls {
194
  display: flex;
195
  justify-content: center;
196
- gap: 12px;
197
- margin: 20px 0;
198
  }
199
 
200
  .control-btn {
201
- width: 60px;
202
- height: 60px;
203
  border-radius: 50%;
204
  background: linear-gradient(45deg, var(--primary), var(--game-purple));
205
  color: white;
206
  display: flex;
207
  align-items: center;
208
  justify-content: center;
209
- font-size: 24px;
210
  cursor: pointer;
211
  border: none;
212
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
213
  transition: all 0.2s;
214
  }
215
 
216
  .control-btn:hover {
217
- transform: scale(1.08);
218
- box-shadow: 0 6px 12px rgba(0,0,0,0.15);
219
  }
220
 
221
  .score-board {
222
- background: #FFFFFF;
223
- border-radius: 12px;
224
- padding: 12px;
225
  text-align: center;
226
  font-family: 'Fredoka One', cursive;
227
- font-size: 20px;
228
- margin: 15px auto;
229
- width: 280px;
230
- box-shadow: 0 4px 8px rgba(0,0,0,0.05);
231
- border: 1px solid #E0E0E0;
232
  }
233
 
234
  .level-indicator {
235
- background: #FFFFFF;
236
- border-radius: 12px;
237
- padding: 8px 16px;
238
  text-align: center;
239
  font-family: 'Fredoka One', cursive;
240
- font-size: 18px;
241
- margin: 8px auto;
242
- width: 180px;
243
- box-shadow: 0 2px 6px rgba(0,0,0,0.05);
244
- border: 1px solid #E0E0E0;
245
  }
246
 
247
  .theme-customizer {
248
- background: #FFFFFF;
249
- border-radius: 15px;
250
- padding: 15px;
251
- margin: 15px 0;
252
- box-shadow: 0 4px 12px rgba(138, 43, 226, 0.1);
253
- border: 1px solid #E0E0E0;
254
  }
255
 
256
  .customizer-title {
257
  text-align: center;
258
  font-family: 'Fredoka One', cursive;
259
  color: var(--primary);
260
- margin-bottom: 15px;
261
- font-size: 20px;
262
  }
263
 
264
  .char-preview {
265
  text-align: center;
266
- font-size: 40px;
267
- margin: 8px 0;
268
  }
269
 
270
  @media (max-width: 768px) {
@@ -273,83 +313,82 @@ st.markdown("""
273
  }
274
 
275
  .game-board {
276
- width: 85vw;
277
- height: 85vw;
278
  }
279
 
280
  .control-btn {
281
- width: 50px;
282
- height: 50px;
283
- font-size: 20px;
284
  }
285
  }
286
 
287
- .concept-buttons {
288
- display: flex;
289
- flex-wrap: wrap;
290
- gap: 10px;
291
- margin: 15px 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  }
293
 
294
  .concept-btn {
295
- flex: 1 0 calc(33.333% - 10px);
296
- min-width: 120px;
 
297
  padding: 12px;
298
- border-radius: 10px;
299
- background: rgba(255, 255, 255, 0.95);
300
- border: 1px solid #E0E0E0;
301
- box-shadow: 0 2px 6px rgba(0,0,0,0.05);
302
  transition: all 0.3s;
 
 
 
 
303
  text-align: center;
304
- cursor: pointer;
305
  }
306
 
307
  .concept-btn:hover {
308
- transform: translateY(-3px);
309
- box-shadow: 0 4px 8px rgba(138, 43, 226, 0.15);
310
- border-color: var(--primary);
311
- }
312
-
313
- .concept-btn.active {
314
- background: linear-gradient(45deg, var(--primary), var(--game-purple));
315
- color: white;
316
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
317
  }
318
 
319
  .example-box {
320
- border: 1px solid #E0E0E0;
321
- border-radius: 12px;
322
  padding: 15px;
 
323
  margin-bottom: 15px;
324
  transition: all 0.3s;
325
- cursor: pointer;
326
  }
327
 
328
  .example-box:hover {
329
  transform: translateY(-3px);
330
- box-shadow: 0 4px 12px rgba(138, 43, 226, 0.1);
331
- border-color: var(--primary);
332
- }
333
-
334
- .toast {
335
- position: fixed;
336
- bottom: 20px;
337
- left: 50%;
338
- transform: translateX(-50%);
339
- background: rgba(0, 0, 0, 0.8);
340
- color: white;
341
- padding: 10px 20px;
342
- border-radius: 25px;
343
- z-index: 1000;
344
- font-family: 'Fredoka One', cursive;
345
- animation: fadeInOut 2s ease-in-out;
346
- }
347
-
348
- @keyframes fadeInOut {
349
- 0% { opacity: 0; bottom: 0; }
350
- 20% { opacity: 1; bottom: 20px; }
351
- 80% { opacity: 1; bottom: 20px; }
352
- 100% { opacity: 0; bottom: 0; }
353
  }
354
  </style>
355
  """, unsafe_allow_html=True)
@@ -366,6 +405,8 @@ def init_session_state():
366
  st.session_state.game_code = ""
367
  if 'game_explanation' not in st.session_state:
368
  st.session_state.game_explanation = ""
 
 
369
  if 'active_tab' not in st.session_state:
370
  st.session_state.active_tab = "story"
371
  if 'loading' not in st.session_state:
@@ -384,8 +425,18 @@ def init_session_state():
384
  st.session_state.total_levels = 3
385
  if 'selected_concept' not in st.session_state:
386
  st.session_state.selected_concept = None
387
- if 'toast_message' not in st.session_state:
388
- st.session_state.toast_message = None
 
 
 
 
 
 
 
 
 
 
389
 
390
  # Concept database
391
  CONCEPTS = {
@@ -475,7 +526,7 @@ Characters:
475
  - Villain: A character that creates obstacles (if applicable)
476
 
477
  Game Mechanics:
478
- 1. Move your character using arrow keys or buttons
479
  2. Collect stars while avoiding obstacles
480
  3. Reach the goal to win
481
  4. Helper characters appear to teach {concept_list}
@@ -487,7 +538,7 @@ Coding Concepts: This game teaches {concept_list} through:
487
  - Tracking progress with variables
488
  - Managing collections with lists
489
 
490
- Visual Style: Clean and colorful game world with simple characters.
491
  """
492
 
493
  # Generate game code explanation
@@ -509,6 +560,80 @@ As you play the game, think about:
509
  The code brings your story to life in a fun game world!
510
  """
511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  def reset_game_state():
513
  st.session_state.game_state = {
514
  "player_pos": [0, 0],
@@ -548,27 +673,32 @@ def move_player(direction):
548
  # Check if new position is valid
549
  if new_pos != player_pos:
550
  # Check for obstacle collision
551
- if new_pos not in obstacles:
552
- state["player_pos"] = new_pos
553
- state["moves"] += 1
554
-
555
- # Check for portal
556
- for portal, positions in portals.items():
557
- if new_pos == positions["in"]:
558
- state["player_pos"] = positions["out"].copy()
559
- state["moves"] += 1 # Count portal as a move
560
- break
561
-
562
- # Check for goal collision
563
- if new_pos == goal_pos:
564
- state["score"] += 20
565
- state["game_over"] = True
566
- st.session_state.toast_message = "🎉 You reached the goal! Well done!"
567
- # Check for star collection
568
- elif new_pos in stars:
569
- state["score"] += 5
570
- state["stars"].remove(new_pos)
571
- st.session_state.toast_message = "⭐ You collected a star! +5 points"
 
 
 
 
 
572
 
573
  st.session_state.game_state = state
574
 
@@ -652,15 +782,56 @@ def create_playable_game():
652
 
653
  # Next level button
654
  if state["game_over"]:
 
 
655
  if st.session_state.current_level < st.session_state.total_levels:
656
  if st.button("Next Level →", use_container_width=True):
657
  st.session_state.current_level += 1
658
  reset_game_state()
659
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
660
 
661
  # Theme customizer section
662
  def theme_customizer():
663
  """Theme customization section"""
 
 
 
 
 
 
 
 
 
 
664
  with st.container():
665
  st.markdown("<div class='theme-customizer'>", unsafe_allow_html=True)
666
  st.markdown("<div class='customizer-title'>🎨 Customize Your Game Theme</div>", unsafe_allow_html=True)
@@ -672,7 +843,7 @@ def theme_customizer():
672
  ["🦸", "👨‍🚀", "🧙‍♂️", "🐱", "🐉", "🦊"],
673
  index=0
674
  )
675
- st.markdown(f"<div class='char-preview'>{st.session_state.player_char}</div>", unsafe_allow_html=True)
676
 
677
  with col2:
678
  st.session_state.goal_char = st.selectbox(
@@ -680,7 +851,7 @@ def theme_customizer():
680
  ["🏁", "🏰", "🚩", "🎯", "🔑"],
681
  index=0
682
  )
683
- st.markdown(f"<div class='char-preview'>{st.session_state.goal_char}</div>", unsafe_allow_html=True)
684
 
685
  with col3:
686
  st.session_state.obstacle_char = st.selectbox(
@@ -688,31 +859,41 @@ def theme_customizer():
688
  ["🪨", "🌵", "🔥", "🌊", "🌳"],
689
  index=0
690
  )
691
- st.markdown(f"<div class='char-preview'>{st.session_state.obstacle_char}</div>", unsafe_allow_html=True)
692
 
693
- # Level selector with visible options
694
- st.session_state.current_level = st.select_slider(
695
  "Select Difficulty Level",
696
- options=[1, 2, 3, 4, 5],
697
- value=st.session_state.current_level
698
  )
699
 
700
  st.markdown("</div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
701
 
702
- # Show toast message
703
- def show_toast():
704
- """Show a toast message if one exists"""
705
- if st.session_state.toast_message:
706
- st.markdown(f"<div class='toast'>{st.session_state.toast_message}</div>", unsafe_allow_html=True)
707
- # Clear message after showing
708
- st.session_state.toast_message = None
 
 
709
 
710
  # Main application function
711
  def main():
712
  init_session_state()
713
 
714
  st.title("🎮 StoryCoder - Play & Learn Coding!")
715
- st.markdown("Turn stories into games, and games into coding skills!",
716
  unsafe_allow_html=True)
717
 
718
  # Theme customizer
@@ -724,18 +905,19 @@ def main():
724
  with col1:
725
  if st.button("📖 Create Story", use_container_width=True):
726
  st.session_state.active_tab = "story"
 
727
  with col2:
728
- if st.button("🎮 Play Game", use_container_width=True,
729
- disabled=not st.session_state.story or st.session_state.loading):
730
  st.session_state.active_tab = "game"
 
731
  with col3:
732
- if st.button("🔍 Concepts", use_container_width=True,
733
- disabled=not st.session_state.concepts or st.session_state.loading):
734
  st.session_state.active_tab = "concepts"
 
735
  with col4:
736
- if st.button("💻 Game Code", use_container_width=True,
737
- disabled=not st.session_state.game_code or st.session_state.loading):
738
  st.session_state.active_tab = "code"
 
739
  with col5:
740
  if st.button("🔄 New Story", use_container_width=True):
741
  st.session_state.story = ""
@@ -743,60 +925,69 @@ def main():
743
  st.session_state.game_scenario = ""
744
  st.session_state.game_code = ""
745
  st.session_state.game_explanation = ""
 
746
  st.session_state.active_tab = "story"
747
  reset_game_state()
748
- st.session_state.toast_message = "✨ New story started! Create your adventure."
749
  st.markdown('</div>', unsafe_allow_html=True)
750
 
751
  # Story creation tab
752
  if st.session_state.active_tab == "story":
753
  with st.container():
754
  st.header("📖 Create Your Story")
 
755
 
756
  # Show examples
757
  st.subheader("✨ Story Examples")
758
  col1, col2, col3 = st.columns(3)
759
- examples = {
760
- "Space Explorer": "An astronaut needs to collect 3 stars while avoiding asteroids in space.",
761
- "Jungle Adventure": "A monkey swings through trees to collect bananas before sunset.",
762
- "Dragon Quest": "A dragon flies through clouds to collect magic crystals in a mystical land."
 
 
 
 
 
 
 
 
 
763
  }
764
 
765
  with col1:
 
766
  with st.container():
767
- st.markdown('<div class="example-box">', unsafe_allow_html=True)
768
- st.subheader("🚀 Space Explorer")
769
- st.code(examples["Space Explorer"], language="text")
770
  if st.button("Copy Story", key="copy_space", use_container_width=True):
771
- st.session_state.story = examples["Space Explorer"]
772
- st.session_state.toast_message = "🚀 Space story copied to clipboard!"
773
- st.markdown('</div>', unsafe_allow_html=True)
774
 
775
  with col2:
 
776
  with st.container():
777
- st.markdown('<div class="example-box">', unsafe_allow_html=True)
778
- st.subheader("🌿 Jungle Adventure")
779
- st.code(examples["Jungle Adventure"], language="text")
780
  if st.button("Copy Story", key="copy_jungle", use_container_width=True):
781
- st.session_state.story = examples["Jungle Adventure"]
782
- st.session_state.toast_message = "🌿 Jungle story copied to clipboard!"
783
- st.markdown('</div>', unsafe_allow_html=True)
784
 
785
  with col3:
 
786
  with st.container():
787
- st.markdown('<div class="example-box">', unsafe_allow_html=True)
788
- st.subheader("🐉 Dragon Quest")
789
- st.code(examples["Dragon Quest"], language="text")
790
  if st.button("Copy Story", key="copy_dragon", use_container_width=True):
791
- st.session_state.story = examples["Dragon Quest"]
792
- st.session_state.toast_message = "🐉 Dragon story copied to clipboard!"
793
- st.markdown('</div>', unsafe_allow_html=True)
794
-
795
- st.write("Write a short story (2-5 sentences) and I'll turn it into a playable game!")
796
 
797
  story = st.text_area(
798
  "Your story:",
799
- height=150,
800
  placeholder="Once upon a time, a brave knight had to collect 5 magical stars in a castle...",
801
  value=st.session_state.story,
802
  key="story_input"
@@ -808,6 +999,7 @@ def main():
808
  else:
809
  st.session_state.story = story
810
  st.session_state.loading = True
 
811
 
812
  with st.spinner("🧠 Analyzing your story for coding concepts..."):
813
  st.session_state.concepts = analyze_story(story)
@@ -822,33 +1014,12 @@ def main():
822
  story, st.session_state.concepts, st.session_state.game_scenario
823
  )
824
 
825
- # Generate simple game code
826
- keywords = re.findall(r'\b\w{4,}\b', st.session_state.story)[:3]
827
- player_char = keywords[0].capitalize() if keywords else "Hero"
828
- collect_item = keywords[1] if len(keywords) > 1 else "star"
829
- obstacle = keywords[2] if len(keywords) > 2 else "rock"
830
-
831
- # Get concept emojis
832
- concept_emojis = "".join([CONCEPTS[c]['emoji'] for c in st.session_state.concepts])
833
-
834
- game_code = f"""
835
- # {player_char}'s Adventure: {st.session_state.story[:20]}...
836
- # Teaches: {concept_emojis} {", ".join([CONCEPTS[c]['name'] for c in st.session_state.concepts])}
837
-
838
- import pygame
839
- import random
840
- import sys
841
-
842
- # Game setup and code would go here...
843
- # This is a simplified representation of the actual game code
844
- """
845
-
846
- st.session_state.game_code = game_code
847
 
848
  reset_game_state()
849
  st.session_state.active_tab = "game"
850
  st.session_state.loading = False
851
- st.session_state.toast_message = "🎮 Game created successfully! Time to play."
852
  st.rerun()
853
 
854
  # Game tab
@@ -864,8 +1035,17 @@ import sys
864
  st.subheader("🌟 Game Scenario")
865
  st.markdown(f'<div class="game-card">{st.session_state.game_scenario}</div>', unsafe_allow_html=True)
866
 
 
 
 
 
 
 
 
 
867
  # Playable game
868
  create_playable_game()
 
869
 
870
  # Game explanation
871
  st.subheader("📚 How This Game Teaches Coding")
@@ -873,6 +1053,7 @@ import sys
873
 
874
  if st.button("Learn Coding Concepts", use_container_width=True):
875
  st.session_state.active_tab = "concepts"
 
876
  st.rerun()
877
 
878
  # Concepts tab
@@ -883,22 +1064,15 @@ import sys
883
  if not st.session_state.concepts:
884
  st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'collect'.")
885
  else:
886
- # Concept buttons instead of dropdown
887
- st.markdown("<div class='concept-buttons'>", unsafe_allow_html=True)
888
-
889
- for concept in st.session_state.concepts:
890
- details = CONCEPTS[concept]
891
- is_active = st.session_state.selected_concept == concept
892
- btn_class = "concept-btn active" if is_active else "concept-btn"
893
-
894
- if st.button(
895
- f"{details['emoji']} {details['name']}",
896
- key=f"concept_{concept}",
897
- use_container_width=True
898
- ):
899
- st.session_state.selected_concept = concept if not is_active else None
900
-
901
- st.markdown("</div>", unsafe_allow_html=True)
902
 
903
  # Show explanation for selected concept
904
  if st.session_state.selected_concept:
@@ -914,11 +1088,10 @@ import sys
914
  <pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
915
  </div>
916
  """, unsafe_allow_html=True)
917
- else:
918
- st.info("👆 Click on a concept button above to learn more about it")
919
 
920
  if st.button("See the Game Code", use_container_width=True):
921
  st.session_state.active_tab = "code"
 
922
  st.rerun()
923
 
924
  # Code tab
@@ -926,15 +1099,169 @@ import sys
926
  st.header("💻 Game Code")
927
  st.write("Here's the Python code for your game. Download it and run on your computer!")
928
 
929
- if st.session_state.game_code:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
930
  # Display code with syntax highlighting
931
  st.subheader("Your Game Code")
932
- st.code(st.session_state.game_code, language="python")
933
 
934
  # Download button
935
  st.download_button(
936
  label="📥 Download Game Code",
937
- data=st.session_state.game_code,
938
  file_name="story_game.py",
939
  mime="text/python",
940
  use_container_width=True
@@ -942,18 +1269,16 @@ import sys
942
 
943
  # Code explanation
944
  st.subheader("🧠 Code Explanation")
945
- st.markdown(f"""
946
  <div class="game-card">
947
- <p>This code creates a game based on your story: <em>{st.session_state.story[:50]}...</em></p>
948
- <p>Key programming concepts used:</p>
949
  <ul>
950
  <li><strong>Variables:</strong> Used to track score and positions</li>
951
  <li><strong>Conditionals:</strong> Check collisions and game rules</li>
952
- <li><strong>Functions:</strong> Organize game mechanics into reusable blocks</li>
953
  <li><strong>Loops:</strong> The game loop runs continuously</li>
954
  <li><strong>Lists:</strong> Store obstacles and collectible stars</li>
955
  </ul>
956
- <p>To run this game, you'll need to install PyGame and Python on your computer.</p>
957
  </div>
958
  """, unsafe_allow_html=True)
959
  else:
@@ -961,10 +1286,8 @@ import sys
961
 
962
  if st.button("Create Another Story!", use_container_width=True):
963
  st.session_state.active_tab = "story"
 
964
  st.rerun()
965
-
966
- # Show toast messages
967
- show_toast()
968
 
969
  if __name__ == "__main__":
970
  main()
 
1
+ # app.py - Enhanced Version with Improved UX
2
  import streamlit as st
3
  import random
4
  import re
 
10
  import io
11
  import matplotlib.pyplot as plt
12
  import numpy as np
13
+ from streamlit.components.v1 import html
14
 
15
  # Configure Streamlit page
16
  st.set_page_config(
 
20
  initial_sidebar_state="expanded"
21
  )
22
 
23
+ # Sound effects
24
+ SOUND_EFFECTS = {
25
+ "click": "data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQoGAACBhYqNkJSZnJ+ipqmtr7K0t7m8vsDCxcfKzM/Q0tTW2Nvd3+Dh4+Xn6evt7/Dx8/T19/j5+vv8/f7/AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+//8=",
26
+ "star": "data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQoGAACBhYqNkJSZnJ+ipqmtr7K0t7m8vsDCxcfKzM/Q0tTW2Nvd3+Dh4+Xn6evt7/Dx8/T19/j5+vv8/f7/AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+//8=",
27
+ "obstacle": "data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQoGAACBhYqNkJSZnJ+ipqmtr7K0t7m8vsDCxcfKzM/Q0tTW2Nvd3+Dh4+Xn6evt7/Dx8/T19/j5+vv8/f7/AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+//8=",
28
+ "goal": "data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQoGAACBhYqNkJSZnJ+ipqmtr7K0t7m8vsDCxcfKzM/Q0tTW2Nvd3+Dh4+Xn6evt7/Dx8/T19/j5+vv8/f7/AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+//8="
29
+ }
30
+
31
+ # Custom CSS with light purple background and wave design
32
  st.markdown("""
33
  <style>
34
  @import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Comic+Neue:wght@700&display=swap');
 
38
  --secondary: #9370DB;
39
  --accent: #FFD700;
40
  --dark: #4B0082;
41
+ --light: #E6E6FA;
42
  --game-blue: #6A5ACD;
43
  --game-purple: #9400D3;
44
+ --gradient-start: #E6E6FA;
45
+ --gradient-mid: #D8BFD8;
46
+ --gradient-end: #DDA0DD;
47
  }
48
 
49
  body {
50
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-mid), var(--gradient-end));
51
+ background-size: 400% 400%;
52
+ animation: gradientBG 15s ease infinite;
53
  font-family: 'Comic Neue', cursive;
54
  margin: 0;
55
  padding: 0;
56
  min-height: 100vh;
57
  }
58
 
59
+ @keyframes gradientBG {
60
+ 0% {background-position: 0% 50%;}
61
+ 50% {background-position: 100% 50%;}
62
+ 100% {background-position: 0% 50%;}
63
+ }
64
+
65
  .stApp {
66
+ background: url('https://www.transparenttextures.com/patterns/always-grey.png');
67
+ background-color: rgba(230, 230, 250, 0.9);
68
+ backdrop-filter: blur(5px);
69
+ -webkit-backdrop-filter: blur(5px);
70
+ }
71
+
72
+ .wave-divider {
73
+ width: 100%;
74
+ height: 150px;
75
+ overflow: hidden;
76
+ margin: -10px 0 20px 0;
77
+ transform: rotate(180deg);
78
+ }
79
+
80
+ .wave-divider svg {
81
+ height: 100%;
82
+ width: 100%;
83
+ }
84
+
85
+ .wave-divider path {
86
+ stroke: none;
87
+ fill: var(--primary);
88
+ opacity: 0.2;
89
  }
90
 
91
  .game-card {
92
+ background: rgba(255, 255, 255, 0.85);
93
+ border-radius: 20px;
94
+ padding: 25px;
95
+ box-shadow: 0 8px 32px rgba(138, 43, 226, 0.2);
96
+ border: 2px solid rgba(255, 255, 255, 0.5);
97
+ margin-bottom: 25px;
98
  transition: all 0.3s;
99
  }
100
 
101
  .game-card:hover {
102
+ transform: translateY(-5px);
103
+ box-shadow: 0 12px 24px rgba(138, 43, 226, 0.3);
104
  }
105
 
106
  .header {
107
  color: var(--dark);
108
  font-family: 'Fredoka One', cursive;
109
+ text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.8);
110
  }
111
 
112
  .concept-card {
113
+ background: rgba(255, 255, 255, 0.9);
114
+ border-radius: 15px;
115
  padding: 15px;
116
  margin: 10px 0;
117
+ border-left: 5px solid var(--accent);
118
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
119
  }
120
 
121
  .stButton>button {
122
  background: linear-gradient(45deg, var(--primary), var(--game-purple));
123
  color: white;
124
+ border-radius: 50px;
125
+ padding: 12px 28px;
126
  font-weight: bold;
127
+ font-size: 18px;
128
  border: none;
129
  transition: all 0.3s;
130
  font-family: 'Fredoka One', cursive;
131
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
132
  }
133
 
134
  .stButton>button:hover {
135
+ transform: scale(1.05);
136
+ box-shadow: 0 8px 16px rgba(138, 43, 226, 0.4);
137
  }
138
 
139
  .stTextInput>div>div>input {
140
+ border-radius: 20px;
141
+ padding: 14px;
142
+ border: 3px solid var(--accent);
143
+ font-size: 18px;
144
+ background: rgba(255, 255, 255, 0.9);
145
  }
146
 
147
  .tabs {
148
  display: flex;
149
+ gap: 10px;
150
  margin-bottom: 20px;
151
  overflow-x: auto;
152
+ background: rgba(255, 255, 255, 0.3);
153
+ padding: 10px;
154
+ border-radius: 20px;
155
+ backdrop-filter: blur(5px);
156
  }
157
 
158
  .tab {
159
+ padding: 12px 24px;
160
+ background: rgba(255, 255, 255, 0.4);
161
+ border-radius: 15px;
162
  cursor: pointer;
163
  font-weight: bold;
164
  white-space: nowrap;
165
  font-family: 'Fredoka One', cursive;
166
+ font-size: 16px;
167
  transition: all 0.3s;
168
  color: var(--dark);
 
169
  }
170
 
171
  .tab.active {
172
  background: linear-gradient(45deg, var(--primary), var(--game-purple));
173
  color: white;
174
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
175
  }
176
 
177
  .game-board {
 
179
  grid-template-columns: repeat(8, 1fr);
180
  grid-template-rows: repeat(8, 1fr);
181
  gap: 4px;
182
+ width: 500px;
183
+ height: 500px;
184
  margin: 0 auto;
185
+ background: rgba(147, 112, 219, 0.2);
186
  padding: 10px;
187
+ border-radius: 15px;
 
188
  }
189
 
190
  .cell {
191
  display: flex;
192
  justify-content: center;
193
  align-items: center;
194
+ border-radius: 8px;
195
+ font-size: 30px;
196
+ background: rgba(255, 255, 255, 0.85);
197
  transition: all 0.2s;
198
  cursor: pointer;
199
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 
200
  }
201
 
202
  .cell:hover {
203
+ transform: scale(1.05);
204
+ background: rgba(255, 255, 255, 1);
205
  }
206
 
207
  .player {
208
  background: linear-gradient(45deg, var(--primary), var(--game-blue));
209
  color: white;
210
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
211
  }
212
 
213
  .goal {
214
  background: linear-gradient(45deg, var(--accent), #FFA500);
215
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
216
  }
217
 
218
  .obstacle {
219
  background: linear-gradient(45deg, var(--secondary), #8A2BE2);
220
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
221
  }
222
 
223
  .star {
224
  background: linear-gradient(45deg, #FFD700, #FFA500);
225
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
226
  }
227
 
228
  .portal {
229
  background: linear-gradient(45deg, #9B5DE5, #6A0DAD);
230
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
231
  }
232
 
233
  .game-controls {
234
  display: flex;
235
  justify-content: center;
236
+ gap: 15px;
237
+ margin: 25px 0;
238
  }
239
 
240
  .control-btn {
241
+ width: 70px;
242
+ height: 70px;
243
  border-radius: 50%;
244
  background: linear-gradient(45deg, var(--primary), var(--game-purple));
245
  color: white;
246
  display: flex;
247
  align-items: center;
248
  justify-content: center;
249
+ font-size: 30px;
250
  cursor: pointer;
251
  border: none;
252
+ box-shadow: 0 6px 12px rgba(0,0,0,0.2);
253
  transition: all 0.2s;
254
  }
255
 
256
  .control-btn:hover {
257
+ transform: scale(1.1);
258
+ box-shadow: 0 8px 16px rgba(0,0,0,0.3);
259
  }
260
 
261
  .score-board {
262
+ background: rgba(255, 255, 255, 0.9);
263
+ border-radius: 15px;
264
+ padding: 15px;
265
  text-align: center;
266
  font-family: 'Fredoka One', cursive;
267
+ font-size: 24px;
268
+ margin: 20px auto;
269
+ width: 300px;
270
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
271
+ border: 2px solid var(--primary);
272
  }
273
 
274
  .level-indicator {
275
+ background: rgba(255, 255, 255, 0.9);
276
+ border-radius: 15px;
277
+ padding: 10px 20px;
278
  text-align: center;
279
  font-family: 'Fredoka One', cursive;
280
+ font-size: 20px;
281
+ margin: 10px auto;
282
+ width: 200px;
283
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
284
+ border: 2px solid var(--accent);
285
  }
286
 
287
  .theme-customizer {
288
+ background: rgba(255, 255, 255, 0.9);
289
+ border-radius: 20px;
290
+ padding: 20px;
291
+ margin: 20px 0;
292
+ box-shadow: 0 8px 16px rgba(138, 43, 226, 0.2);
293
+ border: 3px solid var(--primary);
294
  }
295
 
296
  .customizer-title {
297
  text-align: center;
298
  font-family: 'Fredoka One', cursive;
299
  color: var(--primary);
300
+ margin-bottom: 20px;
301
+ font-size: 24px;
302
  }
303
 
304
  .char-preview {
305
  text-align: center;
306
+ font-size: 48px;
307
+ margin: 10px 0;
308
  }
309
 
310
  @media (max-width: 768px) {
 
313
  }
314
 
315
  .game-board {
316
+ width: 90vw;
317
+ height: 90vw;
318
  }
319
 
320
  .control-btn {
321
+ width: 60px;
322
+ height: 60px;
323
+ font-size: 24px;
324
  }
325
  }
326
 
327
+ .typewriter h2 {
328
+ overflow: hidden;
329
+ border-right: .15em solid var(--accent);
330
+ white-space: nowrap;
331
+ margin: 0 auto;
332
+ letter-spacing: .15em;
333
+ animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite;
334
+ color: var(--dark);
335
+ font-family: 'Fredoka One', cursive;
336
+ }
337
+
338
+ @keyframes typing {
339
+ from { width: 0 }
340
+ to { width: 100% }
341
+ }
342
+
343
+ @keyframes blink-caret {
344
+ from, to { border-color: transparent }
345
+ 50% { border-color: var(--accent); }
346
+ }
347
+
348
+ .floating {
349
+ animation: floating 3s ease-in-out infinite;
350
+ }
351
+
352
+ @keyframes floating {
353
+ 0% { transform: translate(0, 0px); }
354
+ 50% { transform: translate(0, 15px); }
355
+ 100% { transform: translate(0, -0px); }
356
  }
357
 
358
  .concept-btn {
359
+ background: linear-gradient(45deg, var(--primary), var(--game-purple));
360
+ color: white;
361
+ border-radius: 15px;
362
  padding: 12px;
363
+ font-weight: bold;
364
+ font-size: 16px;
365
+ border: none;
 
366
  transition: all 0.3s;
367
+ font-family: 'Fredoka One', cursive;
368
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
369
+ margin: 5px;
370
+ width: 100%;
371
  text-align: center;
 
372
  }
373
 
374
  .concept-btn:hover {
375
+ transform: scale(1.05);
376
+ box-shadow: 0 8px 16px rgba(138, 43, 226, 0.4);
 
 
 
 
 
 
 
377
  }
378
 
379
  .example-box {
380
+ background: rgba(255, 255, 255, 0.85);
381
+ border-radius: 15px;
382
  padding: 15px;
383
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
384
  margin-bottom: 15px;
385
  transition: all 0.3s;
386
+ border: 2px solid rgba(138, 43, 226, 0.2);
387
  }
388
 
389
  .example-box:hover {
390
  transform: translateY(-3px);
391
+ box-shadow: 0 8px 16px rgba(138, 43, 226, 0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  }
393
  </style>
394
  """, unsafe_allow_html=True)
 
405
  st.session_state.game_code = ""
406
  if 'game_explanation' not in st.session_state:
407
  st.session_state.game_explanation = ""
408
+ if 'game_preview' not in st.session_state:
409
+ st.session_state.game_preview = None
410
  if 'active_tab' not in st.session_state:
411
  st.session_state.active_tab = "story"
412
  if 'loading' not in st.session_state:
 
425
  st.session_state.total_levels = 3
426
  if 'selected_concept' not in st.session_state:
427
  st.session_state.selected_concept = None
428
+ if 'sound_played' not in st.session_state:
429
+ st.session_state.sound_played = False
430
+
431
+ # Play sound function
432
+ def play_sound(sound_type):
433
+ audio_html = f"""
434
+ <audio autoplay>
435
+ <source src="{SOUND_EFFECTS[sound_type]}" type="audio/wav">
436
+ Your browser does not support the audio element.
437
+ </audio>
438
+ """
439
+ st.markdown(audio_html, unsafe_allow_html=True)
440
 
441
  # Concept database
442
  CONCEPTS = {
 
526
  - Villain: A character that creates obstacles (if applicable)
527
 
528
  Game Mechanics:
529
+ 1. Move your character using arrow keys
530
  2. Collect stars while avoiding obstacles
531
  3. Reach the goal to win
532
  4. Helper characters appear to teach {concept_list}
 
538
  - Tracking progress with variables
539
  - Managing collections with lists
540
 
541
+ Visual Description: Colorful game world with cartoon-style characters, vibrant landscapes, and magical effects.
542
  """
543
 
544
  # Generate game code explanation
 
560
  The code brings your story to life in a fun game world!
561
  """
562
 
563
+ # Generate game preview visualization
564
+ def generate_game_preview(story):
565
+ """Generate a visual preview of the game"""
566
+ try:
567
+ # Extract keywords for theme
568
+ theme = "space" if "space" in story.lower() else "jungle" if "jungle" in story.lower() else "fantasy"
569
+
570
+ # Create a simple visualization
571
+ fig, ax = plt.subplots(figsize=(10, 6))
572
+
573
+ if theme == "space":
574
+ bg_color = '#0B0B2B'
575
+ player_color = '#8A2BE2'
576
+ goal_color = '#FFD700'
577
+ obstacle_color = '#9370DB'
578
+ title = "Space Adventure"
579
+ elif theme == "jungle":
580
+ bg_color = '#143D2C'
581
+ player_color = '#8A2BE2'
582
+ goal_color = '#FFD700'
583
+ obstacle_color = '#6A5ACD'
584
+ title = "Jungle Adventure"
585
+ else:
586
+ bg_color = '#3A015C'
587
+ player_color = '#8A2BE2'
588
+ goal_color = '#FFD700'
589
+ obstacle_color = '#9370DB'
590
+ title = "Fantasy Quest"
591
+
592
+ ax.set_facecolor(bg_color)
593
+ ax.set_xlim(0, 10)
594
+ ax.set_ylim(0, 6)
595
+
596
+ # Draw game elements
597
+ ax.text(5, 5, title, fontsize=20, ha='center', color='white')
598
+ ax.plot([1, 9], [1, 1], 'w-', linewidth=2) # Ground
599
+
600
+ # Player character
601
+ ax.plot(2, 2, 'o', markersize=15, color=player_color)
602
+ ax.text(2, 2.7, 'You', ha='center', color='white', fontsize=12)
603
+
604
+ # Goal
605
+ ax.plot(8, 2, 'o', markersize=12, color=goal_color)
606
+ ax.text(8, 2.7, 'Goal', ha='center', color='white', fontsize=12)
607
+
608
+ # Obstacles
609
+ for i in range(3):
610
+ x = random.uniform(3, 7)
611
+ y = random.uniform(1.5, 2.5)
612
+ ax.plot(x, y, 's', markersize=15, color=obstacle_color)
613
+ ax.text(x, y+0.4, 'Obstacle', ha='center', color='white', fontsize=8)
614
+
615
+ # Stars
616
+ for i in range(3):
617
+ x = random.uniform(2.5, 7.5)
618
+ y = random.uniform(1.2, 2.8)
619
+ ax.plot(x, y, '*', markersize=15, color='yellow')
620
+ ax.text(x, y+0.4, 'Star', ha='center', color='white', fontsize=8)
621
+
622
+ # Path
623
+ ax.plot([2, 8], [2, 2], 'y--', linewidth=1, alpha=0.5)
624
+
625
+ ax.axis('off')
626
+ ax.set_title("Game Preview", fontsize=16, color='white')
627
+
628
+ # Save to bytes
629
+ buf = io.BytesIO()
630
+ plt.savefig(buf, format='png', dpi=100, bbox_inches='tight', facecolor=bg_color)
631
+ buf.seek(0)
632
+ return buf
633
+ except Exception as e:
634
+ st.error(f"Preview generation error: {str(e)}")
635
+ return None
636
+
637
  def reset_game_state():
638
  st.session_state.game_state = {
639
  "player_pos": [0, 0],
 
673
  # Check if new position is valid
674
  if new_pos != player_pos:
675
  # Check for obstacle collision
676
+ if new_pos in obstacles:
677
+ st.session_state.sound_played = "obstacle"
678
+ return
679
+
680
+ state["player_pos"] = new_pos
681
+ state["moves"] += 1
682
+ st.session_state.sound_played = "click"
683
+
684
+ # Check for portal
685
+ for portal, positions in portals.items():
686
+ if new_pos == positions["in"]:
687
+ state["player_pos"] = positions["out"].copy()
688
+ state["moves"] += 1 # Count portal as a move
689
+ st.session_state.sound_played = "click"
690
+ break
691
+
692
+ # Check for goal collision
693
+ if new_pos == goal_pos:
694
+ state["score"] += 20
695
+ state["game_over"] = True
696
+ st.session_state.sound_played = "goal"
697
+ # Check for star collection
698
+ elif new_pos in stars:
699
+ state["score"] += 5
700
+ state["stars"].remove(new_pos)
701
+ st.session_state.sound_played = "star"
702
 
703
  st.session_state.game_state = state
704
 
 
782
 
783
  # Next level button
784
  if state["game_over"]:
785
+ st.balloons()
786
+ st.success(f"🎉 You won! Final Score: {state['score']} in {state['moves']} moves!")
787
  if st.session_state.current_level < st.session_state.total_levels:
788
  if st.button("Next Level →", use_container_width=True):
789
  st.session_state.current_level += 1
790
  reset_game_state()
791
  st.rerun()
792
+
793
+ # Play sound if needed
794
+ if st.session_state.sound_played:
795
+ play_sound(st.session_state.sound_played)
796
+ st.session_state.sound_played = False
797
+
798
+ # Add keyboard controls
799
+ def add_keyboard_controls():
800
+ js = """
801
+ <script>
802
+ document.addEventListener('keydown', function(event) {
803
+ const key = event.key;
804
+ const buttons = {
805
+ 'ArrowUp': 'up',
806
+ 'ArrowLeft': 'left',
807
+ 'ArrowDown': 'down',
808
+ 'ArrowRight': 'right'
809
+ };
810
+
811
+ if (buttons[key]) {
812
+ const button = document.querySelector(`button[data-testid="baseButton-${buttons[key]}"]`);
813
+ if (button) {
814
+ button.click();
815
+ }
816
+ }
817
+ });
818
+ </script>
819
+ """
820
+ html(js)
821
 
822
  # Theme customizer section
823
  def theme_customizer():
824
  """Theme customization section"""
825
+ st.markdown("""
826
+ <div class="wave-divider">
827
+ <svg viewBox="0 0 1200 120" preserveAspectRatio="none">
828
+ <path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z" opacity=".25" class="shape-fill"></path>
829
+ <path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z" opacity=".5" class="shape-fill"></path>
830
+ <path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z" class="shape-fill"></path>
831
+ </svg>
832
+ </div>
833
+ """, unsafe_allow_html=True)
834
+
835
  with st.container():
836
  st.markdown("<div class='theme-customizer'>", unsafe_allow_html=True)
837
  st.markdown("<div class='customizer-title'>🎨 Customize Your Game Theme</div>", unsafe_allow_html=True)
 
843
  ["🦸", "👨‍🚀", "🧙‍♂️", "🐱", "🐉", "🦊"],
844
  index=0
845
  )
846
+ st.markdown(f"<div class='char-preview floating'>{st.session_state.player_char}</div>", unsafe_allow_html=True)
847
 
848
  with col2:
849
  st.session_state.goal_char = st.selectbox(
 
851
  ["🏁", "🏰", "🚩", "🎯", "🔑"],
852
  index=0
853
  )
854
+ st.markdown(f"<div class='char-preview floating'>{st.session_state.goal_char}</div>", unsafe_allow_html=True)
855
 
856
  with col3:
857
  st.session_state.obstacle_char = st.selectbox(
 
859
  ["🪨", "🌵", "🔥", "🌊", "🌳"],
860
  index=0
861
  )
862
+ st.markdown(f"<div class='char-preview floating'>{st.session_state.obstacle_char}</div>", unsafe_allow_html=True)
863
 
864
+ st.session_state.current_level = st.slider(
 
865
  "Select Difficulty Level",
866
+ 1, 5, st.session_state.current_level, step=1
 
867
  )
868
 
869
  st.markdown("</div>", unsafe_allow_html=True)
870
+
871
+ st.markdown("""
872
+ <div class="wave-divider">
873
+ <svg viewBox="0 0 1200 120" preserveAspectRatio="none">
874
+ <path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z" opacity=".25" class="shape-fill"></path>
875
+ <path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z" opacity=".5" class="shape-fill"></path>
876
+ <path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z" class="shape-fill"></path>
877
+ </svg>
878
+ </div>
879
+ """, unsafe_allow_html=True)
880
 
881
+ # Copy story to clipboard
882
+ def copy_story(story_text):
883
+ js = f"""
884
+ <script>
885
+ navigator.clipboard.writeText(`{story_text}`);
886
+ alert("Story copied to clipboard!");
887
+ </script>
888
+ """
889
+ html(js)
890
 
891
  # Main application function
892
  def main():
893
  init_session_state()
894
 
895
  st.title("🎮 StoryCoder - Play & Learn Coding!")
896
+ st.markdown("<div class='typewriter'><h2>Turn stories into games, and games into coding skills!</h2></div>",
897
  unsafe_allow_html=True)
898
 
899
  # Theme customizer
 
905
  with col1:
906
  if st.button("📖 Create Story", use_container_width=True):
907
  st.session_state.active_tab = "story"
908
+ play_sound("click")
909
  with col2:
910
+ if st.button("🎮 Play Game", use_container_width=True):
 
911
  st.session_state.active_tab = "game"
912
+ play_sound("click")
913
  with col3:
914
+ if st.button("🔍 Concepts", use_container_width=True):
 
915
  st.session_state.active_tab = "concepts"
916
+ play_sound("click")
917
  with col4:
918
+ if st.button("💻 Game Code", use_container_width=True):
 
919
  st.session_state.active_tab = "code"
920
+ play_sound("click")
921
  with col5:
922
  if st.button("🔄 New Story", use_container_width=True):
923
  st.session_state.story = ""
 
925
  st.session_state.game_scenario = ""
926
  st.session_state.game_code = ""
927
  st.session_state.game_explanation = ""
928
+ st.session_state.game_preview = None
929
  st.session_state.active_tab = "story"
930
  reset_game_state()
931
+ play_sound("click")
932
  st.markdown('</div>', unsafe_allow_html=True)
933
 
934
  # Story creation tab
935
  if st.session_state.active_tab == "story":
936
  with st.container():
937
  st.header("📖 Create Your Story")
938
+ st.write("Write a short story (2-5 sentences) and I'll turn it into a playable game!")
939
 
940
  # Show examples
941
  st.subheader("✨ Story Examples")
942
  col1, col2, col3 = st.columns(3)
943
+ example_stories = {
944
+ "Space Explorer": {
945
+ "text": "An astronaut needs to collect 3 stars while avoiding asteroids in space. The stars are scattered across different planets. Each star gives special powers to the astronaut. The goal is to collect all stars before returning to Earth.",
946
+ "image": "https://images.unsplash.com/photo-1462331940025-496dfbfc7564?w=500"
947
+ },
948
+ "Jungle Adventure": {
949
+ "text": "A monkey swings through trees to collect bananas before sunset. There are dangerous snakes and rivers to avoid. Each banana gives energy to jump higher. Collect 10 bananas to win the game!",
950
+ "image": "https://images.unsplash.com/photo-1546182990-dffeafbe841d?w=500"
951
+ },
952
+ "Dragon Quest": {
953
+ "text": "A dragon flies through clouds to collect magic crystals. The crystals are guarded by wizards and hidden in castles. Each crystal makes the dragon stronger. Collect 5 crystals to become the dragon king!",
954
+ "image": "https://images.unsplash.com/photo-1541414779316-9564c1f85944?w=500"
955
+ }
956
  }
957
 
958
  with col1:
959
+ st.caption("Space Explorer")
960
  with st.container():
961
+ st.image(example_stories["Space Explorer"]["image"],
962
+ use_container_width=True,
963
+ caption="Space Explorer Game")
964
  if st.button("Copy Story", key="copy_space", use_container_width=True):
965
+ st.session_state.story = example_stories["Space Explorer"]["text"]
966
+ copy_story(example_stories["Space Explorer"]["text"])
 
967
 
968
  with col2:
969
+ st.caption("Jungle Adventure")
970
  with st.container():
971
+ st.image(example_stories["Jungle Adventure"]["image"],
972
+ use_container_width=True,
973
+ caption="Jungle Adventure Game")
974
  if st.button("Copy Story", key="copy_jungle", use_container_width=True):
975
+ st.session_state.story = example_stories["Jungle Adventure"]["text"]
976
+ copy_story(example_stories["Jungle Adventure"]["text"])
 
977
 
978
  with col3:
979
+ st.caption("Dragon Quest")
980
  with st.container():
981
+ st.image(example_stories["Dragon Quest"]["image"],
982
+ use_container_width=True,
983
+ caption="Dragon Quest Game")
984
  if st.button("Copy Story", key="copy_dragon", use_container_width=True):
985
+ st.session_state.story = example_stories["Dragon Quest"]["text"]
986
+ copy_story(example_stories["Dragon Quest"]["text"])
 
 
 
987
 
988
  story = st.text_area(
989
  "Your story:",
990
+ height=200,
991
  placeholder="Once upon a time, a brave knight had to collect 5 magical stars in a castle...",
992
  value=st.session_state.story,
993
  key="story_input"
 
999
  else:
1000
  st.session_state.story = story
1001
  st.session_state.loading = True
1002
+ play_sound("click")
1003
 
1004
  with st.spinner("🧠 Analyzing your story for coding concepts..."):
1005
  st.session_state.concepts = analyze_story(story)
 
1014
  story, st.session_state.concepts, st.session_state.game_scenario
1015
  )
1016
 
1017
+ with st.spinner("🖼️ Generating game preview..."):
1018
+ st.session_state.game_preview = generate_game_preview(story)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1019
 
1020
  reset_game_state()
1021
  st.session_state.active_tab = "game"
1022
  st.session_state.loading = False
 
1023
  st.rerun()
1024
 
1025
  # Game tab
 
1035
  st.subheader("🌟 Game Scenario")
1036
  st.markdown(f'<div class="game-card">{st.session_state.game_scenario}</div>', unsafe_allow_html=True)
1037
 
1038
+ # Display game preview
1039
+ st.subheader("🖼️ Game Preview")
1040
+ if st.session_state.game_preview:
1041
+ st.image(st.session_state.game_preview, use_container_width=True)
1042
+ else:
1043
+ st.info("Game preview visualization")
1044
+ st.image("https://images.unsplash.com/photo-1542751110-97427bbecf20?w=500", use_container_width=True)
1045
+
1046
  # Playable game
1047
  create_playable_game()
1048
+ add_keyboard_controls()
1049
 
1050
  # Game explanation
1051
  st.subheader("📚 How This Game Teaches Coding")
 
1053
 
1054
  if st.button("Learn Coding Concepts", use_container_width=True):
1055
  st.session_state.active_tab = "concepts"
1056
+ play_sound("click")
1057
  st.rerun()
1058
 
1059
  # Concepts tab
 
1064
  if not st.session_state.concepts:
1065
  st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'collect'.")
1066
  else:
1067
+ # Concept buttons
1068
+ st.subheader("Select a concept to explore:")
1069
+ cols = st.columns(len(st.session_state.concepts))
1070
+ for i, concept in enumerate(st.session_state.concepts):
1071
+ with cols[i]:
1072
+ if st.button(f"{CONCEPTS[concept]['emoji']} {CONCEPTS[concept]['name']}",
1073
+ key=f"concept_{concept}", use_container_width=True):
1074
+ st.session_state.selected_concept = concept
1075
+ play_sound("click")
 
 
 
 
 
 
 
1076
 
1077
  # Show explanation for selected concept
1078
  if st.session_state.selected_concept:
 
1088
  <pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
1089
  </div>
1090
  """, unsafe_allow_html=True)
 
 
1091
 
1092
  if st.button("See the Game Code", use_container_width=True):
1093
  st.session_state.active_tab = "code"
1094
+ play_sound("click")
1095
  st.rerun()
1096
 
1097
  # Code tab
 
1099
  st.header("💻 Game Code")
1100
  st.write("Here's the Python code for your game. Download it and run on your computer!")
1101
 
1102
+ # Generate simple game code
1103
+ if st.session_state.story and st.session_state.concepts:
1104
+ # Extract keywords from story
1105
+ keywords = re.findall(r'\b\w{4,}\b', st.session_state.story)[:3]
1106
+ player_char = keywords[0].capitalize() if keywords else "Hero"
1107
+ collect_item = keywords[1] if len(keywords) > 1 else "star"
1108
+ obstacle = keywords[2] if len(keywords) > 2 else "rock"
1109
+
1110
+ # Get concept emojis
1111
+ concept_emojis = "".join([CONCEPTS[c]['emoji'] for c in st.session_state.concepts])
1112
+
1113
+ game_code = f"""
1114
+ # {player_char}'s Adventure: {st.session_state.story[:20]}...
1115
+ # Teaches: {concept_emojis} {", ".join([CONCEPTS[c]['name'] for c in st.session_state.concepts])}
1116
+
1117
+ import pygame
1118
+ import random
1119
+ import sys
1120
+
1121
+ # Initialize pygame
1122
+ pygame.init()
1123
+
1124
+ # Game setup
1125
+ WIDTH, HEIGHT = 800, 600
1126
+ screen = pygame.display.set_mode((WIDTH, HEIGHT))
1127
+ pygame.display.set_caption("{player_char}'s Adventure")
1128
+ clock = pygame.time.Clock()
1129
+
1130
+ # Colors
1131
+ BACKGROUND = (230, 230, 250) # Light purple
1132
+ PLAYER_COLOR = (138, 43, 226) # Purple
1133
+ GOAL_COLOR = (255, 215, 0) # Gold
1134
+ OBSTACLE_COLOR = (147, 112, 219) # Medium purple
1135
+ TEXT_COLOR = (75, 0, 130) # Indigo
1136
+ STAR_COLOR = (255, 215, 0) # Gold
1137
+
1138
+ # Player setup
1139
+ player_size = 40
1140
+ player_x = 100
1141
+ player_y = HEIGHT // 2
1142
+ player_speed = 5
1143
+
1144
+ # Goal setup
1145
+ goal_size = 30
1146
+ goal_x = WIDTH - 150
1147
+ goal_y = HEIGHT // 2
1148
+
1149
+ # Variables concept: Tracking score
1150
+ score = 0
1151
+ font = pygame.font.SysFont(None, 36)
1152
+
1153
+ # List concept: Creating obstacles
1154
+ obstacles = []
1155
+ for i in range(5):
1156
+ obstacles.append([
1157
+ random.randint(200, WIDTH - 100),
1158
+ random.randint(50, HEIGHT - 100),
1159
+ random.randint(30, 70),
1160
+ random.randint(20, 50)
1161
+ ])
1162
+
1163
+ # List concept: Creating stars
1164
+ stars = []
1165
+ for i in range(5):
1166
+ stars.append([
1167
+ random.randint(100, WIDTH - 100),
1168
+ random.randint(50, HEIGHT - 100),
1169
+ 20
1170
+ ])
1171
+
1172
+ # Game loop
1173
+ running = True
1174
+ while running:
1175
+ # Event handling
1176
+ for event in pygame.event.get():
1177
+ if event.type == pygame.QUIT:
1178
+ running = False
1179
+
1180
+ # Player movement
1181
+ keys = pygame.key.get_pressed()
1182
+ if keys[pygame.K_UP] or keys[pygame.K_w]:
1183
+ player_y -= player_speed
1184
+ if keys[pygame.K_DOWN] or keys[pygame.K_s]:
1185
+ player_y += player_speed
1186
+ if keys[pygame.K_LEFT] or keys[pygame.K_a]:
1187
+ player_x -= player_speed
1188
+ if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
1189
+ player_x += player_speed
1190
+
1191
+ # Boundary checking
1192
+ player_x = max(0, min(WIDTH - player_size, player_x))
1193
+ player_y = max(0, min(HEIGHT - player_size, player_y))
1194
+
1195
+ # Collision detection with goal
1196
+ player_rect = pygame.Rect(player_x, player_y, player_size, player_size)
1197
+ goal_rect = pygame.Rect(goal_x, goal_y, goal_size, goal_size)
1198
+
1199
+ # Conditional concept: Check for collision
1200
+ if player_rect.colliderect(goal_rect):
1201
+ # Function concept: Increase score
1202
+ score += 10
1203
+ # Move goal to new position
1204
+ goal_x = random.randint(100, WIDTH - 100)
1205
+ goal_y = random.randint(50, HEIGHT - 100)
1206
+
1207
+ # Collision detection with stars
1208
+ for star in stars[:]:
1209
+ star_rect = pygame.Rect(star[0], star[1], star[2], star[2])
1210
+ if player_rect.colliderect(star_rect):
1211
+ score += 5
1212
+ stars.remove(star)
1213
+
1214
+ # Drawing
1215
+ screen.fill(BACKGROUND)
1216
+
1217
+ # Draw obstacles
1218
+ for obstacle in obstacles:
1219
+ pygame.draw.rect(screen, OBSTACLE_COLOR,
1220
+ (obstacle[0], obstacle[1], obstacle[2], obstacle[3]))
1221
+
1222
+ # Draw stars
1223
+ for star in stars:
1224
+ pygame.draw.circle(screen, STAR_COLOR,
1225
+ (star[0] + star[2]//2, star[1] + star[2]//2), star[2]//2)
1226
+
1227
+ # Draw player and goal
1228
+ pygame.draw.rect(screen, PLAYER_COLOR,
1229
+ (player_x, player_y, player_size, player_size))
1230
+ pygame.draw.circle(screen, GOAL_COLOR,
1231
+ (goal_x + goal_size//2, goal_y + goal_size//2), goal_size//2)
1232
+
1233
+ # Display score
1234
+ score_text = font.render(f"{collect_item.capitalize()}s: {{score}}", True, TEXT_COLOR)
1235
+ screen.blit(score_text, (20, 20))
1236
+
1237
+ # Display story title
1238
+ title_text = font.render(f"{player_char}'s Adventure: {st.session_state.story[:20]}...", True, TEXT_COLOR)
1239
+ screen.blit(title_text, (WIDTH // 2 - 150, 20))
1240
+
1241
+ # Display concepts
1242
+ concepts_text = font.render(f"Teaches: {', '.join([CONCEPTS[c]['name'] for c in st.session_state.concepts])}", True, TEXT_COLOR)
1243
+ screen.blit(concepts_text, (20, HEIGHT - 40))
1244
+
1245
+ # Display instructions
1246
+ help_text = font.render("Arrow keys to move - Collect stars and reach the goal!", True, TEXT_COLOR)
1247
+ screen.blit(help_text, (WIDTH // 2 - 200, HEIGHT - 80))
1248
+
1249
+ # Update display
1250
+ pygame.display.flip()
1251
+ clock.tick(60)
1252
+
1253
+ pygame.quit()
1254
+ sys.exit()
1255
+ """
1256
+
1257
  # Display code with syntax highlighting
1258
  st.subheader("Your Game Code")
1259
+ st.code(game_code, language="python")
1260
 
1261
  # Download button
1262
  st.download_button(
1263
  label="📥 Download Game Code",
1264
+ data=game_code,
1265
  file_name="story_game.py",
1266
  mime="text/python",
1267
  use_container_width=True
 
1269
 
1270
  # Code explanation
1271
  st.subheader("🧠 Code Explanation")
1272
+ st.markdown("""
1273
  <div class="game-card">
1274
+ <p>This code creates a game based on your story:</p>
 
1275
  <ul>
1276
  <li><strong>Variables:</strong> Used to track score and positions</li>
1277
  <li><strong>Conditionals:</strong> Check collisions and game rules</li>
1278
+ <li><strong>Functions:</strong> pygame functions create the game mechanics</li>
1279
  <li><strong>Loops:</strong> The game loop runs continuously</li>
1280
  <li><strong>Lists:</strong> Store obstacles and collectible stars</li>
1281
  </ul>
 
1282
  </div>
1283
  """, unsafe_allow_html=True)
1284
  else:
 
1286
 
1287
  if st.button("Create Another Story!", use_container_width=True):
1288
  st.session_state.active_tab = "story"
1289
+ play_sound("click")
1290
  st.rerun()
 
 
 
1291
 
1292
  if __name__ == "__main__":
1293
  main()