Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
# app.py -
|
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 |
-
#
|
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: #
|
35 |
-
--secondary: #
|
36 |
-
--accent: #
|
37 |
-
--dark: #
|
38 |
-
--light: #
|
39 |
-
--game-blue: #
|
40 |
-
--game-purple: #
|
41 |
-
--gradient-start: #
|
42 |
-
--gradient-mid: #
|
43 |
-
--gradient-end: #
|
44 |
}
|
45 |
|
46 |
body {
|
@@ -60,34 +54,54 @@ st.markdown("""
|
|
60 |
}
|
61 |
|
62 |
.stApp {
|
63 |
-
background:
|
64 |
-
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
}
|
67 |
|
68 |
.game-card {
|
69 |
-
background: rgba(255, 255, 255, 0.
|
70 |
border-radius: 20px;
|
71 |
padding: 25px;
|
72 |
-
box-shadow: 0 8px 32px rgba(
|
73 |
-
border: 2px solid rgba(255, 255, 255, 0.
|
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(
|
81 |
}
|
82 |
|
83 |
.header {
|
84 |
-
color:
|
85 |
font-family: 'Fredoka One', cursive;
|
86 |
-
text-shadow: 2px 2px 4px rgba(
|
87 |
}
|
88 |
|
89 |
.concept-card {
|
90 |
-
background: rgba(255, 255, 255, 0.
|
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-
|
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(
|
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.
|
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.
|
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:
|
146 |
}
|
147 |
|
148 |
.tab.active {
|
149 |
-
background: linear-gradient(45deg, var(--primary), var(--game-
|
|
|
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(
|
162 |
padding: 10px;
|
163 |
border-radius: 15px;
|
164 |
}
|
@@ -181,18 +196,18 @@ st.markdown("""
|
|
181 |
}
|
182 |
|
183 |
.player {
|
184 |
-
background: linear-gradient(45deg,
|
185 |
color: white;
|
186 |
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
187 |
}
|
188 |
|
189 |
.goal {
|
190 |
-
background: linear-gradient(45deg,
|
191 |
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
192 |
}
|
193 |
|
194 |
.obstacle {
|
195 |
-
background: linear-gradient(45deg,
|
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-
|
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 |
-
.
|
262 |
-
background: rgba(
|
263 |
-
border-radius:
|
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(
|
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 |
-
.
|
288 |
text-align: center;
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
font-size: 80px;
|
294 |
-
display: inline-block;
|
295 |
-
animation: bounce 2s infinite;
|
296 |
}
|
297 |
|
298 |
-
|
299 |
-
|
300 |
-
|
|
|
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": "#
|
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": "#
|
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": "#
|
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": "#
|
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": "#
|
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
|
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 = '#
|
594 |
-
goal_color = '#
|
595 |
-
obstacle_color = '#
|
596 |
title = "Space Adventure"
|
597 |
elif theme == "jungle":
|
598 |
bg_color = '#143D2C'
|
599 |
-
player_color = '#
|
600 |
-
goal_color = '#
|
601 |
-
obstacle_color = '#
|
602 |
title = "Jungle Adventure"
|
603 |
else:
|
604 |
bg_color = '#3A015C'
|
605 |
-
player_color = '#
|
606 |
-
goal_color = '#
|
607 |
-
obstacle_color = '#
|
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 |
-
#
|
805 |
-
def
|
806 |
-
"""
|
807 |
-
st.
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
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 |
-
|
865 |
-
|
866 |
-
st.
|
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.
|
906 |
-
|
907 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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("
|
930 |
-
st.session_state.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
1051 |
if selected_concept:
|
1052 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
1093 |
-
PLAYER_COLOR = (
|
1094 |
-
GOAL_COLOR = (
|
1095 |
-
OBSTACLE_COLOR = (
|
1096 |
-
TEXT_COLOR = (
|
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="
|
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()
|