sunbal7 commited on
Commit
154ab4d
ยท
verified ยท
1 Parent(s): d575992

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -286
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - Ultimate Enhanced Version
2
  import streamlit as st
3
  import random
4
  import re
@@ -10,12 +10,6 @@ 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,22 +19,22 @@ 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');
32
 
33
  :root {
34
- --primary: #6A67CE;
35
- --secondary: #FF7C7C;
36
- --accent: #FDD85D;
37
- --dark: #2D3250;
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 {
@@ -60,34 +54,54 @@ st.markdown("""
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);
73
- border: 2px solid rgba(255, 255, 255, 0.18);
74
  margin-bottom: 25px;
75
  transition: all 0.3s;
76
  }
77
 
78
  .game-card:hover {
79
  transform: translateY(-5px);
80
- box-shadow: 0 12px 24px rgba(31, 38, 135, 0.5);
81
  }
82
 
83
  .header {
84
- color: white;
85
  font-family: 'Fredoka One', cursive;
86
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
87
  }
88
 
89
  .concept-card {
90
- background: rgba(255, 255, 255, 0.95);
91
  border-radius: 15px;
92
  padding: 15px;
93
  margin: 10px 0;
@@ -96,7 +110,7 @@ st.markdown("""
96
  }
97
 
98
  .stButton>button {
99
- background: linear-gradient(45deg, var(--primary), var(--game-blue));
100
  color: white;
101
  border-radius: 50px;
102
  padding: 12px 28px;
@@ -110,7 +124,7 @@ st.markdown("""
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,7 +140,7 @@ st.markdown("""
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,7 +148,7 @@ st.markdown("""
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;
@@ -142,11 +156,12 @@ st.markdown("""
142
  font-family: 'Fredoka One', cursive;
143
  font-size: 16px;
144
  transition: all 0.3s;
145
- color: white;
146
  }
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
 
@@ -158,7 +173,7 @@ st.markdown("""
158
  width: 500px;
159
  height: 500px;
160
  margin: 0 auto;
161
- background: rgba(106, 103, 206, 0.2);
162
  padding: 10px;
163
  border-radius: 15px;
164
  }
@@ -181,18 +196,18 @@ st.markdown("""
181
  }
182
 
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
 
@@ -217,7 +232,7 @@ st.markdown("""
217
  width: 70px;
218
  height: 70px;
219
  border-radius: 50%;
220
- background: linear-gradient(45deg, var(--primary), var(--game-blue));
221
  color: white;
222
  display: flex;
223
  align-items: center;
@@ -244,6 +259,7 @@ st.markdown("""
244
  margin: 20px auto;
245
  width: 300px;
246
  box-shadow: 0 4px 8px rgba(0,0,0,0.15);
 
247
  }
248
 
249
  .level-indicator {
@@ -256,48 +272,30 @@ st.markdown("""
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) {
@@ -324,6 +322,8 @@ st.markdown("""
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 {
@@ -335,6 +335,16 @@ st.markdown("""
335
  from, to { border-color: transparent }
336
  50% { border-color: var(--accent); }
337
  }
 
 
 
 
 
 
 
 
 
 
338
  </style>
339
  """, unsafe_allow_html=True)
340
 
@@ -358,8 +368,6 @@ def init_session_state():
358
  st.session_state.loading = False
359
  if 'game_state' not in st.session_state:
360
  reset_game_state()
361
- if 'groq_api_key' not in st.session_state:
362
- st.session_state.groq_api_key = ""
363
  if 'player_char' not in st.session_state:
364
  st.session_state.player_char = "๐Ÿฆธ"
365
  if 'goal_char' not in st.session_state:
@@ -370,14 +378,6 @@ def init_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 = {
@@ -386,57 +386,43 @@ CONCEPTS = {
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
 
431
- # Initialize Groq client
432
- def get_groq_client():
433
- try:
434
- if st.session_state.groq_api_key:
435
- return Groq(api_key=st.session_state.groq_api_key)
436
- return None
437
- except:
438
- return None
439
-
440
  # Analyze story and extract concepts
441
  def analyze_story(story):
442
  """Analyze story and identify programming concepts"""
@@ -466,42 +452,9 @@ def analyze_story(story):
466
 
467
  return list(set(detected_concepts)) if detected_concepts else ["variable", "function"]
468
 
469
- # Generate game scenario using Groq
470
  def generate_game_scenario(story, concepts):
471
  """Generate a game scenario based on the story and concepts"""
472
- try:
473
- client = get_groq_client()
474
- if client:
475
- concept_names = [CONCEPTS[c]['name'] for c in concepts]
476
- concept_list = ", ".join(concept_names)
477
-
478
- system_prompt = (
479
- "You are an expert in creating educational games for children aged 6-12. "
480
- "Create a simple game scenario based on the child's story. "
481
- "The game should teach programming concepts through gameplay. "
482
- "Structure your response with these sections:\n"
483
- "Game Title: ...\n"
484
- "Game Objective: ...\n"
485
- "Characters: ...\n"
486
- "Game Mechanics: ...\n"
487
- "Coding Concepts: Explain how these programming concepts are used: " + concept_list + "\n"
488
- "Visual Description: Describe the game world visually\n"
489
- "Keep it under 200 words and fun for kids."
490
- )
491
-
492
- response = client.chat.completions.create(
493
- model="llama3-70b-8192",
494
- messages=[
495
- {"role": "system", "content": system_prompt},
496
- {"role": "user", "content": story}
497
- ],
498
- temperature=0.8,
499
- max_tokens=500
500
- )
501
- return response.choices[0].message.content
502
- except Exception as e:
503
- st.error(f"Game scenario generation error: {str(e)}")
504
-
505
  # Fallback template
506
  concept_names = [CONCEPTS[c]['name'] for c in concepts]
507
  concept_list = ", ".join(concept_names)
@@ -532,36 +485,6 @@ Visual Description: Colorful game world with cartoon-style characters, vibrant l
532
  # Generate game code explanation
533
  def generate_game_explanation(story, concepts, game_scenario):
534
  """Generate explanation of game code"""
535
- try:
536
- client = get_groq_client()
537
- if client:
538
- concept_names = [CONCEPTS[c]['name'] for c in concepts]
539
- concept_list = ", ".join(concept_names)
540
-
541
- system_prompt = (
542
- "Explain how the game code implements programming concepts in a way "
543
- "a child aged 6-12 can understand. Use simple analogies and relate to the story. "
544
- "Structure your response with:\n"
545
- "Introduction: ...\n"
546
- "Concept 1: ... (with example from the game)\n"
547
- "Concept 2: ... (with example from the game)\n"
548
- "Conclusion: ...\n"
549
- "Keep it under 300 words and engaging for kids."
550
- )
551
-
552
- response = client.chat.completions.create(
553
- model="llama3-70b-8192",
554
- messages=[
555
- {"role": "system", "content": system_prompt},
556
- {"role": "user", "content": f"Story: {story}\nGame Scenario: {game_scenario}\nConcepts: {concept_list}"}
557
- ],
558
- temperature=0.7,
559
- max_tokens=600
560
- )
561
- return response.choices[0].message.content
562
- except Exception as e:
563
- st.error(f"Explanation generation error: {str(e)}")
564
-
565
  # Fallback explanation
566
  concept_explanations = "\n".join(
567
  [f"- {CONCEPTS[c]['name']}: {CONCEPTS[c]['game_example']}" for c in concepts]
@@ -590,21 +513,21 @@ def generate_game_preview(story):
590
 
591
  if theme == "space":
592
  bg_color = '#0B0B2B'
593
- player_color = '#6A67CE'
594
- goal_color = '#FDD85D'
595
- obstacle_color = '#FF7C7C'
596
  title = "Space Adventure"
597
  elif theme == "jungle":
598
  bg_color = '#143D2C'
599
- player_color = '#6A67CE'
600
- goal_color = '#FDD85D'
601
- obstacle_color = '#D35400'
602
  title = "Jungle Adventure"
603
  else:
604
  bg_color = '#3A015C'
605
- player_color = '#6A67CE'
606
- goal_color = '#FDD85D'
607
- obstacle_color = '#FF7C7C'
608
  title = "Fantasy Quest"
609
 
610
  ax.set_facecolor(bg_color)
@@ -801,79 +724,23 @@ def create_playable_game():
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(
868
- "Enter Groq API Key (optional):",
869
- type="password",
870
- help="Get a free key from https://console.groq.com/keys"
871
- )
872
- st.caption("Using Groq API will create more creative and personalized games!")
873
-
874
- st.divider()
875
 
876
- st.header("๐ŸŽฎ Game Theme")
877
  col1, col2, col3 = st.columns(3)
878
  with col1:
879
  st.session_state.player_char = st.selectbox(
@@ -881,35 +748,52 @@ def main():
881
  ["๐Ÿฆธ", "๐Ÿ‘จโ€๐Ÿš€", "๐Ÿง™โ€โ™‚๏ธ", "๐Ÿฑ", "๐Ÿ‰", "๐ŸฆŠ"],
882
  index=0
883
  )
 
 
884
  with col2:
885
  st.session_state.goal_char = st.selectbox(
886
  "Goal Character",
887
  ["๐Ÿ", "๐Ÿฐ", "๐Ÿšฉ", "๐ŸŽฏ", "๐Ÿ”‘"],
888
  index=0
889
  )
 
 
890
  with col3:
891
  st.session_state.obstacle_char = st.selectbox(
892
  "Obstacle Character",
893
  ["๐Ÿชจ", "๐ŸŒต", "๐Ÿ”ฅ", "๐ŸŒŠ", "๐ŸŒณ"],
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)
915
  col1, col2, col3, col4, col5 = st.columns(5)
@@ -926,8 +810,15 @@ def main():
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
@@ -1047,9 +938,20 @@ def main():
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"
@@ -1089,11 +991,11 @@ pygame.display.set_caption("{player_char}'s Adventure")
1089
  clock = pygame.time.Clock()
1090
 
1091
  # Colors
1092
- BACKGROUND = (230, 240, 255) # Light blue
1093
- PLAYER_COLOR = (106, 103, 206) # Purple
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
@@ -1231,7 +1133,7 @@ sys.exit()
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>
@@ -1245,29 +1147,9 @@ sys.exit()
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()
 
1
+ # app.py - Enhanced Version with New Design
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
 
14
  # Configure Streamlit page
15
  st.set_page_config(
 
19
  initial_sidebar_state="expanded"
20
  )
21
 
22
+ # Custom CSS with light purple background and wave design
23
  st.markdown("""
24
  <style>
25
  @import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Comic+Neue:wght@700&display=swap');
26
 
27
  :root {
28
+ --primary: #8A2BE2;
29
+ --secondary: #9370DB;
30
+ --accent: #FFD700;
31
+ --dark: #4B0082;
32
+ --light: #E6E6FA;
33
+ --game-blue: #6A5ACD;
34
+ --game-purple: #9400D3;
35
+ --gradient-start: #E6E6FA;
36
+ --gradient-mid: #D8BFD8;
37
+ --gradient-end: #DDA0DD;
38
  }
39
 
40
  body {
 
54
  }
55
 
56
  .stApp {
57
+ background: url('https://www.transparenttextures.com/patterns/always-grey.png');
58
+ background-color: rgba(230, 230, 250, 0.9);
59
+ backdrop-filter: blur(5px);
60
+ -webkit-backdrop-filter: blur(5px);
61
+ }
62
+
63
+ .wave-divider {
64
+ width: 100%;
65
+ height: 150px;
66
+ overflow: hidden;
67
+ margin: -10px 0 20px 0;
68
+ transform: rotate(180deg);
69
+ }
70
+
71
+ .wave-divider svg {
72
+ height: 100%;
73
+ width: 100%;
74
+ }
75
+
76
+ .wave-divider path {
77
+ stroke: none;
78
+ fill: var(--primary);
79
+ opacity: 0.2;
80
  }
81
 
82
  .game-card {
83
+ background: rgba(255, 255, 255, 0.85);
84
  border-radius: 20px;
85
  padding: 25px;
86
+ box-shadow: 0 8px 32px rgba(138, 43, 226, 0.2);
87
+ border: 2px solid rgba(255, 255, 255, 0.5);
88
  margin-bottom: 25px;
89
  transition: all 0.3s;
90
  }
91
 
92
  .game-card:hover {
93
  transform: translateY(-5px);
94
+ box-shadow: 0 12px 24px rgba(138, 43, 226, 0.3);
95
  }
96
 
97
  .header {
98
+ color: var(--dark);
99
  font-family: 'Fredoka One', cursive;
100
+ text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.8);
101
  }
102
 
103
  .concept-card {
104
+ background: rgba(255, 255, 255, 0.9);
105
  border-radius: 15px;
106
  padding: 15px;
107
  margin: 10px 0;
 
110
  }
111
 
112
  .stButton>button {
113
+ background: linear-gradient(45deg, var(--primary), var(--game-purple));
114
  color: white;
115
  border-radius: 50px;
116
  padding: 12px 28px;
 
124
 
125
  .stButton>button:hover {
126
  transform: scale(1.05);
127
+ box-shadow: 0 8px 16px rgba(138, 43, 226, 0.4);
128
  }
129
 
130
  .stTextInput>div>div>input {
 
140
  gap: 10px;
141
  margin-bottom: 20px;
142
  overflow-x: auto;
143
+ background: rgba(255, 255, 255, 0.3);
144
  padding: 10px;
145
  border-radius: 20px;
146
  backdrop-filter: blur(5px);
 
148
 
149
  .tab {
150
  padding: 12px 24px;
151
+ background: rgba(255, 255, 255, 0.4);
152
  border-radius: 15px;
153
  cursor: pointer;
154
  font-weight: bold;
 
156
  font-family: 'Fredoka One', cursive;
157
  font-size: 16px;
158
  transition: all 0.3s;
159
+ color: var(--dark);
160
  }
161
 
162
  .tab.active {
163
+ background: linear-gradient(45deg, var(--primary), var(--game-purple));
164
+ color: white;
165
  box-shadow: 0 4px 8px rgba(0,0,0,0.2);
166
  }
167
 
 
173
  width: 500px;
174
  height: 500px;
175
  margin: 0 auto;
176
+ background: rgba(147, 112, 219, 0.2);
177
  padding: 10px;
178
  border-radius: 15px;
179
  }
 
196
  }
197
 
198
  .player {
199
+ background: linear-gradient(45deg, var(--primary), var(--game-blue));
200
  color: white;
201
  box-shadow: 0 4px 8px rgba(0,0,0,0.2);
202
  }
203
 
204
  .goal {
205
+ background: linear-gradient(45deg, var(--accent), #FFA500);
206
  box-shadow: 0 4px 8px rgba(0,0,0,0.2);
207
  }
208
 
209
  .obstacle {
210
+ background: linear-gradient(45deg, var(--secondary), #8A2BE2);
211
  box-shadow: 0 4px 8px rgba(0,0,0,0.2);
212
  }
213
 
 
232
  width: 70px;
233
  height: 70px;
234
  border-radius: 50%;
235
+ background: linear-gradient(45deg, var(--primary), var(--game-purple));
236
  color: white;
237
  display: flex;
238
  align-items: center;
 
259
  margin: 20px auto;
260
  width: 300px;
261
  box-shadow: 0 4px 8px rgba(0,0,0,0.15);
262
+ border: 2px solid var(--primary);
263
  }
264
 
265
  .level-indicator {
 
272
  margin: 10px auto;
273
  width: 200px;
274
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
275
+ border: 2px solid var(--accent);
276
  }
277
 
278
+ .theme-customizer {
279
+ background: rgba(255, 255, 255, 0.9);
280
+ border-radius: 20px;
 
 
 
 
 
 
 
 
 
281
  padding: 20px;
 
 
282
  margin: 20px 0;
283
+ box-shadow: 0 8px 16px rgba(138, 43, 226, 0.2);
284
+ border: 3px solid var(--primary);
 
 
 
 
 
 
285
  }
286
 
287
+ .customizer-title {
288
  text-align: center;
289
+ font-family: 'Fredoka One', cursive;
290
+ color: var(--primary);
291
+ margin-bottom: 20px;
292
+ font-size: 24px;
 
 
 
293
  }
294
 
295
+ .char-preview {
296
+ text-align: center;
297
+ font-size: 48px;
298
+ margin: 10px 0;
299
  }
300
 
301
  @media (max-width: 768px) {
 
322
  margin: 0 auto;
323
  letter-spacing: .15em;
324
  animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite;
325
+ color: var(--dark);
326
+ font-family: 'Fredoka One', cursive;
327
  }
328
 
329
  @keyframes typing {
 
335
  from, to { border-color: transparent }
336
  50% { border-color: var(--accent); }
337
  }
338
+
339
+ .floating {
340
+ animation: floating 3s ease-in-out infinite;
341
+ }
342
+
343
+ @keyframes floating {
344
+ 0% { transform: translate(0, 0px); }
345
+ 50% { transform: translate(0, 15px); }
346
+ 100% { transform: translate(0, -0px); }
347
+ }
348
  </style>
349
  """, unsafe_allow_html=True)
350
 
 
368
  st.session_state.loading = False
369
  if 'game_state' not in st.session_state:
370
  reset_game_state()
 
 
371
  if 'player_char' not in st.session_state:
372
  st.session_state.player_char = "๐Ÿฆธ"
373
  if 'goal_char' not in st.session_state:
 
378
  st.session_state.current_level = 1
379
  if 'total_levels' not in st.session_state:
380
  st.session_state.total_levels = 3
 
 
 
 
 
 
 
 
381
 
382
  # Concept database
383
  CONCEPTS = {
 
386
  "emoji": "๐Ÿ”„",
387
  "description": "Loops repeat actions multiple times",
388
  "example": "for i in range(5):\n print('Hello!')",
389
+ "color": "#8A2BE2",
390
+ "game_example": "Repeating jumps to cross a river"
 
391
  },
392
  "conditional": {
393
  "name": "Conditional",
394
  "emoji": "โ“",
395
  "description": "Conditionals make decisions in code",
396
  "example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
397
+ "color": "#9370DB",
398
+ "game_example": "Choosing paths based on obstacles"
 
399
  },
400
  "function": {
401
  "name": "Function",
402
  "emoji": "โœจ",
403
  "description": "Functions are reusable blocks of code",
404
  "example": "def greet(name):\n print(f'Hello {name}!')",
405
+ "color": "#FFD700",
406
+ "game_example": "Creating a jump function used multiple times"
 
407
  },
408
  "variable": {
409
  "name": "Variable",
410
  "emoji": "๐Ÿ“ฆ",
411
  "description": "Variables store information",
412
  "example": "score = 10\nplayer = 'Alex'",
413
+ "color": "#6A5ACD",
414
+ "game_example": "Keeping track of collected stars"
 
415
  },
416
  "list": {
417
  "name": "List",
418
  "emoji": "๐Ÿ“",
419
  "description": "Lists store collections of items",
420
  "example": "fruits = ['apple', 'banana', 'orange']",
421
+ "color": "#9400D3",
422
+ "game_example": "Storing collected treasures in a backpack"
 
423
  }
424
  }
425
 
 
 
 
 
 
 
 
 
 
426
  # Analyze story and extract concepts
427
  def analyze_story(story):
428
  """Analyze story and identify programming concepts"""
 
452
 
453
  return list(set(detected_concepts)) if detected_concepts else ["variable", "function"]
454
 
455
+ # Generate game scenario
456
  def generate_game_scenario(story, concepts):
457
  """Generate a game scenario based on the story and concepts"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
  # Fallback template
459
  concept_names = [CONCEPTS[c]['name'] for c in concepts]
460
  concept_list = ", ".join(concept_names)
 
485
  # Generate game code explanation
486
  def generate_game_explanation(story, concepts, game_scenario):
487
  """Generate explanation of game code"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  # Fallback explanation
489
  concept_explanations = "\n".join(
490
  [f"- {CONCEPTS[c]['name']}: {CONCEPTS[c]['game_example']}" for c in concepts]
 
513
 
514
  if theme == "space":
515
  bg_color = '#0B0B2B'
516
+ player_color = '#8A2BE2'
517
+ goal_color = '#FFD700'
518
+ obstacle_color = '#9370DB'
519
  title = "Space Adventure"
520
  elif theme == "jungle":
521
  bg_color = '#143D2C'
522
+ player_color = '#8A2BE2'
523
+ goal_color = '#FFD700'
524
+ obstacle_color = '#6A5ACD'
525
  title = "Jungle Adventure"
526
  else:
527
  bg_color = '#3A015C'
528
+ player_color = '#8A2BE2'
529
+ goal_color = '#FFD700'
530
+ obstacle_color = '#9370DB'
531
  title = "Fantasy Quest"
532
 
533
  ax.set_facecolor(bg_color)
 
724
  reset_game_state()
725
  st.rerun()
726
 
727
+ # Theme customizer section
728
+ def theme_customizer():
729
+ """Theme customization section"""
730
+ st.markdown("""
731
+ <div class="wave-divider">
732
+ <svg viewBox="0 0 1200 120" preserveAspectRatio="none">
733
+ <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>
734
+ <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>
735
+ <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>
736
+ </svg>
737
+ </div>
738
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
 
740
+ with st.container():
741
+ st.markdown("<div class='theme-customizer'>", unsafe_allow_html=True)
742
+ st.markdown("<div class='customizer-title'>๐ŸŽจ Customize Your Game Theme</div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
743
 
 
744
  col1, col2, col3 = st.columns(3)
745
  with col1:
746
  st.session_state.player_char = st.selectbox(
 
748
  ["๐Ÿฆธ", "๐Ÿ‘จโ€๐Ÿš€", "๐Ÿง™โ€โ™‚๏ธ", "๐Ÿฑ", "๐Ÿ‰", "๐ŸฆŠ"],
749
  index=0
750
  )
751
+ st.markdown(f"<div class='char-preview floating'>{st.session_state.player_char}</div>", unsafe_allow_html=True)
752
+
753
  with col2:
754
  st.session_state.goal_char = st.selectbox(
755
  "Goal Character",
756
  ["๐Ÿ", "๐Ÿฐ", "๐Ÿšฉ", "๐ŸŽฏ", "๐Ÿ”‘"],
757
  index=0
758
  )
759
+ st.markdown(f"<div class='char-preview floating'>{st.session_state.goal_char}</div>", unsafe_allow_html=True)
760
+
761
  with col3:
762
  st.session_state.obstacle_char = st.selectbox(
763
  "Obstacle Character",
764
  ["๐Ÿชจ", "๐ŸŒต", "๐Ÿ”ฅ", "๐ŸŒŠ", "๐ŸŒณ"],
765
  index=0
766
  )
767
+ st.markdown(f"<div class='char-preview floating'>{st.session_state.obstacle_char}</div>", unsafe_allow_html=True)
768
 
 
 
 
769
  st.session_state.current_level = st.slider(
770
  "Select Difficulty Level",
771
  1, 5, st.session_state.current_level
772
  )
773
 
774
+ st.markdown("</div>", unsafe_allow_html=True)
775
+
776
+ st.markdown("""
777
+ <div class="wave-divider">
778
+ <svg viewBox="0 0 1200 120" preserveAspectRatio="none">
779
+ <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>
780
+ <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>
781
+ <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>
782
+ </svg>
783
+ </div>
784
+ """, unsafe_allow_html=True)
785
+
786
+ # Main application function
787
+ def main():
788
+ init_session_state()
789
 
790
  st.title("๐ŸŽฎ StoryCoder - Play & Learn Coding!")
791
  st.markdown("<div class='typewriter'><h2>Turn stories into games, and games into coding skills!</h2></div>",
792
  unsafe_allow_html=True)
793
 
794
+ # Theme customizer
795
+ theme_customizer()
796
+
797
  # Create tabs
798
  st.markdown('<div class="tabs">', unsafe_allow_html=True)
799
  col1, col2, col3, col4, col5 = st.columns(5)
 
810
  if st.button("๐Ÿ’ป Game Code", use_container_width=True):
811
  st.session_state.active_tab = "code"
812
  with col5:
813
+ if st.button("๐Ÿ”„ New Story", use_container_width=True):
814
+ st.session_state.story = ""
815
+ st.session_state.concepts = []
816
+ st.session_state.game_scenario = ""
817
+ st.session_state.game_code = ""
818
+ st.session_state.game_explanation = ""
819
+ st.session_state.game_preview = None
820
+ st.session_state.active_tab = "story"
821
+ reset_game_state()
822
  st.markdown('</div>', unsafe_allow_html=True)
823
 
824
  # Story creation tab
 
938
  format_func=lambda x: CONCEPTS[x]["name"]
939
  )
940
 
941
+ # Show explanation for selected concept
942
  if selected_concept:
943
+ details = CONCEPTS[selected_concept]
944
+ st.markdown(f"""
945
+ <div class="concept-card" style="border-left: 5px solid {details['color']};">
946
+ <div style="display:flex; align-items:center; gap:15px;">
947
+ <span style="font-size:36px;">{details['emoji']}</span>
948
+ <h3 style="color:{details['color']};">{details['name']}</h3>
949
+ </div>
950
+ <p>{details['description']}</p>
951
+ <p><b>In your game:</b> {details['game_example']}</p>
952
+ <pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
953
+ </div>
954
+ """, unsafe_allow_html=True)
955
 
956
  if st.button("See the Game Code", use_container_width=True):
957
  st.session_state.active_tab = "code"
 
991
  clock = pygame.time.Clock()
992
 
993
  # Colors
994
+ BACKGROUND = (230, 230, 250) # Light purple
995
+ PLAYER_COLOR = (138, 43, 226) # Purple
996
+ GOAL_COLOR = (255, 215, 0) # Gold
997
+ OBSTACLE_COLOR = (147, 112, 219) # Medium purple
998
+ TEXT_COLOR = (75, 0, 130) # Indigo
999
  STAR_COLOR = (255, 215, 0) # Gold
1000
 
1001
  # Player setup
 
1133
  # Code explanation
1134
  st.subheader("๐Ÿง  Code Explanation")
1135
  st.markdown("""
1136
+ <div class="game-card">
1137
  <p>This code creates a game based on your story:</p>
1138
  <ul>
1139
  <li><strong>Variables:</strong> Used to track score and positions</li>
 
1147
  else:
1148
  st.warning("No game code generated yet!")
1149
 
 
 
 
 
 
 
 
1150
  if st.button("Create Another Story!", use_container_width=True):
1151
  st.session_state.active_tab = "story"
1152
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
1153
 
1154
  if __name__ == "__main__":
1155
  main()