Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,1196 +1,545 @@
|
|
1 |
-
# app.py
|
2 |
import streamlit as st
|
|
|
|
|
3 |
import time
|
|
|
4 |
import random
|
5 |
-
import
|
6 |
-
|
7 |
-
|
|
|
|
|
8 |
import numpy as np
|
9 |
-
from transformers import pipeline
|
10 |
-
import base64
|
11 |
-
import re
|
12 |
-
import pygame
|
13 |
-
import sys
|
14 |
-
import os
|
15 |
-
import tempfile
|
16 |
|
17 |
-
#
|
18 |
st.set_page_config(
|
19 |
-
page_title="
|
20 |
-
page_icon="
|
21 |
layout="wide",
|
22 |
initial_sidebar_state="expanded"
|
23 |
)
|
24 |
|
25 |
-
# Custom CSS
|
26 |
st.markdown("""
|
27 |
<style>
|
28 |
-
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
text-align: center;
|
38 |
-
font-size: 2.5rem;
|
39 |
-
text-shadow: 3px 3px 0 #000;
|
40 |
-
padding: 1rem;
|
41 |
-
letter-spacing: 2px;
|
42 |
-
}
|
43 |
-
|
44 |
-
.subheader {
|
45 |
-
font-family: 'Comic Neue', cursive;
|
46 |
-
color: #ffeb3b;
|
47 |
-
text-align: center;
|
48 |
-
font-size: 1.8rem;
|
49 |
-
margin-bottom: 2rem;
|
50 |
-
}
|
51 |
-
|
52 |
-
.story-box {
|
53 |
-
background-color: rgba(255, 255, 255, 0.9);
|
54 |
-
border-radius: 20px;
|
55 |
-
padding: 2rem;
|
56 |
-
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
|
57 |
-
margin-bottom: 2rem;
|
58 |
-
border: 4px solid #ff6b6b;
|
59 |
-
}
|
60 |
-
|
61 |
-
.robot-speech {
|
62 |
-
background-color: #4caf50;
|
63 |
-
color: white;
|
64 |
-
border-radius: 20px;
|
65 |
-
padding: 1.5rem;
|
66 |
-
font-size: 1.2rem;
|
67 |
-
position: relative;
|
68 |
-
margin-top: 2rem;
|
69 |
-
border: 3px solid #2e7d32;
|
70 |
-
}
|
71 |
-
|
72 |
-
.robot-speech:after {
|
73 |
-
content: '';
|
74 |
-
position: absolute;
|
75 |
-
top: -20px;
|
76 |
-
left: 50px;
|
77 |
-
border: 10px solid transparent;
|
78 |
-
border-bottom-color: #4caf50;
|
79 |
-
}
|
80 |
-
|
81 |
-
.generate-btn {
|
82 |
-
background: linear-gradient(135deg, #ff5722, #ff9800) !important;
|
83 |
-
color: white !important;
|
84 |
-
font-weight: bold !important;
|
85 |
-
font-size: 1.2rem !important;
|
86 |
-
padding: 0.7rem 2rem !important;
|
87 |
-
border-radius: 50px !important;
|
88 |
-
border: none !important;
|
89 |
-
box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
|
90 |
-
transition: all 0.3s !important;
|
91 |
-
margin-top: 1rem;
|
92 |
-
font-family: 'Press Start 2P', cursive !important;
|
93 |
-
letter-spacing: 1px;
|
94 |
-
}
|
95 |
-
|
96 |
-
.generate-btn:hover {
|
97 |
-
transform: scale(1.05) !important;
|
98 |
-
box-shadow: 0 6px 12px rgba(0,0,0,0.4) !important;
|
99 |
-
background: linear-gradient(135deg, #ff7043, #ffa726) !important;
|
100 |
-
}
|
101 |
-
|
102 |
-
.code-block {
|
103 |
-
background: #2d2d2d;
|
104 |
-
color: #f8f8f2;
|
105 |
-
padding: 1rem;
|
106 |
-
border-radius: 10px;
|
107 |
-
font-family: 'Courier New', monospace;
|
108 |
-
font-size: 1.1rem;
|
109 |
-
margin: 1rem 0;
|
110 |
-
overflow-x: auto;
|
111 |
-
border-left: 4px solid #ff9800;
|
112 |
-
}
|
113 |
-
|
114 |
-
.animation-frame {
|
115 |
-
border: 3px solid #ffeb3b;
|
116 |
-
border-radius: 10px;
|
117 |
-
margin: 5px;
|
118 |
-
}
|
119 |
-
|
120 |
-
.achievement-badge {
|
121 |
-
background: #ffd54f;
|
122 |
-
color: #333;
|
123 |
-
border-radius: 50%;
|
124 |
-
width: 60px;
|
125 |
-
height: 60px;
|
126 |
-
display: flex;
|
127 |
-
align-items: center;
|
128 |
-
justify-content: center;
|
129 |
-
font-size: 1.5rem;
|
130 |
-
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
131 |
-
margin: 0 auto;
|
132 |
-
}
|
133 |
-
|
134 |
-
.hero-suggestion {
|
135 |
-
background: #ffeb3b;
|
136 |
-
color: #333;
|
137 |
-
border-radius: 10px;
|
138 |
-
padding: 1rem;
|
139 |
-
margin: 1rem 0;
|
140 |
-
text-align: center;
|
141 |
-
font-weight: bold;
|
142 |
-
border: 2px dashed #ff9800;
|
143 |
-
}
|
144 |
-
|
145 |
-
.world-suggestion {
|
146 |
-
background: #4caf50;
|
147 |
-
color: white;
|
148 |
-
border-radius: 10px;
|
149 |
-
padding: 1rem;
|
150 |
-
margin: 1rem 0;
|
151 |
-
text-align: center;
|
152 |
-
font-weight: bold;
|
153 |
-
border: 2px dashed #2e7d32;
|
154 |
-
}
|
155 |
-
|
156 |
-
.step-container {
|
157 |
-
display: flex;
|
158 |
-
justify-content: space-between;
|
159 |
-
margin-bottom: 2rem;
|
160 |
-
}
|
161 |
-
|
162 |
-
.step {
|
163 |
-
background: rgba(255, 255, 255, 0.9);
|
164 |
-
border-radius: 10px;
|
165 |
-
padding: 1rem;
|
166 |
-
width: 23%;
|
167 |
-
text-align: center;
|
168 |
-
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
169 |
-
}
|
170 |
-
|
171 |
-
.step-number {
|
172 |
-
font-size: 1.5rem;
|
173 |
-
font-weight: bold;
|
174 |
-
background: #ff5722;
|
175 |
-
color: white;
|
176 |
-
border-radius: 50%;
|
177 |
-
width: 30px;
|
178 |
-
height: 30px;
|
179 |
-
display: flex;
|
180 |
-
align-items: center;
|
181 |
-
justify-content: center;
|
182 |
-
margin: 0 auto 10px;
|
183 |
-
}
|
184 |
-
|
185 |
-
.coding-tutorial {
|
186 |
-
background: rgba(41, 128, 185, 0.9);
|
187 |
-
border-radius: 20px;
|
188 |
-
padding: 1.5rem;
|
189 |
-
margin-top: 2rem;
|
190 |
-
color: white;
|
191 |
-
border: 3px solid #2980b9;
|
192 |
-
}
|
193 |
-
|
194 |
-
.code-playground {
|
195 |
-
background: rgba(44, 62, 80, 0.9);
|
196 |
-
border-radius: 15px;
|
197 |
-
padding: 1.5rem;
|
198 |
-
margin-top: 1.5rem;
|
199 |
-
}
|
200 |
-
|
201 |
-
/* Progress bar styling */
|
202 |
-
.stProgress > div > div > div {
|
203 |
-
background-color: #ff5722 !important;
|
204 |
-
}
|
205 |
-
|
206 |
-
.level-indicator {
|
207 |
-
background: rgba(52, 152, 219, 0.9);
|
208 |
-
color: white;
|
209 |
-
border-radius: 50px;
|
210 |
-
padding: 0.5rem 1rem;
|
211 |
-
display: inline-block;
|
212 |
-
margin-bottom: 1rem;
|
213 |
-
font-weight: bold;
|
214 |
-
border: 2px solid #2c3e50;
|
215 |
-
}
|
216 |
-
</style>
|
217 |
-
""", unsafe_allow_html=True)
|
218 |
-
|
219 |
-
# Initialize AI models
|
220 |
-
@st.cache_resource
|
221 |
-
def load_models():
|
222 |
-
"""Load open-source AI models"""
|
223 |
-
try:
|
224 |
-
# Named entity recognition for identifying objects
|
225 |
-
ner_model = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english")
|
226 |
-
|
227 |
-
# Text classification for theme detection
|
228 |
-
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
|
229 |
-
|
230 |
-
# Text generation for code explanations
|
231 |
-
explanation_generator = pipeline("text2text-generation", model="google/flan-t5-large")
|
232 |
-
|
233 |
-
return ner_model, classifier, explanation_generator
|
234 |
-
except Exception as e:
|
235 |
-
st.error(f"Error loading models: {e}")
|
236 |
-
return None, None, None
|
237 |
-
|
238 |
-
# Image generation functions
|
239 |
-
def create_storyboard_image(text, width=400, height=300):
|
240 |
-
"""Create a storyboard image from text"""
|
241 |
-
# Create blank image
|
242 |
-
img = Image.new('RGB', (width, height), color=(25, 25, 112)) # Dark blue background
|
243 |
-
|
244 |
-
# Load a comic-style font (fallback to default if not available)
|
245 |
-
try:
|
246 |
-
font = ImageFont.truetype("comic.ttf", 16)
|
247 |
-
except:
|
248 |
-
font = ImageFont.load_default()
|
249 |
-
|
250 |
-
draw = ImageDraw.Draw(img)
|
251 |
|
252 |
-
|
253 |
-
|
|
|
|
|
254 |
|
255 |
-
|
256 |
-
|
|
|
257 |
|
258 |
-
|
259 |
-
|
260 |
-
|
|
|
|
|
|
|
|
|
|
|
261 |
|
262 |
-
|
263 |
-
|
264 |
-
|
|
|
265 |
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
|
|
|
|
272 |
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
star_color = None
|
284 |
-
elif theme == "underwater":
|
285 |
-
bg_color = (0, 105, 148)
|
286 |
-
star_color = None
|
287 |
-
elif theme == "desert":
|
288 |
-
bg_color = (210, 180, 140)
|
289 |
-
star_color = None
|
290 |
-
elif theme == "arctic":
|
291 |
-
bg_color = (240, 248, 255)
|
292 |
-
star_color = None
|
293 |
-
else:
|
294 |
-
bg_color = (0, 0, 30)
|
295 |
-
star_color = (255, 255, 255)
|
296 |
-
|
297 |
-
img = Image.new('RGB', (width, height), color=bg_color)
|
298 |
-
draw = ImageDraw.Draw(img)
|
299 |
-
|
300 |
-
# Draw background elements
|
301 |
-
if theme == "space":
|
302 |
-
# Stars
|
303 |
-
for _ in range(50):
|
304 |
-
x = random.randint(0, width)
|
305 |
-
y = random.randint(0, height)
|
306 |
-
draw.ellipse([x, y, x+2, y+2], fill=star_color)
|
307 |
-
|
308 |
-
# Planets
|
309 |
-
draw.ellipse([300, 30, 380, 110], fill=(255, 165, 0), outline=(255, 215, 0), width=2)
|
310 |
-
|
311 |
-
elif theme == "jungle":
|
312 |
-
# Trees
|
313 |
-
for x in [50, 150, 250, 350]:
|
314 |
-
draw.rectangle([x, 150, x+20, 250], fill=(101, 67, 33))
|
315 |
-
draw.ellipse([x-20, 120, x+40, 180], fill=(34, 139, 34))
|
316 |
-
|
317 |
-
# Sun
|
318 |
-
draw.ellipse([320, 30, 380, 90], fill=(255, 215, 0))
|
319 |
-
|
320 |
-
elif theme == "medieval":
|
321 |
-
# Castle
|
322 |
-
draw.rectangle([250, 100, 350, 250], fill=(169, 169, 169))
|
323 |
-
draw.rectangle([280, 150, 320, 250], fill=(105, 105, 105))
|
324 |
-
for x in [260, 300, 340]:
|
325 |
-
draw.polygon([(x, 80), (x-20, 100), (x+20, 100)], fill=(220, 20, 60))
|
326 |
-
|
327 |
-
# Mountains
|
328 |
-
draw.polygon([(0, 250), (100, 100), (200, 250)], fill=(120, 120, 120))
|
329 |
-
|
330 |
-
elif theme == "underwater":
|
331 |
-
# Seaweed
|
332 |
-
for x in [50, 150, 250, 350]:
|
333 |
-
draw.line([(x, 250), (x, 180)], fill=(0, 128, 0), width=5)
|
334 |
-
draw.ellipse([x-20, 170, x+20, 190], fill=(0, 128, 0))
|
335 |
-
|
336 |
-
# Bubbles
|
337 |
-
for _ in range(20):
|
338 |
-
x = random.randint(0, width)
|
339 |
-
y = random.randint(0, 200)
|
340 |
-
size = random.randint(5, 15)
|
341 |
-
draw.ellipse([x, y, x+size, y+size], fill=(173, 216, 230), outline=(70, 130, 180))
|
342 |
-
|
343 |
-
elif theme == "desert":
|
344 |
-
# Sand dunes
|
345 |
-
for x in [0, 100, 200, 300]:
|
346 |
-
draw.arc([x, 150, x+200, 300], 180, 360, fill=(210, 180, 140), width=50)
|
347 |
-
|
348 |
-
# Sun
|
349 |
-
draw.ellipse([300, 30, 370, 100], fill=(255, 140, 0))
|
350 |
-
|
351 |
-
elif theme == "arctic":
|
352 |
-
# Snowy ground
|
353 |
-
draw.rectangle([0, 200, width, height], fill=(255, 255, 255))
|
354 |
-
|
355 |
-
# Snowflakes
|
356 |
-
for _ in range(50):
|
357 |
-
x = random.randint(0, width)
|
358 |
-
y = random.randint(0, 180)
|
359 |
-
draw.ellipse([x, y, x+3, y+3], fill=(255, 255, 255))
|
360 |
-
|
361 |
-
# Mountains
|
362 |
-
draw.polygon([(0, 250), (100, 100), (200, 250)], fill=(200, 200, 200))
|
363 |
-
|
364 |
-
# Draw moving elements based on frame
|
365 |
-
if character == "spaceship":
|
366 |
-
ship_x = 50 + i * 40
|
367 |
-
ship_y = 120
|
368 |
-
# Spaceship body
|
369 |
-
draw.polygon([(ship_x, ship_y), (ship_x+50, ship_y),
|
370 |
-
(ship_x+25, ship_y-30)], fill=(169, 169, 169))
|
371 |
-
# Spaceship details
|
372 |
-
draw.rectangle([ship_x+20, ship_y-10, ship_x+30, ship_y], fill=(0, 191, 255))
|
373 |
-
|
374 |
-
if "shoot" in story.lower() and i > 1:
|
375 |
-
for j in range(3):
|
376 |
-
laser_x = ship_x + 25
|
377 |
-
laser_y = ship_y - 30 + j*5
|
378 |
-
draw.line([(laser_x, laser_y), (width, laser_y)], fill=(255, 0, 0), width=3)
|
379 |
-
|
380 |
-
elif character == "dragon":
|
381 |
-
dragon_x = 50 + i * 30
|
382 |
-
dragon_y = 140
|
383 |
-
# Dragon body
|
384 |
-
draw.ellipse([dragon_x, dragon_y, dragon_x+60, dragon_y+30], fill=(178, 34, 34))
|
385 |
-
# Dragon head
|
386 |
-
draw.ellipse([dragon_x+50, dragon_y+5, dragon_x+70, dragon_y+25], fill=(178, 34, 34))
|
387 |
-
# Dragon wings
|
388 |
-
draw.ellipse([dragon_x+10, dragon_y-20, dragon_x+40, dragon_y+10], fill=(138, 43, 226))
|
389 |
-
|
390 |
-
if "fire" in story.lower() and i > 0:
|
391 |
-
for j in range(5):
|
392 |
-
flame_x = dragon_x + 70
|
393 |
-
flame_y = dragon_y + 15 - j*5
|
394 |
-
flame_size = random.randint(8, 18)
|
395 |
-
draw.ellipse([flame_x, flame_y, flame_x+flame_size, flame_y+flame_size],
|
396 |
-
fill=(255, random.randint(100, 200), 0))
|
397 |
-
|
398 |
-
elif character == "knight":
|
399 |
-
knight_x = 50 + i * 30
|
400 |
-
knight_y = 150
|
401 |
-
# Knight body
|
402 |
-
draw.rectangle([knight_x, knight_y, knight_x+25, knight_y+50], fill=(70, 70, 70))
|
403 |
-
# Knight head
|
404 |
-
draw.ellipse([knight_x+5, knight_y-15, knight_x+20, knight_y], fill=(210, 180, 140))
|
405 |
-
# Knight helmet
|
406 |
-
draw.arc([knight_x, knight_y-20, knight_x+25, knight_y], 0, 180, fill=(50, 50, 50), width=3)
|
407 |
-
# Sword
|
408 |
-
draw.rectangle([knight_x+20, knight_y+10, knight_x+30, knight_y+15], fill=(192, 192, 192))
|
409 |
-
draw.polygon([(knight_x+30, knight_y+12), (knight_x+40, knight_y+10), (knight_x+40, knight_y+15)], fill=(192, 192, 192))
|
410 |
-
|
411 |
-
if "attack" in story.lower() and i % 2 == 1:
|
412 |
-
# Draw sword swing
|
413 |
-
draw.line([(knight_x+30, knight_y+12), (knight_x+60, knight_y-20)], fill=(255, 255, 0), width=3)
|
414 |
-
|
415 |
-
elif character == "mermaid":
|
416 |
-
mermaid_x = 50 + i * 30
|
417 |
-
mermaid_y = 150
|
418 |
-
# Mermaid tail
|
419 |
-
draw.ellipse([mermaid_x, mermaid_y, mermaid_x+40, mermaid_y+30], fill=(255, 105, 180))
|
420 |
-
# Mermaid body
|
421 |
-
draw.ellipse([mermaid_x+10, mermaid_y-30, mermaid_x+30, mermaid_y], fill=(255, 218, 185))
|
422 |
-
# Mermaid hair
|
423 |
-
draw.ellipse([mermaid_x-10, mermaid_y-35, mermaid_x+40, mermaid_y-20], fill=(255, 215, 0))
|
424 |
-
|
425 |
-
if "swim" in story.lower() and i > 0:
|
426 |
-
# Draw bubbles
|
427 |
-
for j in range(3):
|
428 |
-
bubble_x = mermaid_x + random.randint(0, 40)
|
429 |
-
bubble_y = mermaid_y - random.randint(20, 40)
|
430 |
-
draw.ellipse([bubble_x, bubble_y, bubble_x+8, bubble_y+8], fill=(173, 216, 230))
|
431 |
-
|
432 |
-
elif character == "robot":
|
433 |
-
robot_x = 50 + i * 35
|
434 |
-
robot_y = 150
|
435 |
-
# Robot body
|
436 |
-
draw.rectangle([robot_x, robot_y, robot_x+30, robot_y+50], fill=(100, 100, 100))
|
437 |
-
# Robot head
|
438 |
-
draw.rectangle([robot_x-5, robot_y-20, robot_x+35, robot_y], fill=(150, 150, 150))
|
439 |
-
# Robot eyes
|
440 |
-
draw.ellipse([robot_x+5, robot_y-15, robot_x+10, robot_y-10], fill=(0, 255, 0))
|
441 |
-
draw.ellipse([robot_x+20, robot_y-15, robot_x+25, robot_y-10], fill=(0, 255, 0))
|
442 |
-
# Robot arms
|
443 |
-
draw.rectangle([robot_x-10, robot_y+10, robot_x, robot_y+20], fill=(100, 100, 100))
|
444 |
-
draw.rectangle([robot_x+30, robot_y+10, robot_x+40, robot_y+20], fill=(100, 100, 100))
|
445 |
-
|
446 |
-
if "laser" in story.lower() and i > 1:
|
447 |
-
draw.line([(robot_x+40, robot_y+15), (width, robot_y+15)], fill=(0, 255, 0), width=3)
|
448 |
-
|
449 |
-
elif character == "unicorn":
|
450 |
-
unicorn_x = 50 + i * 30
|
451 |
-
unicorn_y = 150
|
452 |
-
# Unicorn body
|
453 |
-
draw.ellipse([unicorn_x, unicorn_y, unicorn_x+40, unicorn_y+20], fill=(255, 240, 245))
|
454 |
-
# Unicorn head
|
455 |
-
draw.ellipse([unicorn_x+30, unicorn_y-5, unicorn_x+50, unicorn_y+15], fill=(255, 240, 245))
|
456 |
-
# Unicorn horn
|
457 |
-
draw.polygon([(unicorn_x+40, unicorn_y-10), (unicorn_x+45, unicorn_y-30), (unicorn_x+50, unicorn_y-10)], fill=(255, 215, 0))
|
458 |
-
# Unicorn mane
|
459 |
-
for j in range(5):
|
460 |
-
color = random.choice(['#FF69B4', '#9370DB', '#87CEFA'])
|
461 |
-
draw.ellipse([unicorn_x+25+j*3, unicorn_y-10-j*2, unicorn_x+35+j*3, unicorn_y+j*2], fill=color)
|
462 |
-
|
463 |
-
# Draw enemies based on theme
|
464 |
-
if theme == "space" and "alien" in story.lower():
|
465 |
-
alien_x = 300
|
466 |
-
alien_y = 120 - i*5
|
467 |
-
draw.ellipse([alien_x, alien_y, alien_x+30, alien_y+30], fill=(50, 205, 50))
|
468 |
-
draw.ellipse([alien_x+7, alien_y+8, alien_x+12, alien_y+13], fill=(0, 0, 0))
|
469 |
-
draw.ellipse([alien_x+18, alien_y+8, alien_x+23, alien_y+13], fill=(0, 0, 0))
|
470 |
-
draw.arc([alien_x+7, alien_y+15, alien_x+23, alien_y+25], 0, 180, fill=(0, 0, 0), width=2)
|
471 |
-
|
472 |
-
elif theme == "jungle" and "snake" in story.lower():
|
473 |
-
snake_x = 300
|
474 |
-
snake_y = 180 - i*5
|
475 |
-
for segment in range(8):
|
476 |
-
offset = segment * 8
|
477 |
-
draw.ellipse([snake_x+offset, snake_y+offset, snake_x+offset+20, snake_y+offset+20], fill=(0, 128, 0))
|
478 |
-
|
479 |
-
elif theme == "medieval" and "dragon" in story.lower() and character != "dragon":
|
480 |
-
dragon_x = 250
|
481 |
-
dragon_y = 100
|
482 |
-
draw.ellipse([dragon_x, dragon_y, dragon_x+50, dragon_y+25], fill=(178, 34, 34))
|
483 |
-
draw.line([(dragon_x+50, dragon_y+12), (dragon_x+80, dragon_y)], fill=(178, 34, 34), width=4)
|
484 |
-
|
485 |
-
elif theme == "underwater" and "shark" in story.lower():
|
486 |
-
shark_x = 250
|
487 |
-
shark_y = 100 + i*8
|
488 |
-
# Shark body
|
489 |
-
draw.ellipse([shark_x, shark_y, shark_x+80, shark_y+40], fill=(169, 169, 169))
|
490 |
-
# Shark fin
|
491 |
-
draw.polygon([(shark_x+60, shark_y), (shark_x+70, shark_y-30), (shark_x+80, shark_y)], fill=(169, 169, 169))
|
492 |
-
|
493 |
-
elif theme == "desert" and "scorpion" in story.lower():
|
494 |
-
scorpion_x = 300
|
495 |
-
scorpion_y = 200
|
496 |
-
# Scorpion body
|
497 |
-
draw.ellipse([scorpion_x, scorpion_y, scorpion_x+40, scorpion_y+20], fill=(165, 42, 42))
|
498 |
-
# Scorpion tail
|
499 |
-
draw.line([(scorpion_x+40, scorpion_y+10), (scorpion_x+60, scorpion_y-10)], fill=(165, 42, 42), width=3)
|
500 |
-
# Scorpion claws
|
501 |
-
draw.line([(scorpion_x, scorpion_y+5), (scorpion_x-15, scorpion_y)], fill=(165, 42, 42), width=3)
|
502 |
-
draw.line([(scorpion_x, scorpion_y+15), (scorpion_x-15, scorpion_y+20)], fill=(165, 42, 42), width=3)
|
503 |
-
|
504 |
-
elif theme == "arctic" and "yeti" in story.lower():
|
505 |
-
yeti_x = 280
|
506 |
-
yeti_y = 180
|
507 |
-
# Yeti body
|
508 |
-
draw.ellipse([yeti_x, yeti_y, yeti_x+50, yeti_y+50], fill=(240, 240, 240))
|
509 |
-
# Yeti eyes
|
510 |
-
draw.ellipse([yeti_x+15, yeti_y+15, yeti_x+20, yeti_y+20], fill=(0, 0, 0))
|
511 |
-
draw.ellipse([yeti_x+30, yeti_y+15, yeti_x+35, yeti_y+20], fill=(0, 0, 0))
|
512 |
-
# Yeti mouth
|
513 |
-
draw.arc([yeti_x+15, yeti_y+25, yeti_x+35, yeti_y+35], 0, 180, fill=(0, 0, 0), width=2)
|
514 |
-
|
515 |
-
# Add frame number
|
516 |
-
draw.text((10, 10), f"Frame {i+1}", fill=(255, 255, 255))
|
517 |
-
|
518 |
-
frames.append(img)
|
519 |
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
# Basic PyGame template
|
525 |
-
code = f"""import pygame
|
526 |
-
import sys
|
527 |
-
|
528 |
-
# Initialize Pygame
|
529 |
-
pygame.init()
|
530 |
-
|
531 |
-
# Screen dimensions
|
532 |
-
WIDTH, HEIGHT = 800, 600
|
533 |
-
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
534 |
-
pygame.display.set_caption("Your Story: {character.capitalize()} in {theme.capitalize()}")
|
535 |
-
|
536 |
-
# Colors
|
537 |
-
BACKGROUND = {get_theme_color(theme)}
|
538 |
-
HERO_COLOR = {get_character_color(character)}
|
539 |
-
|
540 |
-
# Hero position
|
541 |
-
hero_x = 100
|
542 |
-
hero_y = HEIGHT // 2
|
543 |
-
hero_speed = 5
|
544 |
-
|
545 |
-
# Main game loop
|
546 |
-
clock = pygame.time.Clock()
|
547 |
-
running = True
|
548 |
-
|
549 |
-
while running:
|
550 |
-
for event in pygame.event.get():
|
551 |
-
if event.type == pygame.QUIT:
|
552 |
-
running = False
|
553 |
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
hero_x += hero_speed
|
560 |
-
if keys[pygame.K_UP]:
|
561 |
-
hero_y -= hero_speed
|
562 |
-
if keys[pygame.K_DOWN]:
|
563 |
-
hero_y += hero_speed
|
564 |
|
565 |
-
|
566 |
-
|
|
|
|
|
|
|
|
|
567 |
|
568 |
-
|
569 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
570 |
|
571 |
-
|
572 |
-
|
|
|
|
|
573 |
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
sys.exit()
|
579 |
-
"""
|
580 |
-
return code
|
581 |
-
|
582 |
-
def get_theme_color(theme):
|
583 |
-
"""Get theme color for PyGame code"""
|
584 |
-
colors = {
|
585 |
-
"space": "(0, 0, 30)",
|
586 |
-
"jungle": "(34, 139, 34)",
|
587 |
-
"medieval": "(139, 69, 19)",
|
588 |
-
"underwater": "(0, 105, 148)",
|
589 |
-
"desert": "(210, 180, 140)",
|
590 |
-
"arctic": "(240, 248, 255)"
|
591 |
}
|
592 |
-
|
|
|
593 |
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
"
|
598 |
-
"
|
599 |
-
"
|
600 |
-
"
|
601 |
-
"
|
602 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
603 |
}
|
604 |
-
|
605 |
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
(hero_x+25, hero_y-30)])
|
613 |
-
pygame.draw.rect(screen, (0, 191, 255), (hero_x+20, hero_y-10, 10, 10))"""
|
614 |
-
elif character == "dragon":
|
615 |
-
return """ # Draw dragon
|
616 |
-
pygame.draw.ellipse(screen, HERO_COLOR, (hero_x, hero_y, 60, 30))
|
617 |
-
pygame.draw.ellipse(screen, HERO_COLOR, (hero_x+50, hero_y+5, 20, 20))
|
618 |
-
pygame.draw.ellipse(screen, (138, 43, 226), (hero_x+10, hero_y-20, 30, 30))"""
|
619 |
-
elif character == "knight":
|
620 |
-
return """ # Draw knight
|
621 |
-
pygame.draw.rect(screen, HERO_COLOR, (hero_x, hero_y, 25, 50))
|
622 |
-
pygame.draw.ellipse(screen, (210, 180, 140), (hero_x+5, hero_y-15, 15, 15))
|
623 |
-
pygame.draw.arc(screen, (50, 50, 50), (hero_x, hero_y-20, 25, 20), 0, 3.14, 3)
|
624 |
-
pygame.draw.rect(screen, (192, 192, 192), (hero_x+20, hero_y+10, 10, 5))
|
625 |
-
pygame.draw.polygon(screen, (192, 192, 192), [(hero_x+30, hero_y+12),
|
626 |
-
(hero_x+40, hero_y+10),
|
627 |
-
(hero_x+40, hero_y+15)])"""
|
628 |
-
elif character == "mermaid":
|
629 |
-
return """ # Draw mermaid
|
630 |
-
pygame.draw.ellipse(screen, HERO_COLOR, (hero_x, hero_y, 40, 30))
|
631 |
-
pygame.draw.ellipse(screen, (255, 218, 185), (hero_x+10, hero_y-30, 20, 30))
|
632 |
-
pygame.draw.ellipse(screen, (255, 215, 0), (hero_x-10, hero_y-35, 50, 15))"""
|
633 |
-
elif character == "robot":
|
634 |
-
return """ # Draw robot
|
635 |
-
pygame.draw.rect(screen, HERO_COLOR, (hero_x, hero_y, 30, 50))
|
636 |
-
pygame.draw.rect(screen, (150, 150, 150), (hero_x-5, hero_y-20, 40, 20))
|
637 |
-
pygame.draw.ellipse(screen, (0, 255, 0), (hero_x+5, hero_y-15, 5, 5))
|
638 |
-
pygame.draw.ellipse(screen, (0, 255, 0), (hero_x+20, hero_y-15, 5, 5))
|
639 |
-
pygame.draw.rect(screen, HERO_COLOR, (hero_x-10, hero_y+10, 10, 10))
|
640 |
-
pygame.draw.rect(screen, HERO_COLOR, (hero_x+30, hero_y+10, 10, 10))"""
|
641 |
-
elif character == "unicorn":
|
642 |
-
return """ # Draw unicorn
|
643 |
-
pygame.draw.ellipse(screen, HERO_COLOR, (hero_x, hero_y, 40, 20))
|
644 |
-
pygame.draw.ellipse(screen, HERO_COLOR, (hero_x+30, hero_y-5, 20, 20))
|
645 |
-
pygame.draw.polygon(screen, (255, 215, 0), [(hero_x+40, hero_y-10),
|
646 |
-
(hero_x+45, hero_y-30),
|
647 |
-
(hero_x+50, hero_y-10)])"""
|
648 |
-
else:
|
649 |
-
return """ # Draw hero
|
650 |
-
pygame.draw.rect(screen, HERO_COLOR, (hero_x, hero_y, 40, 60))"""
|
651 |
|
652 |
-
def
|
653 |
-
"""
|
654 |
-
|
655 |
-
|
656 |
-
code += """ # Draw alien
|
657 |
-
pygame.draw.ellipse(screen, (50, 205, 50), (600, 200, 30, 30))
|
658 |
-
pygame.draw.ellipse(screen, (0, 0, 0), (610, 210, 5, 5))
|
659 |
-
pygame.draw.ellipse(screen, (0, 0, 0), (620, 210, 5, 5))
|
660 |
-
pygame.draw.arc(screen, (0, 0, 0), (610, 220, 15, 10), 0, 3.14, 2)
|
661 |
-
"""
|
662 |
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
pygame.draw.line(screen, (178, 34, 34), (700, 162), (730, 150), 4)
|
667 |
-
"""
|
668 |
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
pygame.draw.rect(screen, (255, 215, 0), (705, 395, 20, 10))
|
673 |
-
"""
|
674 |
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
pygame.draw.rect(screen, (105, 105, 105), (650, 350, 50, 100))
|
679 |
-
for x in range(610, 740, 40):
|
680 |
-
pygame.draw.polygon(screen, (220, 20, 60), [(x, 300), (x-20, 320), (x+20, 320)])
|
681 |
-
"""
|
682 |
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
result = explanation_generator(
|
693 |
-
prompt,
|
694 |
-
max_length=250,
|
695 |
-
num_return_sequences=1,
|
696 |
-
)
|
697 |
-
|
698 |
-
return result[0]['generated_text']
|
699 |
-
except:
|
700 |
-
# Fallback explanation if model fails
|
701 |
-
return f"""See how your story became real code? For example, when you wrote "{story.split()[0]}",
|
702 |
-
we used code like: character.move(). That's how we turn words into actions!"""
|
703 |
|
704 |
-
def
|
705 |
-
"""
|
706 |
try:
|
707 |
-
#
|
708 |
-
|
709 |
-
world = "space"
|
710 |
|
711 |
-
#
|
712 |
-
|
713 |
-
|
714 |
-
"dragon": ["dragon", "monster", "creature", "beast", "wyvern"],
|
715 |
-
"knight": ["knight", "warrior", "prince", "princess", "king", "queen", "sword", "castle"],
|
716 |
-
"mermaid": ["mermaid", "merman", "sea", "ocean", "underwater", "fish"],
|
717 |
-
"robot": ["robot", "cyborg", "machine", "android"],
|
718 |
-
"unicorn": ["unicorn", "horse", "pony", "magic"]
|
719 |
-
}
|
720 |
|
721 |
-
|
722 |
-
|
|
|
723 |
|
724 |
-
|
725 |
-
story_lower = story.lower()
|
726 |
-
for candidate, keywords in hero_keywords.items():
|
727 |
-
if any(keyword in story_lower for keyword in keywords):
|
728 |
-
hero = candidate
|
729 |
-
break
|
730 |
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
if person_entities:
|
737 |
-
if "dragon" in story_lower:
|
738 |
-
hero = "dragon"
|
739 |
-
elif "knight" in story_lower or "king" in story_lower or "queen" in story_lower:
|
740 |
-
hero = "knight"
|
741 |
-
elif "mermaid" in story_lower or "sea" in story_lower:
|
742 |
-
hero = "mermaid"
|
743 |
-
elif "robot" in story_lower:
|
744 |
-
hero = "robot"
|
745 |
-
elif "unicorn" in story_lower:
|
746 |
-
hero = "unicorn"
|
747 |
-
|
748 |
-
# Classify world
|
749 |
-
result = classifier(story, world_labels)
|
750 |
-
world = result['labels'][0]
|
751 |
-
|
752 |
-
# Override based on specific keywords
|
753 |
-
if "underwater" in story_lower or "ocean" in story_lower or "sea" in story_lower:
|
754 |
-
world = "underwater"
|
755 |
-
if "forest" in story_lower or "jungle" in story_lower:
|
756 |
-
world = "jungle"
|
757 |
-
if "castle" in story_lower or "kingdom" in story_lower or "dragon" in story_lower or "knight" in story_lower:
|
758 |
-
world = "medieval"
|
759 |
-
if "space" in story_lower or "alien" in story_lower or "planet" in story_lower:
|
760 |
-
world = "space"
|
761 |
-
if "desert" in story_lower or "sand" in story_lower:
|
762 |
-
world = "desert"
|
763 |
-
if "arctic" in story_lower or "snow" in story_lower or "ice" in story_lower:
|
764 |
-
world = "arctic"
|
765 |
-
|
766 |
-
return hero, world
|
767 |
-
|
768 |
-
except Exception as e:
|
769 |
-
st.error(f"Error analyzing story: {str(e)}")
|
770 |
-
return "spaceship", "space"
|
771 |
-
|
772 |
-
def run_pygame_code(code):
|
773 |
-
"""Run PyGame code and capture the output"""
|
774 |
-
with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f:
|
775 |
-
f.write(code)
|
776 |
-
temp_file_name = f.name
|
777 |
-
|
778 |
-
# Create a video capture of the PyGame window
|
779 |
-
os.system(f"python {temp_file_name} &")
|
780 |
-
return temp_file_name
|
781 |
-
|
782 |
-
# Header section
|
783 |
-
st.markdown('<div class="header">CodeTales ✨</div>', unsafe_allow_html=True)
|
784 |
-
st.markdown('<div class="subheader">Storytime + Coding Magic</div>', unsafe_allow_html=True)
|
785 |
-
st.markdown('<div style="text-align:center; color:white; font-size:1.2rem; margin-bottom:2rem;">Turn wild stories into playable games with AI magic!</div>', unsafe_allow_html=True)
|
786 |
-
|
787 |
-
# How it works section
|
788 |
-
st.markdown("### ✨ How It Works")
|
789 |
-
step_container = st.container()
|
790 |
-
with step_container:
|
791 |
-
cols = st.columns(4)
|
792 |
-
with cols[0]:
|
793 |
-
st.markdown('<div class="step"><div class="step-number">1</div>Write Your Story</div>', unsafe_allow_html=True)
|
794 |
-
with cols[1]:
|
795 |
-
st.markdown('<div class="step"><div class="step-number">2</div>AI Chooses Hero & World</div>', unsafe_allow_html=True)
|
796 |
-
with cols[2]:
|
797 |
-
st.markdown('<div class="step"><div class="step-number">3</div>Watch Animation</div>', unsafe_allow_html=True)
|
798 |
-
with cols[3]:
|
799 |
-
st.markdown('<div class="step"><div class="step-number">4</div>Learn Coding</div>', unsafe_allow_html=True)
|
800 |
-
|
801 |
-
# Load models
|
802 |
-
ner_model, classifier, explanation_generator = load_models()
|
803 |
-
|
804 |
-
# Initialize session state
|
805 |
-
if 'animation_generated' not in st.session_state:
|
806 |
-
st.session_state.animation_generated = False
|
807 |
-
if 'story_text' not in st.session_state:
|
808 |
-
st.session_state.story_text = ""
|
809 |
-
if 'animation_frames' not in st.session_state:
|
810 |
-
st.session_state.animation_frames = []
|
811 |
-
if 'code_explanation' not in st.session_state:
|
812 |
-
st.session_state.code_explanation = ""
|
813 |
-
if 'selected_character' not in st.session_state:
|
814 |
-
st.session_state.selected_character = "spaceship"
|
815 |
-
if 'selected_theme' not in st.session_state:
|
816 |
-
st.session_state.selected_theme = "space"
|
817 |
-
if 'story_count' not in st.session_state:
|
818 |
-
st.session_state.story_count = 0
|
819 |
-
if 'level' not in st.session_state:
|
820 |
-
st.session_state.level = 1
|
821 |
-
if 'xp' not in st.session_state:
|
822 |
-
st.session_state.xp = 0
|
823 |
-
if 'analyzed' not in st.session_state:
|
824 |
-
st.session_state.analyzed = False
|
825 |
-
if 'pygame_code' not in st.session_state:
|
826 |
-
st.session_state.pygame_code = ""
|
827 |
-
if 'show_code' not in st.session_state:
|
828 |
-
st.session_state.show_code = False
|
829 |
-
|
830 |
-
# Character and theme mapping
|
831 |
-
characters = {
|
832 |
-
"spaceship": "🚀 Spaceship",
|
833 |
-
"dragon": "🐉 Dragon",
|
834 |
-
"knight": "🛡️ Knight",
|
835 |
-
"mermaid": "🧜♀️ Mermaid",
|
836 |
-
"robot": "🤖 Robot",
|
837 |
-
"unicorn": "🦄 Unicorn"
|
838 |
-
}
|
839 |
-
|
840 |
-
themes = {
|
841 |
-
"space": "🌌 Space",
|
842 |
-
"jungle": "🌿 Jungle",
|
843 |
-
"medieval": "🏰 Medieval",
|
844 |
-
"underwater": "🌊 Underwater",
|
845 |
-
"desert": "🏜️ Desert",
|
846 |
-
"arctic": "❄️ Arctic"
|
847 |
-
}
|
848 |
-
|
849 |
-
# Main content
|
850 |
-
st.markdown(f'<div class="level-indicator">Level {st.session_state.level} ✨ XP: {st.session_state.xp}/100</div>', unsafe_allow_html=True)
|
851 |
-
progress = st.progress(st.session_state.xp / 100)
|
852 |
|
853 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
854 |
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
|
|
|
|
|
|
|
|
|
|
879 |
else:
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
st.markdown("### 🧙 Step 2: Your Hero & World")
|
884 |
-
|
885 |
-
st.markdown(f'<div class="hero-suggestion">Your Hero: {characters[st.session_state.selected_character]}</div>',
|
886 |
-
unsafe_allow_html=True)
|
887 |
-
st.markdown(f'<div class="world-suggestion">Your World: {themes[st.session_state.selected_theme]}</div>',
|
888 |
-
unsafe_allow_html=True)
|
889 |
-
|
890 |
-
# Customization options
|
891 |
-
st.markdown("### 🎨 Customize Your Hero")
|
892 |
-
custom_char = st.selectbox(
|
893 |
-
"Choose a different hero:",
|
894 |
-
options=list(characters.keys()),
|
895 |
-
format_func=lambda x: characters[x],
|
896 |
-
index=list(characters.keys()).index(st.session_state.selected_character)
|
897 |
-
)
|
898 |
-
st.session_state.selected_character = custom_char
|
899 |
-
|
900 |
-
# Generate animation button
|
901 |
-
if st.button("🎬 Generate Animation & Code!", use_container_width=True, key="generate", type="primary"):
|
902 |
-
st.session_state.animation_generated = True
|
903 |
-
with st.spinner("🧙♂️ Creating your animation and code..."):
|
904 |
-
# Generate animation frames
|
905 |
-
st.session_state.animation_frames = generate_sprite_animation(
|
906 |
-
story_text,
|
907 |
-
character=st.session_state.selected_character,
|
908 |
-
theme=st.session_state.selected_theme,
|
909 |
-
num_frames=8
|
910 |
-
)
|
911 |
-
|
912 |
-
# Generate code explanation
|
913 |
-
st.session_state.code_explanation = generate_code_explanation(story_text, explanation_generator)
|
914 |
-
|
915 |
-
# Generate PyGame code
|
916 |
-
st.session_state.pygame_code = generate_pygame_code(
|
917 |
-
story_text,
|
918 |
-
st.session_state.selected_character,
|
919 |
-
st.session_state.selected_theme
|
920 |
-
)
|
921 |
-
|
922 |
-
# Update user progress
|
923 |
-
st.session_state.story_count += 1
|
924 |
-
st.session_state.xp += 25
|
925 |
-
|
926 |
-
# Check for level up
|
927 |
-
if st.session_state.xp >= 100:
|
928 |
-
st.session_state.level += 1
|
929 |
-
st.session_state.xp = st.session_state.xp - 100
|
930 |
-
st.session_state.new_level = True
|
931 |
-
|
932 |
-
st.markdown('</div>', unsafe_allow_html=True)
|
933 |
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
label="⬇️ Download Animation",
|
958 |
-
data=gif_buffer.getvalue(),
|
959 |
-
file_name="your_story.gif",
|
960 |
-
mime="image/gif",
|
961 |
-
use_container_width=True
|
962 |
-
)
|
963 |
-
|
964 |
-
# Display character and theme info
|
965 |
-
st.success(f"✨ Your {characters[st.session_state.selected_character]} in the {themes[st.session_state.selected_theme]} world!")
|
966 |
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
use_container_width=True)
|
971 |
-
st.info("👆 Click 'Generate Animation & Code!' to bring your story to life!")
|
972 |
-
elif story_text:
|
973 |
-
st.markdown("### 🎮 Your Animation Will Appear Here")
|
974 |
-
preview_img = create_storyboard_image(story_text)
|
975 |
-
st.image(preview_img, caption="Your Story Preview", use_container_width=True)
|
976 |
-
st.info("👆 Click 'Analyze My Story' to begin the magic!")
|
977 |
-
else:
|
978 |
-
st.image("https://img.freepik.com/free-vector/hand-drawn-colorful-space-background_23-2148821798.jpg",
|
979 |
-
use_container_width=True)
|
980 |
-
st.info("👆 Write your story and click 'Analyze My Story' to begin!")
|
981 |
-
|
982 |
-
st.markdown('</div>', unsafe_allow_html=True)
|
983 |
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
st.markdown("### 🧠 AI-Powered Explanation:")
|
991 |
-
st.write(st.session_state.code_explanation)
|
992 |
-
|
993 |
-
# Toggle for showing full code
|
994 |
-
if st.button("👨💻 Show Me the Full Code!", key="show_code"):
|
995 |
-
st.session_state.show_code = not st.session_state.show_code
|
996 |
-
|
997 |
-
if st.session_state.show_code:
|
998 |
-
st.markdown("### 🐍 Your Story as Python Code")
|
999 |
-
st.code(st.session_state.pygame_code, language='python')
|
1000 |
|
1001 |
-
|
1002 |
-
|
1003 |
-
temp_file = run_pygame_code(st.session_state.pygame_code)
|
1004 |
-
st.success(f"Game launched! Check your desktop for the PyGame window")
|
1005 |
-
st.info("Use arrow keys to move your character!")
|
1006 |
-
|
1007 |
-
# Step-by-step coding tutorial
|
1008 |
-
st.markdown("### 👣 Step-by-Step Coding Tutorial")
|
1009 |
-
with st.expander("1. Setting Up Your Game"):
|
1010 |
-
st.markdown("""
|
1011 |
-
Every PyGame program starts with these basic steps:
|
1012 |
-
```python
|
1013 |
-
import pygame # Import the game library
|
1014 |
-
import sys # Import system library for exit
|
1015 |
|
1016 |
-
#
|
1017 |
-
|
|
|
|
|
|
|
1018 |
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
```
|
1024 |
-
""")
|
1025 |
-
|
1026 |
-
with st.expander("2. Creating Your Hero"):
|
1027 |
-
st.markdown(f"""
|
1028 |
-
Let's create your {st.session_state.selected_character} hero:
|
1029 |
-
```python
|
1030 |
-
# Hero position
|
1031 |
-
hero_x = 100
|
1032 |
-
hero_y = 300
|
1033 |
-
|
1034 |
-
# How to draw your hero
|
1035 |
-
def draw_hero():
|
1036 |
-
{get_character_drawing_code(st.session_state.selected_character).replace('\\n', '\\n ')}
|
1037 |
-
```
|
1038 |
-
""")
|
1039 |
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
if
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1101 |
|
1102 |
-
|
1103 |
-
|
|
|
|
|
|
|
|
|
|
|
1104 |
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1109 |
|
1110 |
-
#
|
1111 |
-
|
1112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1113 |
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
1120 |
-
|
1121 |
-
# Code Playground
|
1122 |
-
if st.session_state.animation_generated:
|
1123 |
-
st.markdown('<div class="code-playground">', unsafe_allow_html=True)
|
1124 |
-
st.markdown("### 🧪 Step 5: Code Playground")
|
1125 |
-
st.markdown("Try modifying your code and see the changes!")
|
1126 |
-
|
1127 |
-
# Editable code area
|
1128 |
-
modified_code = st.text_area(
|
1129 |
-
"Edit your Python code:",
|
1130 |
-
value=st.session_state.pygame_code,
|
1131 |
-
height=300,
|
1132 |
-
key="code_editor"
|
1133 |
-
)
|
1134 |
-
|
1135 |
-
# Run the modified code
|
1136 |
-
if st.button("🚀 Run My Modified Code", key="run_modified_code"):
|
1137 |
-
with st.spinner("Running your code..."):
|
1138 |
-
temp_file = run_pygame_code(modified_code)
|
1139 |
-
st.success("Game launched with your modifications!")
|
1140 |
-
|
1141 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
1142 |
-
|
1143 |
-
# New level notification
|
1144 |
-
if 'new_level' in st.session_state and st.session_state.new_level:
|
1145 |
-
st.balloons()
|
1146 |
-
st.success(f"🌟 Level Up! You're now Level {st.session_state.level}!")
|
1147 |
-
st.session_state.new_level = False
|
1148 |
-
|
1149 |
-
# Achievements
|
1150 |
-
st.markdown("### 🏆 Your Achievements")
|
1151 |
-
achievement_cols = st.columns(4)
|
1152 |
-
with achievement_cols[0]:
|
1153 |
-
st.markdown('<div class="achievement-badge">📝</div>', unsafe_allow_html=True)
|
1154 |
-
st.markdown("**Storyteller**", help="Created your first story")
|
1155 |
-
st.markdown(f"{st.session_state.story_count} story created")
|
1156 |
-
|
1157 |
-
with achievement_cols[1]:
|
1158 |
-
st.markdown('<div class="achievement-badge">👾</div>', unsafe_allow_html=True)
|
1159 |
-
st.markdown("**Animator**", help="Generated 5 animations")
|
1160 |
-
st.markdown(f"{min(st.session_state.story_count, 5)}/5 animations")
|
1161 |
|
1162 |
-
|
1163 |
-
|
1164 |
-
st.markdown("**Coder**", help="Modified and ran code")
|
1165 |
-
st.markdown("Unlocked!" if st.session_state.animation_generated else "Locked")
|
1166 |
-
|
1167 |
-
with achievement_cols[3]:
|
1168 |
-
st.markdown('<div class="achievement-badge">🚀</div>', unsafe_allow_html=True)
|
1169 |
-
st.markdown("**Explorer**", help="Used 3 different heroes")
|
1170 |
-
heroes_used = len(set([st.session_state.selected_character]))
|
1171 |
-
st.markdown(f"{heroes_used}/3 heroes used")
|
1172 |
-
|
1173 |
-
# Benefits section
|
1174 |
-
st.markdown("""
|
1175 |
-
## ❤ Why Kids & Parents Love CodeTales
|
1176 |
-
|
1177 |
-
| For Kids 👧👦 | For Parents & Teachers 👪👩🏫 |
|
1178 |
-
|--------------|----------------------------|
|
1179 |
-
| ✨ Create your own animated stories | 🧠 Teaches computational thinking |
|
1180 |
-
| 🚀 See imagination come to life | 🔍 Develops problem-solving skills |
|
1181 |
-
| 🎮 Interactive game creation | ➗ Reinforces STEM fundamentals |
|
1182 |
-
| 😄 Fun characters and worlds | 🧩 Encourages logical reasoning |
|
1183 |
-
| 🌟 Unlock new levels | 📈 Tracks learning progress |
|
1184 |
-
| 💻 Real Python coding experience | 🎯 Aligned with coding curriculum |
|
1185 |
-
""")
|
1186 |
-
|
1187 |
-
# Footer
|
1188 |
-
st.markdown("---")
|
1189 |
-
st.markdown("""
|
1190 |
-
<center>
|
1191 |
-
<p style="color:white; font-size:1.1rem;">
|
1192 |
-
✨ Made with magic by CodeTales Team ✨<br>
|
1193 |
-
Transforming stories into games since 2023
|
1194 |
-
</p>
|
1195 |
-
</center>
|
1196 |
-
""", unsafe_allow_html=True)
|
|
|
1 |
+
# app.py - Hugging Face Compatible Version
|
2 |
import streamlit as st
|
3 |
+
import requests
|
4 |
+
import json
|
5 |
import time
|
6 |
+
import base64
|
7 |
import random
|
8 |
+
from PIL import Image
|
9 |
+
import io
|
10 |
+
import matplotlib.pyplot as plt
|
11 |
+
import matplotlib.animation as animation
|
12 |
+
from matplotlib.animation import FuncAnimation
|
13 |
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
# Configure Streamlit page
|
16 |
st.set_page_config(
|
17 |
+
page_title="StoryCoder - Learn Python Through Stories",
|
18 |
+
page_icon="🧙♂️",
|
19 |
layout="wide",
|
20 |
initial_sidebar_state="expanded"
|
21 |
)
|
22 |
|
23 |
+
# Custom CSS for colorful UI
|
24 |
st.markdown("""
|
25 |
<style>
|
26 |
+
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
|
27 |
+
|
28 |
+
:root {
|
29 |
+
--primary: #FF6B6B;
|
30 |
+
--secondary: #4ECDC4;
|
31 |
+
--accent: #FFD166;
|
32 |
+
--dark: #1A535C;
|
33 |
+
--light: #F7FFF7;
|
34 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
+
body {
|
37 |
+
background: linear-gradient(135deg, var(--light) 0%, #E8F4F8 100%);
|
38 |
+
font-family: 'Comic Neue', cursive;
|
39 |
+
}
|
40 |
|
41 |
+
.stApp {
|
42 |
+
background: url('https://www.transparenttextures.com/patterns/cartographer.png');
|
43 |
+
}
|
44 |
|
45 |
+
.story-box {
|
46 |
+
background-color: white;
|
47 |
+
border-radius: 20px;
|
48 |
+
padding: 25px;
|
49 |
+
box-shadow: 0 8px 16px rgba(26, 83, 92, 0.15);
|
50 |
+
border: 3px solid var(--accent);
|
51 |
+
margin-bottom: 25px;
|
52 |
+
}
|
53 |
|
54 |
+
.header {
|
55 |
+
color: var(--dark);
|
56 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
57 |
+
}
|
58 |
|
59 |
+
.concept-card {
|
60 |
+
background: linear-gradient(145deg, #ffffff, #f0f0f0);
|
61 |
+
border-radius: 15px;
|
62 |
+
padding: 15px;
|
63 |
+
margin: 10px 0;
|
64 |
+
border-left: 5px solid var(--secondary);
|
65 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.05);
|
66 |
+
}
|
67 |
|
68 |
+
.stButton>button {
|
69 |
+
background: linear-gradient(45deg, var(--primary), var(--secondary));
|
70 |
+
color: white;
|
71 |
+
border-radius: 12px;
|
72 |
+
padding: 10px 24px;
|
73 |
+
font-weight: bold;
|
74 |
+
font-size: 18px;
|
75 |
+
border: none;
|
76 |
+
transition: all 0.3s;
|
77 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
|
79 |
+
.stButton>button:hover {
|
80 |
+
transform: scale(1.05);
|
81 |
+
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
|
82 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
+
.stTextInput>div>div>input {
|
85 |
+
border-radius: 12px;
|
86 |
+
padding: 12px;
|
87 |
+
border: 2px solid var(--accent);
|
88 |
+
}
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
+
.tabs {
|
91 |
+
display: flex;
|
92 |
+
gap: 10px;
|
93 |
+
margin-bottom: 20px;
|
94 |
+
overflow-x: auto;
|
95 |
+
}
|
96 |
|
97 |
+
.tab {
|
98 |
+
padding: 10px 20px;
|
99 |
+
background-color: var(--accent);
|
100 |
+
border-radius: 10px;
|
101 |
+
cursor: pointer;
|
102 |
+
font-weight: bold;
|
103 |
+
white-space: nowrap;
|
104 |
+
}
|
105 |
|
106 |
+
.tab.active {
|
107 |
+
background-color: var(--secondary);
|
108 |
+
color: white;
|
109 |
+
}
|
110 |
|
111 |
+
@media (max-width: 768px) {
|
112 |
+
.tabs {
|
113 |
+
flex-wrap: wrap;
|
114 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
}
|
116 |
+
</style>
|
117 |
+
""", unsafe_allow_html=True)
|
118 |
|
119 |
+
# Concept database
|
120 |
+
CONCEPTS = {
|
121 |
+
"loop": {
|
122 |
+
"name": "Loop",
|
123 |
+
"emoji": "🔄",
|
124 |
+
"description": "Loops repeat actions multiple times",
|
125 |
+
"example": "for i in range(5):\n print('Hello!')",
|
126 |
+
"color": "#FF9E6D"
|
127 |
+
},
|
128 |
+
"conditional": {
|
129 |
+
"name": "Conditional",
|
130 |
+
"emoji": "❓",
|
131 |
+
"description": "Conditionals make decisions in code",
|
132 |
+
"example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
|
133 |
+
"color": "#4ECDC4"
|
134 |
+
},
|
135 |
+
"function": {
|
136 |
+
"name": "Function",
|
137 |
+
"emoji": "✨",
|
138 |
+
"description": "Functions are reusable blocks of code",
|
139 |
+
"example": "def greet(name):\n print(f'Hello {name}!')",
|
140 |
+
"color": "#FFD166"
|
141 |
+
},
|
142 |
+
"variable": {
|
143 |
+
"name": "Variable",
|
144 |
+
"emoji": "📦",
|
145 |
+
"description": "Variables store information",
|
146 |
+
"example": "score = 10\nplayer = 'Alex'",
|
147 |
+
"color": "#FF6B6B"
|
148 |
+
},
|
149 |
+
"list": {
|
150 |
+
"name": "List",
|
151 |
+
"emoji": "📝",
|
152 |
+
"description": "Lists store collections of items",
|
153 |
+
"example": "fruits = ['apple', 'banana', 'orange']",
|
154 |
+
"color": "#1A535C"
|
155 |
}
|
156 |
+
}
|
157 |
|
158 |
+
# Free AI models via Hugging Face Inference API
|
159 |
+
HF_API_URL = "https://api-inference.huggingface.co/models/"
|
160 |
+
MODELS = {
|
161 |
+
"codegen": "Salesforce/codegen-350M-mono",
|
162 |
+
"story": "gpt2"
|
163 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
+
def analyze_story(story):
|
166 |
+
"""Analyze story using simple NLP and identify programming concepts"""
|
167 |
+
story_lower = story.lower()
|
168 |
+
detected_concepts = []
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
|
170 |
+
# Check for loops
|
171 |
+
if any(word in story_lower for word in ["times", "repeat", "again", "multiple"]):
|
172 |
+
detected_concepts.append("loop")
|
|
|
|
|
173 |
|
174 |
+
# Check for conditionals
|
175 |
+
if any(word in story_lower for word in ["if", "when", "unless", "whether"]):
|
176 |
+
detected_concepts.append("conditional")
|
|
|
|
|
177 |
|
178 |
+
# Check for functions
|
179 |
+
if any(word in story_lower for word in ["make", "create", "do", "perform", "cast"]):
|
180 |
+
detected_concepts.append("function")
|
|
|
|
|
|
|
|
|
181 |
|
182 |
+
# Check for variables
|
183 |
+
if any(word in story_lower for word in ["is", "has", "set to", "value"]):
|
184 |
+
detected_concepts.append("variable")
|
185 |
+
|
186 |
+
# Check for lists
|
187 |
+
if any(word in story_lower for word in ["and", "many", "several", "collection", "items"]):
|
188 |
+
detected_concepts.append("list")
|
189 |
+
|
190 |
+
return list(set(detected_concepts))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
+
def generate_animation_code(story, concepts):
|
193 |
+
"""Generate Python animation code using free Hugging Face model"""
|
194 |
try:
|
195 |
+
# For Hugging Face Spaces, we'll use a simplified approach
|
196 |
+
# since we can't run complex AI models for free
|
|
|
197 |
|
198 |
+
# Build a template-based code generator
|
199 |
+
characters = ["rabbit", "dragon", "cat", "dog", "knight", "wizard", "scientist", "pirate"]
|
200 |
+
actions = ["hop", "run", "fly", "jump", "cast", "collect", "change", "grow"]
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
|
202 |
+
character = random.choice(characters)
|
203 |
+
action = random.choice(actions)
|
204 |
+
count = random.randint(2, 5)
|
205 |
|
206 |
+
concept = concepts[0] if concepts else "loop"
|
|
|
|
|
|
|
|
|
|
|
207 |
|
208 |
+
if concept == "loop":
|
209 |
+
code = f"""# {story}
|
210 |
+
import matplotlib.pyplot as plt
|
211 |
+
import matplotlib.animation as animation
|
212 |
+
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
|
214 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
215 |
+
ax.set_xlim(0, 10)
|
216 |
+
ax.set_ylim(0, 10)
|
217 |
+
ax.set_title('{" ".join(story.split()[:5])}...')
|
218 |
+
ax.set_facecolor('#f0f8ff')
|
219 |
+
|
220 |
+
{character}_x = [2]
|
221 |
+
{character}_y = [2]
|
222 |
+
{character}_dot, = ax.plot({character}_x, {character}_y, 'o', markersize=20, color='orange')
|
223 |
+
|
224 |
+
carrot_x = [8]
|
225 |
+
carrot_y = [8]
|
226 |
+
carrot_dot, = ax.plot(carrot_x, carrot_y, 'o', markersize=15, color='orange')
|
227 |
+
|
228 |
+
def animate(frame):
|
229 |
+
# This is a loop that runs {count} times
|
230 |
+
if frame < {count}:
|
231 |
+
# Move the {character} closer to the target
|
232 |
+
new_x = {character}_x[-1] + 1.5
|
233 |
+
new_y = {character}_y[-1] + 1.5
|
234 |
+
{character}_x.append(new_x)
|
235 |
+
{character}_y.append(new_y)
|
236 |
+
{character}_dot.set_data({character}_x[-1], {character}_y[-1])
|
237 |
+
|
238 |
+
return {character}_dot, carrot_dot
|
239 |
+
|
240 |
+
ani = animation.FuncAnimation(fig, animate, frames={count+1}, interval=1000)
|
241 |
+
plt.grid(True, linestyle='--', alpha=0.7)
|
242 |
+
plt.show()
|
243 |
+
"""
|
244 |
+
elif concept == "conditional":
|
245 |
+
code = f"""# {story}
|
246 |
+
import matplotlib.pyplot as plt
|
247 |
+
import numpy as np
|
248 |
|
249 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
250 |
+
ax.set_xlim(0, 10)
|
251 |
+
ax.set_ylim(0, 10)
|
252 |
+
ax.set_title('{" ".join(story.split()[:5])}...')
|
253 |
+
ax.set_facecolor('#f0f8ff')
|
254 |
+
|
255 |
+
# This variable stores our condition
|
256 |
+
is_unlocked = {'True' if random.choice([True, False]) else 'False'}
|
257 |
+
|
258 |
+
# Draw treasure chest
|
259 |
+
chest = plt.Rectangle((4, 4), 2, 2, fill=True, color='brown')
|
260 |
+
ax.add_patch(chest)
|
261 |
+
|
262 |
+
# Draw lock if not unlocked
|
263 |
+
if not is_unlocked:
|
264 |
+
lock = plt.Rectangle((5, 6), 0.5, 0.8, fill=True, color='gray')
|
265 |
+
ax.add_patch(lock)
|
266 |
+
ax.text(5, 3, 'Locked! Find the key', fontsize=12, ha='center')
|
267 |
+
else:
|
268 |
+
# Show open chest with treasure
|
269 |
+
chest.set_fill(False)
|
270 |
+
chest.set_edgecolor('brown')
|
271 |
+
chest.set_linestyle('--')
|
272 |
+
ax.text(5, 5, '$$$', fontsize=20, ha='center')
|
273 |
+
ax.text(5, 3, 'Treasure Acquired!', fontsize=12, ha='center')
|
274 |
+
|
275 |
+
plt.grid(True, linestyle='--', alpha=0.7)
|
276 |
+
plt.show()
|
277 |
+
"""
|
278 |
else:
|
279 |
+
code = f"""# {story}
|
280 |
+
import matplotlib.pyplot as plt
|
281 |
+
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
282 |
|
283 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
284 |
+
ax.set_xlim(0, 10)
|
285 |
+
ax.set_ylim(0, 10)
|
286 |
+
ax.set_title('{" ".join(story.split()[:5])}...')
|
287 |
+
ax.set_facecolor('#f0f8ff')
|
288 |
+
|
289 |
+
# Draw the scene
|
290 |
+
ax.text(5, 8, '✨ Your Story ✨', fontsize=20, ha='center')
|
291 |
+
ax.text(5, 7, f'"{story[:50]}{"..." if len(story) > 50 else ""}"',
|
292 |
+
fontsize=14, ha='center', wrap=True)
|
293 |
+
|
294 |
+
# Draw coding concept icons
|
295 |
+
y_pos = 5
|
296 |
+
for concept in {concepts}:
|
297 |
+
details = {CONCEPTS}[concept]
|
298 |
+
ax.text(2, y_pos, details['emoji'], fontsize=30, ha='center')
|
299 |
+
ax.text(5, y_pos, details['name'], fontsize=16, ha='left')
|
300 |
+
y_pos -= 1
|
301 |
+
|
302 |
+
ax.text(5, 2, 'Generated with Python!', fontsize=14, ha='center', style='italic')
|
303 |
+
plt.grid(True, linestyle='--', alpha=0.7)
|
304 |
+
plt.show()
|
305 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
|
307 |
+
return code
|
308 |
+
except Exception as e:
|
309 |
+
return f"# Error generating code\nprint('Could not generate code: {str(e)}')"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
|
311 |
+
def create_matplotlib_animation(code):
|
312 |
+
"""Create an animation using matplotlib"""
|
313 |
+
try:
|
314 |
+
# Create a namespace to execute the code
|
315 |
+
namespace = {}
|
316 |
+
exec(code, namespace)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
|
318 |
+
# Get the figure from the executed code
|
319 |
+
fig = plt.gcf()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
|
321 |
+
# Create a BytesIO object to save the animation
|
322 |
+
buf = io.BytesIO()
|
323 |
+
fig.savefig(buf, format='png')
|
324 |
+
buf.seek(0)
|
325 |
+
image = Image.open(buf)
|
326 |
|
327 |
+
return image
|
328 |
+
except Exception as e:
|
329 |
+
st.error(f"Animation error: {str(e)}")
|
330 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
|
332 |
+
def generate_story_image(story):
|
333 |
+
"""Generate a story image using a free API"""
|
334 |
+
try:
|
335 |
+
# For Hugging Face deployment, we'll use placeholders
|
336 |
+
# In a real deployment, you could use Hugging Face's diffusion models
|
337 |
+
|
338 |
+
# Create a simple image with text
|
339 |
+
fig, ax = plt.subplots(figsize=(10, 6))
|
340 |
+
ax.set_facecolor('#f0f8ff')
|
341 |
+
ax.set_xlim(0, 10)
|
342 |
+
ax.set_ylim(0, 10)
|
343 |
+
ax.axis('off')
|
344 |
+
|
345 |
+
# Add title
|
346 |
+
ax.text(5, 8, '✨ Your Story ✨', fontsize=24,
|
347 |
+
ha='center', color='purple', fontweight='bold')
|
348 |
+
|
349 |
+
# Add story text (wrapped)
|
350 |
+
words = story.split()
|
351 |
+
lines = []
|
352 |
+
current_line = []
|
353 |
+
|
354 |
+
for word in words:
|
355 |
+
if len(' '.join(current_line + [word])) < 60:
|
356 |
+
current_line.append(word)
|
357 |
+
else:
|
358 |
+
lines.append(' '.join(current_line))
|
359 |
+
current_line = [word]
|
360 |
+
|
361 |
+
if current_line:
|
362 |
+
lines.append(' '.join(current_line))
|
363 |
+
|
364 |
+
for i, line in enumerate(lines):
|
365 |
+
ax.text(5, 6.5 - i*0.7, line, fontsize=14, ha='center')
|
366 |
+
|
367 |
+
# Add decoration
|
368 |
+
ax.text(2, 2, '🐰', fontsize=40, ha='center')
|
369 |
+
ax.text(8, 2, '🐉', fontsize=40, ha='center')
|
370 |
+
ax.text(5, 1, 'Created with StoryCoder', fontsize=12,
|
371 |
+
ha='center', style='italic', color='gray')
|
372 |
+
|
373 |
+
# Save to buffer
|
374 |
+
buf = io.BytesIO()
|
375 |
+
plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0.5)
|
376 |
+
buf.seek(0)
|
377 |
+
image = Image.open(buf)
|
378 |
+
plt.close()
|
379 |
+
|
380 |
+
return image
|
381 |
+
except Exception as e:
|
382 |
+
st.error(f"Image generation error: {str(e)}")
|
383 |
+
return None
|
384 |
+
|
385 |
+
def main():
|
386 |
+
"""Main application function"""
|
387 |
+
st.title("🧙♂️ StoryCoder - Learn Python Through Stories!")
|
388 |
+
st.subheader("Turn your story into an animation and discover coding secrets!")
|
389 |
+
|
390 |
+
# Initialize session state
|
391 |
+
if 'story' not in st.session_state:
|
392 |
+
st.session_state.story = ""
|
393 |
+
if 'concepts' not in st.session_state:
|
394 |
+
st.session_state.concepts = []
|
395 |
+
if 'animation_code' not in st.session_state:
|
396 |
+
st.session_state.animation_code = ""
|
397 |
+
if 'active_tab' not in st.session_state:
|
398 |
+
st.session_state.active_tab = "story"
|
399 |
+
if 'animation_image' not in st.session_state:
|
400 |
+
st.session_state.animation_image = None
|
401 |
+
|
402 |
+
# Create tabs
|
403 |
+
tabs = st.empty()
|
404 |
+
tab_cols = st.columns(5)
|
405 |
+
with tab_cols[0]:
|
406 |
+
if st.button("📖 Create Story"):
|
407 |
+
st.session_state.active_tab = "story"
|
408 |
+
with tab_cols[1]:
|
409 |
+
if st.button("🎬 Animation"):
|
410 |
+
st.session_state.active_tab = "animation"
|
411 |
+
with tab_cols[2]:
|
412 |
+
if st.button("🔍 Concepts"):
|
413 |
+
st.session_state.active_tab = "concepts"
|
414 |
+
with tab_cols[3]:
|
415 |
+
if st.button("💻 Code"):
|
416 |
+
st.session_state.active_tab = "code"
|
417 |
+
with tab_cols[4]:
|
418 |
+
if st.button("🔄 Reset"):
|
419 |
+
st.session_state.story = ""
|
420 |
+
st.session_state.concepts = []
|
421 |
+
st.session_state.animation_code = ""
|
422 |
+
st.session_state.animation_image = None
|
423 |
+
st.session_state.active_tab = "story"
|
424 |
+
|
425 |
+
# Story creation tab
|
426 |
+
if st.session_state.active_tab == "story":
|
427 |
+
with st.container():
|
428 |
+
st.header("📖 Create Your Story")
|
429 |
+
st.write("Write a short story (2-5 sentences) and I'll turn it into an animation!")
|
430 |
|
431 |
+
story = st.text_area(
|
432 |
+
"Your story:",
|
433 |
+
height=200,
|
434 |
+
placeholder="Once upon a time, a rabbit hopped 3 times to reach a carrot...",
|
435 |
+
value=st.session_state.story,
|
436 |
+
key="story_input"
|
437 |
+
)
|
438 |
|
439 |
+
if st.button("Create Animation!", use_container_width=True):
|
440 |
+
if len(story) < 10:
|
441 |
+
st.error("Your story needs to be at least 10 characters long!")
|
442 |
+
else:
|
443 |
+
st.session_state.story = story
|
444 |
+
with st.spinner("🧠 Analyzing your story for coding concepts..."):
|
445 |
+
st.session_state.concepts = analyze_story(story)
|
446 |
+
|
447 |
+
with st.spinner("✨ Generating animation code..."):
|
448 |
+
st.session_state.animation_code = generate_animation_code(
|
449 |
+
story, st.session_state.concepts
|
450 |
+
)
|
451 |
+
|
452 |
+
with st.spinner("🎨 Creating your animation..."):
|
453 |
+
st.session_state.animation_image = create_matplotlib_animation(
|
454 |
+
st.session_state.animation_code
|
455 |
+
)
|
456 |
+
|
457 |
+
st.session_state.active_tab = "animation"
|
458 |
+
st.experimental_rerun()
|
459 |
|
460 |
+
# Show examples
|
461 |
+
st.subheader("✨ Story Examples")
|
462 |
+
col1, col2, col3 = st.columns(3)
|
463 |
+
with col1:
|
464 |
+
st.caption("Loop Example")
|
465 |
+
st.code('"A dragon breathes fire 5 times at the castle"', language="text")
|
466 |
+
with col2:
|
467 |
+
st.caption("Conditional Example")
|
468 |
+
st.code('"If it rains, the cat stays inside, else it goes out"', language="text")
|
469 |
+
with col3:
|
470 |
+
st.caption("Function Example")
|
471 |
+
st.code('"A wizard casts a spell to make flowers grow"', language="text")
|
472 |
+
|
473 |
+
# Animation tab
|
474 |
+
elif st.session_state.active_tab == "animation":
|
475 |
+
st.header("🎬 Your Story Animation")
|
476 |
+
|
477 |
+
if not st.session_state.animation_image:
|
478 |
+
st.warning("Please create a story first!")
|
479 |
+
st.session_state.active_tab = "story"
|
480 |
+
st.experimental_rerun()
|
481 |
+
|
482 |
+
# Display animation
|
483 |
+
st.image(st.session_state.animation_image, use_column_width=True)
|
484 |
+
|
485 |
+
st.success("✨ Animation created successfully!")
|
486 |
+
st.caption("This animation was generated with Python code based on your story!")
|
487 |
+
|
488 |
+
if st.button("Reveal Coding Secrets!", use_container_width=True):
|
489 |
+
st.session_state.active_tab = "concepts"
|
490 |
+
st.experimental_rerun()
|
491 |
+
|
492 |
+
# Concepts tab
|
493 |
+
elif st.session_state.active_tab == "concepts":
|
494 |
+
st.header("🔍 Coding Concepts in Your Story")
|
495 |
+
st.subheader("We secretly used these programming concepts:")
|
496 |
+
|
497 |
+
if not st.session_state.concepts:
|
498 |
+
st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'make'.")
|
499 |
+
else:
|
500 |
+
for concept in st.session_state.concepts:
|
501 |
+
if concept in CONCEPTS:
|
502 |
+
details = CONCEPTS[concept]
|
503 |
+
st.markdown(f"""
|
504 |
+
<div class="concept-card" style="border-left: 5px solid {details['color']};">
|
505 |
+
<div style="display:flex; align-items:center; gap:15px;">
|
506 |
+
<span style="font-size:36px;">{details['emoji']}</span>
|
507 |
+
<h3 style="color:{details['color']};">{details['name']}</h3>
|
508 |
+
</div>
|
509 |
+
<p>{details['description']}</p>
|
510 |
+
<pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
|
511 |
+
</div>
|
512 |
+
""", unsafe_allow_html=True)
|
513 |
+
|
514 |
+
if st.button("See the Magic Code!", use_container_width=True):
|
515 |
+
st.session_state.active_tab = "code"
|
516 |
+
st.experimental_rerun()
|
517 |
+
|
518 |
+
# Code tab
|
519 |
+
elif st.session_state.active_tab == "code":
|
520 |
+
st.header("💻 The Magic Code Behind Your Animation")
|
521 |
+
st.write("Here's the Python code that created your animation:")
|
522 |
+
|
523 |
+
if st.session_state.animation_code:
|
524 |
+
st.code(st.session_state.animation_code, language="python")
|
525 |
+
|
526 |
+
# Download button
|
527 |
+
st.download_button(
|
528 |
+
label="Download Animation Code",
|
529 |
+
data=st.session_state.animation_code,
|
530 |
+
file_name="story_animation.py",
|
531 |
+
mime="text/python",
|
532 |
+
use_container_width=True
|
533 |
+
)
|
534 |
+
|
535 |
+
st.write("You can run this code on your computer to see the animation!")
|
536 |
+
else:
|
537 |
+
st.warning("No code generated yet!")
|
538 |
|
539 |
+
if st.button("Create Another Story!", use_container_width=True):
|
540 |
+
st.session_state.active_tab = "story"
|
541 |
+
st.session_state.story = ""
|
542 |
+
st.experimental_rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
543 |
|
544 |
+
if __name__ == "__main__":
|
545 |
+
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|