sunbal7 commited on
Commit
af8eab3
ยท
verified ยท
1 Parent(s): c7e7458

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +376 -60
app.py CHANGED
@@ -2,13 +2,18 @@
2
  import streamlit as st
3
  import time
4
  import random
5
- from transformers import pipeline
6
- from PIL import Image, ImageDraw, ImageFont
7
- import numpy as np
8
  import textwrap
9
  import os
10
  import base64
11
  from io import BytesIO
 
 
 
 
 
 
 
12
 
13
  # Set up the page
14
  st.set_page_config(
@@ -18,22 +23,26 @@ st.set_page_config(
18
  initial_sidebar_state="expanded"
19
  )
20
 
 
 
 
21
  # Custom CSS
22
  st.markdown("""
23
  <style>
24
- @import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
25
 
26
  .stApp {
27
  background: linear-gradient(135deg, #6e8efb, #a777e3);
28
  }
29
 
30
  .header {
31
- font-family: 'Comic Neue', cursive;
32
  color: white;
33
  text-align: center;
34
- font-size: 3.5rem;
35
  text-shadow: 3px 3px 0 #000;
36
  padding: 1rem;
 
37
  }
38
 
39
  .subheader {
@@ -50,6 +59,7 @@ st.markdown("""
50
  padding: 2rem;
51
  box-shadow: 0 8px 16px rgba(0,0,0,0.2);
52
  margin-bottom: 2rem;
 
53
  }
54
 
55
  .robot-speech {
@@ -60,6 +70,7 @@ st.markdown("""
60
  font-size: 1.2rem;
61
  position: relative;
62
  margin-top: 2rem;
 
63
  }
64
 
65
  .robot-speech:after {
@@ -72,7 +83,7 @@ st.markdown("""
72
  }
73
 
74
  .generate-btn {
75
- background: #ff5722 !important;
76
  color: white !important;
77
  font-weight: bold !important;
78
  font-size: 1.2rem !important;
@@ -82,11 +93,14 @@ st.markdown("""
82
  box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
83
  transition: all 0.3s !important;
84
  margin-top: 1rem;
 
 
85
  }
86
 
87
  .generate-btn:hover {
88
  transform: scale(1.05) !important;
89
  box-shadow: 0 6px 12px rgba(0,0,0,0.4) !important;
 
90
  }
91
 
92
  .code-block {
@@ -94,10 +108,11 @@ st.markdown("""
94
  color: #f8f8f2;
95
  padding: 1rem;
96
  border-radius: 10px;
97
- font-family: monospace;
98
  font-size: 1.1rem;
99
  margin: 1rem 0;
100
  overflow-x: auto;
 
101
  }
102
 
103
  .animation-frame {
@@ -105,6 +120,60 @@ st.markdown("""
105
  border-radius: 10px;
106
  margin: 5px;
107
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  </style>
109
  """, unsafe_allow_html=True)
110
 
@@ -122,10 +191,13 @@ def load_models():
122
  # Named entity recognition for identifying objects
123
  ner_model = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english")
124
 
125
- return story_to_code, sentiment_analyzer, ner_model
 
 
 
126
  except Exception as e:
127
  st.error(f"Error loading models: {e}")
128
- return None, None, None
129
 
130
  # Image generation functions
131
  def create_storyboard_image(text, width=400, height=300):
@@ -157,24 +229,38 @@ def create_storyboard_image(text, width=400, height=300):
157
 
158
  return img
159
 
160
- def generate_sprite_animation(story, num_frames=4):
161
  """Generate a sprite-based animation from story"""
162
  frames = []
163
  width, height = 300, 200
164
 
165
  for i in range(num_frames):
166
- # Create base image
167
- img = Image.new('RGB', (width, height), color=(0, 0, 30))
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  draw = ImageDraw.Draw(img)
169
 
170
- # Draw stars
171
- for _ in range(30):
172
- x = random.randint(0, width)
173
- y = random.randint(0, height)
174
- draw.ellipse([x, y, x+2, y+2], fill=(255, 255, 255))
 
175
 
176
  # Draw moving elements based on frame
177
- if "spaceship" in story.lower():
178
  ship_x = 50 + i * 60
179
  ship_y = 80
180
  draw.polygon([(ship_x, ship_y), (ship_x+30, ship_y),
@@ -186,44 +272,75 @@ def generate_sprite_animation(story, num_frames=4):
186
  laser_y = ship_y - 20 + j*5
187
  draw.line([(laser_x, laser_y), (width, laser_y)], fill=(255, 0, 0), width=2)
188
 
189
- if "alien" in story.lower():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  alien_x = 200
191
  alien_y = 100 - i*10
192
  draw.ellipse([alien_x, alien_y, alien_x+20, alien_y+20], fill=(50, 205, 50))
193
  draw.ellipse([alien_x+5, alien_y+5, alien_x+7, alien_y+7], fill=(0, 0, 0))
194
  draw.ellipse([alien_x+13, alien_y+5, alien_x+15, alien_y+7], fill=(0, 0, 0))
195
 
196
- if "dragon" in story.lower():
197
- dragon_x = 150 + i*20
198
- dragon_y = 150
 
 
 
 
 
 
 
199
  draw.ellipse([dragon_x, dragon_y, dragon_x+40, dragon_y+20], fill=(178, 34, 34))
200
  draw.line([(dragon_x+40, dragon_y+10), (dragon_x+60, dragon_y)], fill=(178, 34, 34), width=3)
201
-
202
- if "fire" in story.lower() and i > 0:
203
- for j in range(5):
204
- flame_x = dragon_x + 60
205
- flame_y = dragon_y - j*5
206
- flame_size = random.randint(5, 15)
207
- draw.ellipse([flame_x, flame_y, flame_x+flame_size, flame_y+flame_size],
208
- fill=(255, random.randint(100, 200), 0))
209
 
210
  frames.append(img)
211
 
212
  return frames
213
 
214
- def generate_code_explanation(story, story_to_code):
215
  """Generate code explanation using open-source model"""
216
  try:
217
  # Create a prompt for the model
218
- prompt = f"Explain how this story would be translated to code: '{story}'. Show a code snippet and explanation."
219
 
220
  # Generate text
221
- result = story_to_code(
222
  prompt,
223
- max_length=150,
224
  num_return_sequences=1,
225
- temperature=0.7,
226
- top_k=50
227
  )
228
 
229
  return result[0]['generated_text']
@@ -232,6 +349,34 @@ def generate_code_explanation(story, story_to_code):
232
  return f"""See how your story became real code? For example, when you wrote "{story.split()[0]}",
233
  we used code like: character.move(). That's how we turn words into actions!"""
234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  # Header section
236
  st.markdown('<div class="header">CodeTales โœจ</div>', unsafe_allow_html=True)
237
  st.markdown('<div class="subheader">Storytime + Coding Magic</div>', unsafe_allow_html=True)
@@ -251,10 +396,13 @@ with st.expander("โœจ How It Works (Like Baking a Cake) ๐ŸŽ‚"):
251
 
252
  **4. Robot Teacher Explains ๐Ÿค–**
253
  Tavus shows: *"See? 'spaceship.move_right()' makes it fly! That's coding!"*
 
 
 
254
  """)
255
 
256
  # Load models
257
- story_to_code, sentiment_analyzer, ner_model = load_models()
258
 
259
  # Initialize session state
260
  if 'animation_generated' not in st.session_state:
@@ -265,19 +413,103 @@ if 'animation_frames' not in st.session_state:
265
  st.session_state.animation_frames = []
266
  if 'code_explanation' not in st.session_state:
267
  st.session_state.code_explanation = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
  # Main content
 
 
 
270
  col1, col2 = st.columns([1, 1])
271
 
272
  with col1:
273
  st.markdown('<div class="story-box">', unsafe_allow_html=True)
274
  st.markdown("### ๐Ÿ“– Write Your Story Here:")
 
 
 
 
 
 
 
 
275
  story_text = st.text_area(
276
  "Tell your adventure story...",
277
  height=200,
278
  placeholder="Once upon a time, a brave spaceship zoomed through space, shooting lasers at alien spaceships...",
279
  label_visibility="collapsed",
280
- value=st.session_state.story_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  )
282
 
283
  # Generate button with animation
@@ -288,28 +520,41 @@ with col1:
288
 
289
  with st.spinner("๐Ÿง™โ€โ™‚๏ธ Cooking your story in the magic oven..."):
290
  # Generate animation frames
291
- st.session_state.animation_frames = generate_sprite_animation(story_text)
 
 
 
 
292
 
293
  # Generate code explanation
294
- st.session_state.code_explanation = generate_code_explanation(story_text, story_to_code)
295
 
296
- # Analyze story sentiment (just for fun)
297
- if sentiment_analyzer:
298
- sentiment = sentiment_analyzer(story_text[:512])[0]
299
- st.session_state.sentiment = f"Your story feels {sentiment['label'].lower()}! (Confidence: {sentiment['score']:.2f})"
300
- else:
301
- st.session_state.sentiment = "Your story is full of adventure!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
- # Identify story characters
304
- if ner_model and len(story_text) > 10:
305
- entities = ner_model(story_text[:256])
306
- characters = list(set([ent['word'] for ent in entities if ent['entity'] in ['B-PER', 'I-PER']]))
307
- if characters:
308
- st.session_state.characters = f"Main characters: {', '.join(characters)}"
309
- else:
310
- st.session_state.characters = "Exciting characters are ready for action!"
311
  else:
312
- st.session_state.characters = "Your heroes are preparing for adventure!"
313
  else:
314
  st.warning("Please enter a story first!")
315
  st.markdown('</div>', unsafe_allow_html=True)
@@ -345,10 +590,21 @@ with col2:
345
  use_container_width=True
346
  )
347
 
348
- # Display story analysis
349
- st.success("โœจ Animation created!")
350
- st.info(f"๐ŸŽญ {st.session_state.characters}")
351
- st.info(f"๐Ÿ˜Š {st.session_state.sentiment}")
 
 
 
 
 
 
 
 
 
 
 
352
 
353
  elif st.session_state.animation_generated:
354
  st.warning("Couldn't generate animation. Try a different story!")
@@ -451,6 +707,64 @@ if st.session_state.animation_generated and st.session_state.story_text:
451
 
452
  st.markdown("</div>", unsafe_allow_html=True)
453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  # Benefits section
455
  st.markdown("""
456
  ## โค Why Everyone Will Love CodeTales
@@ -462,6 +776,8 @@ st.markdown("""
462
  | ๐ŸŽฎ Creates personal video games | โž— Reinforces math fundamentals |
463
  | ๐Ÿ˜„ Makes learning fun and exciting | ๐Ÿงฉ Encourages problem-solving abilities |
464
  | ๐ŸŒŸ 100% private with no API keys | ๐Ÿ’ฐ Completely free and open-source |
 
 
465
  """)
466
 
467
  # Footer
 
2
  import streamlit as st
3
  import time
4
  import random
5
+ import json
 
 
6
  import textwrap
7
  import os
8
  import base64
9
  from io import BytesIO
10
+ from PIL import Image, ImageDraw, ImageFont
11
+ import numpy as np
12
+ from gtts import gTTS
13
+ from pygame import mixer
14
+ import tempfile
15
+ import requests
16
+ from transformers import pipeline, set_seed
17
 
18
  # Set up the page
19
  st.set_page_config(
 
23
  initial_sidebar_state="expanded"
24
  )
25
 
26
+ # Initialize pygame mixer
27
+ mixer.init()
28
+
29
  # Custom CSS
30
  st.markdown("""
31
  <style>
32
+ @import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Press+Start+2P&display=swap');
33
 
34
  .stApp {
35
  background: linear-gradient(135deg, #6e8efb, #a777e3);
36
  }
37
 
38
  .header {
39
+ font-family: 'Press Start 2P', cursive;
40
  color: white;
41
  text-align: center;
42
+ font-size: 2.5rem;
43
  text-shadow: 3px 3px 0 #000;
44
  padding: 1rem;
45
+ letter-spacing: 2px;
46
  }
47
 
48
  .subheader {
 
59
  padding: 2rem;
60
  box-shadow: 0 8px 16px rgba(0,0,0,0.2);
61
  margin-bottom: 2rem;
62
+ border: 4px solid #ff6b6b;
63
  }
64
 
65
  .robot-speech {
 
70
  font-size: 1.2rem;
71
  position: relative;
72
  margin-top: 2rem;
73
+ border: 3px solid #2e7d32;
74
  }
75
 
76
  .robot-speech:after {
 
83
  }
84
 
85
  .generate-btn {
86
+ background: linear-gradient(135deg, #ff5722, #ff9800) !important;
87
  color: white !important;
88
  font-weight: bold !important;
89
  font-size: 1.2rem !important;
 
93
  box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
94
  transition: all 0.3s !important;
95
  margin-top: 1rem;
96
+ font-family: 'Press Start 2P', cursive !important;
97
+ letter-spacing: 1px;
98
  }
99
 
100
  .generate-btn:hover {
101
  transform: scale(1.05) !important;
102
  box-shadow: 0 6px 12px rgba(0,0,0,0.4) !important;
103
+ background: linear-gradient(135deg, #ff7043, #ffa726) !important;
104
  }
105
 
106
  .code-block {
 
108
  color: #f8f8f2;
109
  padding: 1rem;
110
  border-radius: 10px;
111
+ font-family: 'Courier New', monospace;
112
  font-size: 1.1rem;
113
  margin: 1rem 0;
114
  overflow-x: auto;
115
+ border-left: 4px solid #ff9800;
116
  }
117
 
118
  .animation-frame {
 
120
  border-radius: 10px;
121
  margin: 5px;
122
  }
123
+
124
+ .achievement-badge {
125
+ background: #ffd54f;
126
+ color: #333;
127
+ border-radius: 50%;
128
+ width: 60px;
129
+ height: 60px;
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ font-size: 1.5rem;
134
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
135
+ margin: 0 auto;
136
+ }
137
+
138
+ .character-option {
139
+ border: 3px solid transparent;
140
+ border-radius: 10px;
141
+ padding: 5px;
142
+ margin: 5px;
143
+ cursor: pointer;
144
+ transition: all 0.3s;
145
+ }
146
+
147
+ .character-option:hover {
148
+ transform: scale(1.05);
149
+ }
150
+
151
+ .character-option.selected {
152
+ border-color: #ff5722;
153
+ }
154
+
155
+ .tab-content {
156
+ padding: 1rem;
157
+ border: 2px solid #4caf50;
158
+ border-radius: 10px;
159
+ background: rgba(255, 255, 255, 0.8);
160
+ margin-top: 1rem;
161
+ }
162
+
163
+ .level-indicator {
164
+ background: #4caf50;
165
+ color: white;
166
+ border-radius: 20px;
167
+ padding: 0.5rem 1rem;
168
+ display: inline-block;
169
+ font-weight: bold;
170
+ margin-bottom: 1rem;
171
+ }
172
+
173
+ /* Progress bar styling */
174
+ .stProgress > div > div > div {
175
+ background-color: #ff5722 !important;
176
+ }
177
  </style>
178
  """, unsafe_allow_html=True)
179
 
 
191
  # Named entity recognition for identifying objects
192
  ner_model = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english")
193
 
194
+ # Text-to-text generation for better explanations
195
+ explanation_generator = pipeline("text2text-generation", model="google/flan-t5-base")
196
+
197
+ return story_to_code, sentiment_analyzer, ner_model, explanation_generator
198
  except Exception as e:
199
  st.error(f"Error loading models: {e}")
200
+ return None, None, None, None
201
 
202
  # Image generation functions
203
  def create_storyboard_image(text, width=400, height=300):
 
229
 
230
  return img
231
 
232
+ def generate_sprite_animation(story, character="spaceship", theme="space", num_frames=4):
233
  """Generate a sprite-based animation from story"""
234
  frames = []
235
  width, height = 300, 200
236
 
237
  for i in range(num_frames):
238
+ # Create base image with theme
239
+ if theme == "space":
240
+ bg_color = (0, 0, 30)
241
+ star_color = (255, 255, 255)
242
+ elif theme == "jungle":
243
+ bg_color = (0, 100, 0)
244
+ star_color = None # No stars in jungle
245
+ elif theme == "medieval":
246
+ bg_color = (139, 69, 19)
247
+ star_color = None
248
+ else:
249
+ bg_color = (0, 0, 30)
250
+ star_color = (255, 255, 255)
251
+
252
+ img = Image.new('RGB', (width, height), color=bg_color)
253
  draw = ImageDraw.Draw(img)
254
 
255
+ # Draw stars for space theme
256
+ if star_color:
257
+ for _ in range(30):
258
+ x = random.randint(0, width)
259
+ y = random.randint(0, height)
260
+ draw.ellipse([x, y, x+2, y+2], fill=star_color)
261
 
262
  # Draw moving elements based on frame
263
+ if character == "spaceship":
264
  ship_x = 50 + i * 60
265
  ship_y = 80
266
  draw.polygon([(ship_x, ship_y), (ship_x+30, ship_y),
 
272
  laser_y = ship_y - 20 + j*5
273
  draw.line([(laser_x, laser_y), (width, laser_y)], fill=(255, 0, 0), width=2)
274
 
275
+ elif character == "dragon":
276
+ dragon_x = 50 + i * 40
277
+ dragon_y = 100
278
+ # Draw dragon body
279
+ draw.ellipse([dragon_x, dragon_y, dragon_x+40, dragon_y+20], fill=(178, 34, 34))
280
+ # Draw dragon head
281
+ draw.ellipse([dragon_x+30, dragon_y-5, dragon_x+50, dragon_y+15], fill=(178, 34, 34))
282
+ # Draw wings
283
+ draw.ellipse([dragon_x+10, dragon_y-15, dragon_x+30, dragon_y], fill=(138, 43, 226))
284
+
285
+ if "fire" in story.lower() and i > 0:
286
+ for j in range(5):
287
+ flame_x = dragon_x + 50
288
+ flame_y = dragon_y + 5 - j*5
289
+ flame_size = random.randint(5, 15)
290
+ draw.ellipse([flame_x, flame_y, flame_x+flame_size, flame_y+flame_size],
291
+ fill=(255, random.randint(100, 200), 0))
292
+
293
+ elif character == "knight":
294
+ knight_x = 50 + i * 40
295
+ knight_y = 120
296
+ # Draw knight body
297
+ draw.rectangle([knight_x, knight_y, knight_x+20, knight_y+40], fill=(70, 70, 70))
298
+ # Draw knight head
299
+ draw.ellipse([knight_x+5, knight_y-15, knight_x+15, knight_y-5], fill=(210, 180, 140))
300
+ # Draw sword
301
+ draw.rectangle([knight_x+15, knight_y+10, knight_x+25, knight_y+15], fill=(192, 192, 192))
302
+ draw.polygon([(knight_x+25, knight_y+12), (knight_x+35, knight_y+10), (knight_x+35, knight_y+15)], fill=(192, 192, 192))
303
+
304
+ if "attack" in story.lower() and i % 2 == 1:
305
+ # Draw sword swing
306
+ draw.line([(knight_x+25, knight_y+12), (knight_x+45, knight_y-10)], fill=(255, 255, 0), width=2)
307
+
308
+ # Draw enemies based on theme
309
+ if theme == "space" and "alien" in story.lower():
310
  alien_x = 200
311
  alien_y = 100 - i*10
312
  draw.ellipse([alien_x, alien_y, alien_x+20, alien_y+20], fill=(50, 205, 50))
313
  draw.ellipse([alien_x+5, alien_y+5, alien_x+7, alien_y+7], fill=(0, 0, 0))
314
  draw.ellipse([alien_x+13, alien_y+5, alien_x+15, alien_y+7], fill=(0, 0, 0))
315
 
316
+ elif theme == "jungle" and "snake" in story.lower():
317
+ snake_x = 200
318
+ snake_y = 150 - i*5
319
+ for segment in range(5):
320
+ offset = segment * 5
321
+ draw.ellipse([snake_x+offset, snake_y+offset, snake_x+offset+15, snake_y+offset+15], fill=(0, 128, 0))
322
+
323
+ elif theme == "medieval" and "dragon" in story.lower() and character != "dragon":
324
+ dragon_x = 220
325
+ dragon_y = 80
326
  draw.ellipse([dragon_x, dragon_y, dragon_x+40, dragon_y+20], fill=(178, 34, 34))
327
  draw.line([(dragon_x+40, dragon_y+10), (dragon_x+60, dragon_y)], fill=(178, 34, 34), width=3)
 
 
 
 
 
 
 
 
328
 
329
  frames.append(img)
330
 
331
  return frames
332
 
333
+ def generate_code_explanation(story, explanation_generator):
334
  """Generate code explanation using open-source model"""
335
  try:
336
  # Create a prompt for the model
337
+ prompt = f"Explain to a child how this story would become code: '{story}'"
338
 
339
  # Generate text
340
+ result = explanation_generator(
341
  prompt,
342
+ max_length=200,
343
  num_return_sequences=1,
 
 
344
  )
345
 
346
  return result[0]['generated_text']
 
349
  return f"""See how your story became real code? For example, when you wrote "{story.split()[0]}",
350
  we used code like: character.move(). That's how we turn words into actions!"""
351
 
352
+ def text_to_speech(text, lang='en'):
353
+ """Convert text to speech using gTTS"""
354
+ try:
355
+ tts = gTTS(text=text, lang=lang, slow=False)
356
+ with tempfile.NamedTemporaryFile(delete=True, suffix='.mp3') as fp:
357
+ tts.save(fp.name)
358
+ return fp.name
359
+ except Exception as e:
360
+ st.error(f"Text-to-speech error: {e}")
361
+ return None
362
+
363
+ # Achievement system
364
+ ACHIEVEMENTS = {
365
+ "first_story": {"name": "Storyteller", "icon": "๐Ÿ“–", "description": "Created your first story!"},
366
+ "code_master": {"name": "Code Master", "icon": "๐Ÿ’ป", "description": "Used 5 different coding concepts"},
367
+ "animator": {"name": "Animator", "icon": "๐ŸŽฌ", "description": "Created 3 animations"},
368
+ "voice_artist": {"name": "Voice Artist", "icon": "๐ŸŽค", "description": "Used text-to-speech feature"},
369
+ "character_designer": {"name": "Character Designer", "icon": "๐Ÿ‘พ", "description": "Created a custom character"}
370
+ }
371
+
372
+ def unlock_achievement(achievement_id):
373
+ """Unlock an achievement for the user"""
374
+ if achievement_id not in st.session_state.achievements_unlocked:
375
+ st.session_state.achievements_unlocked.append(achievement_id)
376
+ st.session_state.new_achievement = achievement_id
377
+ return True
378
+ return False
379
+
380
  # Header section
381
  st.markdown('<div class="header">CodeTales โœจ</div>', unsafe_allow_html=True)
382
  st.markdown('<div class="subheader">Storytime + Coding Magic</div>', unsafe_allow_html=True)
 
396
 
397
  **4. Robot Teacher Explains ๐Ÿค–**
398
  Tavus shows: *"See? 'spaceship.move_right()' makes it fly! That's coding!"*
399
+
400
+ **5. Level Up! ๐Ÿ†**
401
+ Earn achievements as you learn and create!
402
  """)
403
 
404
  # Load models
405
+ story_to_code, sentiment_analyzer, ner_model, explanation_generator = load_models()
406
 
407
  # Initialize session state
408
  if 'animation_generated' not in st.session_state:
 
413
  st.session_state.animation_frames = []
414
  if 'code_explanation' not in st.session_state:
415
  st.session_state.code_explanation = ""
416
+ if 'selected_character' not in st.session_state:
417
+ st.session_state.selected_character = "spaceship"
418
+ if 'selected_theme' not in st.session_state:
419
+ st.session_state.selected_theme = "space"
420
+ if 'sound_enabled' not in st.session_state:
421
+ st.session_state.sound_enabled = True
422
+ if 'achievements_unlocked' not in st.session_state:
423
+ st.session_state.achievements_unlocked = []
424
+ if 'new_achievement' not in st.session_state:
425
+ st.session_state.new_achievement = None
426
+ if 'story_count' not in st.session_state:
427
+ st.session_state.story_count = 0
428
+ if 'level' not in st.session_state:
429
+ st.session_state.level = 1
430
+ if 'xp' not in st.session_state:
431
+ st.session_state.xp = 0
432
+ if 'voice_enabled' not in st.session_state:
433
+ st.session_state.voice_enabled = False
434
+ if 'custom_character' not in st.session_state:
435
+ st.session_state.custom_character = None
436
+
437
+ # Story templates
438
+ story_templates = {
439
+ "None": "",
440
+ "Space Adventure": "A brave spaceship zooms through space, shooting lasers at alien spaceships!",
441
+ "Dragon Quest": "A knight fights a fire-breathing dragon to save the princess in the castle.",
442
+ "Jungle Explorer": "An explorer runs through the jungle, jumping over snakes and swinging on vines."
443
+ }
444
 
445
  # Main content
446
+ st.markdown(f'<div class="level-indicator">Level {st.session_state.level} โœจ XP: {st.session_state.xp}/100</div>', unsafe_allow_html=True)
447
+ progress = st.progress(st.session_state.xp / 100)
448
+
449
  col1, col2 = st.columns([1, 1])
450
 
451
  with col1:
452
  st.markdown('<div class="story-box">', unsafe_allow_html=True)
453
  st.markdown("### ๐Ÿ“– Write Your Story Here:")
454
+
455
+ # Story template selector
456
+ selected_template = st.selectbox(
457
+ "Or choose a story template to start with:",
458
+ list(story_templates.keys()),
459
+ index=0
460
+ )
461
+
462
  story_text = st.text_area(
463
  "Tell your adventure story...",
464
  height=200,
465
  placeholder="Once upon a time, a brave spaceship zoomed through space, shooting lasers at alien spaceships...",
466
  label_visibility="collapsed",
467
+ value=story_templates[selected_template] if selected_template != "None" else st.session_state.story_text
468
+ )
469
+
470
+ # Character selection
471
+ st.markdown("### ๐Ÿง™ Choose Your Hero")
472
+ char_cols = st.columns(3)
473
+ characters = {
474
+ "spaceship": "๐Ÿš€ Spaceship",
475
+ "dragon": "๐Ÿ‰ Dragon",
476
+ "knight": "๐Ÿ›ก๏ธ Knight"
477
+ }
478
+
479
+ for i, (char_key, char_label) in enumerate(characters.items()):
480
+ with char_cols[i]:
481
+ if st.button(
482
+ char_label,
483
+ key=f"char_{char_key}",
484
+ use_container_width=True,
485
+ type="primary" if st.session_state.selected_character == char_key else "secondary"
486
+ ):
487
+ st.session_state.selected_character = char_key
488
+
489
+ # Theme selection
490
+ st.markdown("### ๐ŸŽจ Choose Your World")
491
+ theme_cols = st.columns(3)
492
+ themes = {
493
+ "space": "๐ŸŒŒ Space",
494
+ "jungle": "๐ŸŒฟ Jungle",
495
+ "medieval": "๐Ÿฐ Medieval"
496
+ }
497
+
498
+ for i, (theme_key, theme_label) in enumerate(themes.items()):
499
+ with theme_cols[i]:
500
+ if st.button(
501
+ theme_label,
502
+ key=f"theme_{theme_key}",
503
+ use_container_width=True,
504
+ type="primary" if st.session_state.selected_theme == theme_key else "secondary"
505
+ ):
506
+ st.session_state.selected_theme = theme_key
507
+
508
+ # Voice options
509
+ st.session_state.voice_enabled = st.toggle(
510
+ "๐Ÿ—ฃ๏ธ Enable Tavus Voice",
511
+ value=st.session_state.voice_enabled,
512
+ help="Hear Tavus explain coding concepts with text-to-speech"
513
  )
514
 
515
  # Generate button with animation
 
520
 
521
  with st.spinner("๐Ÿง™โ€โ™‚๏ธ Cooking your story in the magic oven..."):
522
  # Generate animation frames
523
+ st.session_state.animation_frames = generate_sprite_animation(
524
+ story_text,
525
+ character=st.session_state.selected_character,
526
+ theme=st.session_state.selected_theme
527
+ )
528
 
529
  # Generate code explanation
530
+ st.session_state.code_explanation = generate_code_explanation(story_text, explanation_generator)
531
 
532
+ # Update user progress
533
+ st.session_state.story_count += 1
534
+ st.session_state.xp += 15
535
+
536
+ # Check for level up
537
+ if st.session_state.xp >= 100:
538
+ st.session_state.level += 1
539
+ st.session_state.xp = st.session_state.xp - 100
540
+ st.session_state.new_level = True
541
+
542
+ # Unlock achievements
543
+ if st.session_state.story_count == 1:
544
+ unlock_achievement("first_story")
545
+
546
+ # Generate speech if enabled
547
+ if st.session_state.voice_enabled:
548
+ speech_file = text_to_speech(st.session_state.code_explanation)
549
+ if speech_file:
550
+ st.session_state.speech_file = speech_file
551
+ unlock_achievement("voice_artist")
552
 
553
+ # Simulate sound effect
554
+ if st.session_state.sound_enabled:
555
+ st.session_state.sound_message = "๐Ÿ”Š Sound effects added: Laser blasts, explosions, and more!"
 
 
 
 
 
556
  else:
557
+ st.session_state.sound_message = ""
558
  else:
559
  st.warning("Please enter a story first!")
560
  st.markdown('</div>', unsafe_allow_html=True)
 
590
  use_container_width=True
591
  )
592
 
593
+ # Display sound message
594
+ if st.session_state.sound_enabled and 'sound_message' in st.session_state:
595
+ st.info(st.session_state.sound_message)
596
+
597
+ # Display character and theme info
598
+ st.success(f"โœจ Your {characters[st.session_state.selected_character]} in the {themes[st.session_state.selected_theme]} world!")
599
+
600
+ # Play voice explanation
601
+ if st.session_state.voice_enabled and 'speech_file' in st.session_state:
602
+ if st.button("๐Ÿ”Š Play Tavus Explanation", use_container_width=True):
603
+ try:
604
+ mixer.music.load(st.session_state.speech_file)
605
+ mixer.music.play()
606
+ except:
607
+ st.warning("Couldn't play audio. Try enabling sound in your browser.")
608
 
609
  elif st.session_state.animation_generated:
610
  st.warning("Couldn't generate animation. Try a different story!")
 
707
 
708
  st.markdown("</div>", unsafe_allow_html=True)
709
 
710
+ # Achievements section
711
+ if st.session_state.achievements_unlocked:
712
+ st.markdown("### ๐Ÿ† Your Achievements")
713
+ achievement_cols = st.columns(5)
714
+ for i, ach_id in enumerate(st.session_state.achievements_unlocked):
715
+ with achievement_cols[i % 5]:
716
+ ach = ACHIEVEMENTS[ach_id]
717
+ st.markdown(f'<div class="achievement-badge">{ach["icon"]}</div>', unsafe_allow_html=True)
718
+ st.markdown(f"**{ach['name']}**")
719
+ st.caption(ach['description'])
720
+
721
+ # New achievement notification
722
+ if 'new_achievement' in st.session_state and st.session_state.new_achievement:
723
+ ach = ACHIEVEMENTS[st.session_state.new_achievement]
724
+ st.success(f"๐ŸŽ‰ Achievement Unlocked: {ach['name']} - {ach['description']}")
725
+ st.session_state.new_achievement = None
726
+
727
+ # New level notification
728
+ if 'new_level' in st.session_state and st.session_state.new_level:
729
+ st.balloons()
730
+ st.success(f"๐ŸŒŸ Level Up! You're now Level {st.session_state.level}!")
731
+ st.session_state.new_level = False
732
+
733
+ # Coding challenges section
734
+ st.markdown("### ๐Ÿ’ป Coding Challenges")
735
+ with st.expander("Complete challenges to earn XP!"):
736
+ challenge_cols = st.columns(3)
737
+
738
+ with challenge_cols[0]:
739
+ st.markdown("#### Challenge 1: The Mover")
740
+ st.code("""
741
+ # Make the character move to the right
742
+ character.move_right(10)
743
+ """)
744
+ if st.button("Run Challenge 1", key="challenge1"):
745
+ st.session_state.xp += 5
746
+ st.success("+5 XP! Character moved right!")
747
+
748
+ with challenge_cols[1]:
749
+ st.markdown("#### Challenge 2: The Jumper")
750
+ st.code("""
751
+ # Make the character jump
752
+ character.jump(20)
753
+ """)
754
+ if st.button("Run Challenge 2", key="challenge2"):
755
+ st.session_state.xp += 5
756
+ st.success("+5 XP! Character jumped!")
757
+
758
+ with challenge_cols[2]:
759
+ st.markdown("#### Challenge 3: The Shooter")
760
+ st.code("""
761
+ # Make the character shoot a laser
762
+ laser = character.shoot()
763
+ """)
764
+ if st.button("Run Challenge 3", key="challenge3"):
765
+ st.session_state.xp += 5
766
+ st.success("+5 XP! Laser fired!")
767
+
768
  # Benefits section
769
  st.markdown("""
770
  ## โค Why Everyone Will Love CodeTales
 
776
  | ๐ŸŽฎ Creates personal video games | โž— Reinforces math fundamentals |
777
  | ๐Ÿ˜„ Makes learning fun and exciting | ๐Ÿงฉ Encourages problem-solving abilities |
778
  | ๐ŸŒŸ 100% private with no API keys | ๐Ÿ’ฐ Completely free and open-source |
779
+ | ๐Ÿ† Achievement system motivates learning | ๐Ÿ“ˆ Progress tracking shows growth |
780
+ | ๐Ÿ—ฃ๏ธ Voice explanations for accessibility | ๐ŸŽจ Creative expression through stories |
781
  """)
782
 
783
  # Footer