# app.py import streamlit as st import time import random import textwrap from io import BytesIO from PIL import Image, ImageDraw, ImageFont import numpy as np from transformers import pipeline import base64 import re # Set up the page st.set_page_config( page_title="CodeTales ✨", page_icon="🚀", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS st.markdown(""" """, unsafe_allow_html=True) # Initialize AI models @st.cache_resource def load_models(): """Load open-source AI models""" try: # Named entity recognition for identifying objects ner_model = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english") # Text classification for theme detection classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli") # Text generation for code explanations explanation_generator = pipeline("text2text-generation", model="google/flan-t5-large") return ner_model, classifier, explanation_generator except Exception as e: st.error(f"Error loading models: {e}") return None, None, None # Image generation functions def create_storyboard_image(text, width=400, height=300): """Create a storyboard image from text""" # Create blank image img = Image.new('RGB', (width, height), color=(25, 25, 112)) # Dark blue background # Load a comic-style font (fallback to default if not available) try: font = ImageFont.truetype("comic.ttf", 16) except: font = ImageFont.load_default() draw = ImageDraw.Draw(img) # Draw title draw.text((10, 10), "Your Story Comes to Life!", fill=(255, 215, 0), font=font) # Draw text box draw.rectangle([10, 40, width-10, height-40], fill=(240, 248, 255), outline=(255, 215, 0), width=2) # Wrap text wrapped_text = textwrap.fill(text, width=40) draw.text((20, 50), wrapped_text, fill=(25, 25, 112), font=font) # Draw decorations draw.rectangle([width-50, height-30, width-30, height-10], fill=(220, 20, 60), outline=(255, 215, 0), width=1) draw.ellipse([20, height-50, 70, height], fill=(30, 144, 255), outline=(255, 215, 0), width=1) return img def generate_sprite_animation(story, character="spaceship", theme="space", num_frames=4): """Generate a sprite-based animation from story""" frames = [] width, height = 300, 200 for i in range(num_frames): # Create base image with theme if theme == "space": bg_color = (0, 0, 30) star_color = (255, 255, 255) elif theme == "jungle": bg_color = (0, 100, 0) star_color = None # No stars in jungle elif theme == "medieval": bg_color = (139, 69, 19) star_color = None elif theme == "underwater": bg_color = (0, 105, 148) star_color = None else: bg_color = (0, 0, 30) star_color = (255, 255, 255) img = Image.new('RGB', (width, height), color=bg_color) draw = ImageDraw.Draw(img) # Draw stars for space theme if star_color: for _ in range(30): x = random.randint(0, width) y = random.randint(0, height) draw.ellipse([x, y, x+2, y+2], fill=star_color) # Draw moving elements based on frame if character == "spaceship": ship_x = 50 + i * 60 ship_y = 80 draw.polygon([(ship_x, ship_y), (ship_x+30, ship_y), (ship_x+15, ship_y-20)], fill=(169, 169, 169)) if "shoot" in story.lower() and i > 1: for j in range(3): laser_x = ship_x + 15 laser_y = ship_y - 20 + j*5 draw.line([(laser_x, laser_y), (width, laser_y)], fill=(255, 0, 0), width=2) elif character == "dragon": dragon_x = 50 + i * 40 dragon_y = 100 # Draw dragon body draw.ellipse([dragon_x, dragon_y, dragon_x+40, dragon_y+20], fill=(178, 34, 34)) # Draw dragon head draw.ellipse([dragon_x+30, dragon_y-5, dragon_x+50, dragon_y+15], fill=(178, 34, 34)) # Draw wings draw.ellipse([dragon_x+10, dragon_y-15, dragon_x+30, dragon_y], fill=(138, 43, 226)) if "fire" in story.lower() and i > 0: for j in range(5): flame_x = dragon_x + 50 flame_y = dragon_y + 5 - j*5 flame_size = random.randint(5, 15) draw.ellipse([flame_x, flame_y, flame_x+flame_size, flame_y+flame_size], fill=(255, random.randint(100, 200), 0)) elif character == "knight": knight_x = 50 + i * 40 knight_y = 120 # Draw knight body draw.rectangle([knight_x, knight_y, knight_x+20, knight_y+40], fill=(70, 70, 70)) # Draw knight head draw.ellipse([knight_x+5, knight_y-15, knight_x+15, knight_y-5], fill=(210, 180, 140)) # Draw sword draw.rectangle([knight_x+15, knight_y+10, knight_x+25, knight_y+15], fill=(192, 192, 192)) draw.polygon([(knight_x+25, knight_y+12), (knight_x+35, knight_y+10), (knight_x+35, knight_y+15)], fill=(192, 192, 192)) if "attack" in story.lower() and i % 2 == 1: # Draw sword swing draw.line([(knight_x+25, knight_y+12), (knight_x+45, knight_y-10)], fill=(255, 255, 0), width=2) elif character == "mermaid": mermaid_x = 50 + i * 40 mermaid_y = 120 # Draw mermaid tail draw.ellipse([mermaid_x, mermaid_y, mermaid_x+30, mermaid_y+20], fill=(255, 105, 180)) # Draw mermaid body draw.ellipse([mermaid_x+5, mermaid_y-20, mermaid_x+25, mermaid_y], fill=(255, 218, 185)) # Draw hair draw.ellipse([mermaid_x-5, mermaid_y-25, mermaid_x+30, mermaid_y-15], fill=(255, 215, 0)) if "swim" in story.lower() and i > 0: # Draw bubbles for j in range(3): bubble_x = mermaid_x + random.randint(0, 30) bubble_y = mermaid_y - random.randint(10, 30) draw.ellipse([bubble_x, bubble_y, bubble_x+5, bubble_y+5], fill=(173, 216, 230)) # Draw enemies based on theme if theme == "space" and "alien" in story.lower(): alien_x = 200 alien_y = 100 - i*10 draw.ellipse([alien_x, alien_y, alien_x+20, alien_y+20], fill=(50, 205, 50)) draw.ellipse([alien_x+5, alien_y+5, alien_x+7, alien_y+7], fill=(0, 0, 0)) draw.ellipse([alien_x+13, alien_y+5, alien_x+15, alien_y+7], fill=(0, 0, 0)) elif theme == "jungle" and "snake" in story.lower(): snake_x = 200 snake_y = 150 - i*5 for segment in range(5): offset = segment * 5 draw.ellipse([snake_x+offset, snake_y+offset, snake_x+offset+15, snake_y+offset+15], fill=(0, 128, 0)) elif theme == "medieval" and "dragon" in story.lower() and character != "dragon": dragon_x = 220 dragon_y = 80 draw.ellipse([dragon_x, dragon_y, dragon_x+40, dragon_y+20], fill=(178, 34, 34)) draw.line([(dragon_x+40, dragon_y+10), (dragon_x+60, dragon_y)], fill=(178, 34, 34), width=3) elif theme == "underwater" and "shark" in story.lower(): shark_x = 220 shark_y = 80 + i*10 # Draw shark body draw.ellipse([shark_x, shark_y, shark_x+60, shark_y+30], fill=(169, 169, 169)) # Draw shark fin draw.polygon([(shark_x+40, shark_y), (shark_x+50, shark_y-20), (shark_x+60, shark_y)], fill=(169, 169, 169)) frames.append(img) return frames def generate_code_explanation(story, explanation_generator): """Generate code explanation using open-source model""" try: # Create a prompt for the model prompt = f"Explain to a child how this story would become code: '{story}'. Use simple analogies and relate to real-world objects." # Generate text result = explanation_generator( prompt, max_length=250, num_return_sequences=1, ) return result[0]['generated_text'] except: # Fallback explanation if model fails return f"""See how your story became real code? For example, when you wrote "{story.split()[0]}", we used code like: character.move(). That's how we turn words into actions!""" def extract_story_elements(story, ner_model, classifier): """Extract hero and world from the story using AI models""" try: # Default values hero = "spaceship" world = "space" # Find hero based on keywords and entities hero_keywords = { "spaceship": ["spaceship", "rocket", "ship", "alien", "planet", "star"], "dragon": ["dragon", "monster", "creature", "beast", "wyvern"], "knight": ["knight", "warrior", "prince", "princess", "king", "queen", "sword"], "mermaid": ["mermaid", "merman", "sea", "ocean", "underwater", "fish"] } # Find world based on keywords and classification world_labels = ["space", "jungle", "medieval", "underwater"] # Find hero by keywords story_lower = story.lower() for candidate, keywords in hero_keywords.items(): if any(keyword in story_lower for keyword in keywords): hero = candidate break # Use NER to find entities entities = ner_model(story) person_entities = [e['word'] for e in entities if e['entity'] in ['B-PER', 'I-PER']] # If we found specific character names, adjust hero if person_entities: if "dragon" in story_lower: hero = "dragon" elif "knight" in story_lower or "king" in story_lower or "queen" in story_lower: hero = "knight" elif "mermaid" in story_lower or "sea" in story_lower: hero = "mermaid" # Classify world result = classifier(story, world_labels) world = result['labels'][0] # Override based on specific keywords if "underwater" in story_lower or "ocean" in story_lower or "sea" in story_lower: world = "underwater" if "forest" in story_lower or "jungle" in story_lower: world = "jungle" if "castle" in story_lower or "kingdom" in story_lower or "dragon" in story_lower: world = "medieval" if "space" in story_lower or "alien" in story_lower or "planet" in story_lower: world = "space" return hero, world except Exception as e: st.error(f"Error analyzing story: {str(e)}") return "spaceship", "space" # Header section st.markdown('
✨ Made with magic by CodeTales Team ✨
Transforming stories into games since 2023