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