sunbal7's picture
Create app.py
6e60411 verified
raw
history blame
26.6 kB
# app.py - Game-Based Learning Version
import streamlit as st
import os
import time
import random
import base64
import json
import requests
import re
from PIL import Image
import io
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
# Configure Streamlit page
st.set_page_config(
page_title="StoryCoder - Learn Coding Through Games",
page_icon="๐Ÿง™โ€โ™‚๏ธ",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for colorful UI
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
:root {
--primary: #FF6B6B;
--secondary: #4ECDC4;
--accent: #FFD166;
--dark: #1A535C;
--light: #F7FFF7;
}
body {
background: linear-gradient(135deg, var(--light) 0%, #E8F4F8 100%);
font-family: 'Comic Neue', cursive;
}
.stApp {
background: url('https://www.transparenttextures.com/patterns/cartographer.png');
}
.story-box {
background-color: white;
border-radius: 20px;
padding: 25px;
box-shadow: 0 8px 16px rgba(26, 83, 92, 0.15);
border: 3px solid var(--accent);
margin-bottom: 25px;
}
.header {
color: var(--dark);
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.concept-card {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: 15px;
padding: 15px;
margin: 10px 0;
border-left: 5px solid var(--secondary);
box-shadow: 0 4px 8px rgba(0,0,0,0.05);
}
.stButton>button {
background: linear-gradient(45deg, var(--primary), var(--secondary));
color: white;
border-radius: 12px;
padding: 10px 24px;
font-weight: bold;
font-size: 18px;
border: none;
transition: all 0.3s;
}
.stButton>button:hover {
transform: scale(1.05);
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
}
.stTextInput>div>div>input {
border-radius: 12px;
padding: 12px;
border: 2px solid var(--accent);
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
overflow-x: auto;
}
.tab {
padding: 10px 20px;
background-color: var(--accent);
border-radius: 10px;
cursor: pointer;
font-weight: bold;
white-space: nowrap;
}
.tab.active {
background-color: var(--secondary);
color: white;
}
@media (max-width: 768px) {
.tabs {
flex-wrap: wrap;
}
}
.game-container {
background-color: #1a1a2e;
border-radius: 15px;
padding: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
margin-bottom: 25px;
position: relative;
overflow: hidden;
}
.game-preview {
border-radius: 10px;
overflow: hidden;
margin: 0 auto;
display: block;
max-width: 100%;
}
.character {
font-size: 48px;
text-align: center;
margin: 10px 0;
}
.game-instructions {
background-color: #f0f8ff;
border-radius: 15px;
padding: 15px;
margin: 15px 0;
}
.ai-game {
border: 3px solid var(--accent);
border-radius: 15px;
padding: 15px;
background: white;
margin: 20px 0;
}
</style>
""", unsafe_allow_html=True)
# Concept database
CONCEPTS = {
"loop": {
"name": "Loop",
"emoji": "๐Ÿ”„",
"description": "Loops repeat actions multiple times",
"example": "for i in range(5):\n print('Hello!')",
"color": "#FF9E6D"
},
"conditional": {
"name": "Conditional",
"emoji": "โ“",
"description": "Conditionals make decisions in code",
"example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
"color": "#4ECDC4"
},
"function": {
"name": "Function",
"emoji": "โœจ",
"description": "Functions are reusable blocks of code",
"example": "def greet(name):\n print(f'Hello {name}!')",
"color": "#FFD166"
},
"variable": {
"name": "Variable",
"emoji": "๐Ÿ“ฆ",
"description": "Variables store information",
"example": "score = 10\nplayer = 'Alex'",
"color": "#FF6B6B"
},
"list": {
"name": "List",
"emoji": "๐Ÿ“",
"description": "Lists store collections of items",
"example": "fruits = ['apple', 'banana', 'orange']",
"color": "#1A535C"
}
}
# Pre-generated game examples
GAME_EXAMPLES = {
"loop": {
"image": "https://placehold.co/600x400/orange/white?text=Loop+Game",
"description": "Collect coins in a looping maze"
},
"conditional": {
"image": "https://placehold.co/600x400/blue/white?text=Conditional+Game",
"description": "Avoid obstacles by making decisions"
},
"function": {
"image": "https://placehold.co/600x400/green/white?text=Function+Game",
"description": "Cast spells using reusable functions"
}
}
# Initialize Groq client
def get_groq_client():
api_key = st.secrets.get("GROQ_API_KEY", "")
if not api_key:
st.warning("Groq API key not found. Using simpler game generation.")
return None
try:
from groq import Groq
return Groq(api_key=api_key)
except ImportError:
st.error("Groq library not installed. Please install with `pip install groq`")
return None
except Exception as e:
st.error(f"Error initializing Groq client: {str(e)}")
return None
# Analyze story and identify programming concepts
def analyze_story(story):
story_lower = story.lower()
detected_concepts = []
# Check for loops
if any(word in story_lower for word in ["times", "repeat", "again", "multiple", "many"]):
detected_concepts.append("loop")
# Check for conditionals
if any(word in story_lower for word in ["if", "when", "unless", "whether", "decision"]):
detected_concepts.append("conditional")
# Check for functions
if any(word in story_lower for word in ["make", "create", "do", "perform", "cast", "spell"]):
detected_concepts.append("function")
# Check for variables
if any(word in story_lower for word in ["is", "has", "set to", "value", "store"]):
detected_concepts.append("variable")
# Check for lists
if any(word in story_lower for word in ["and", "many", "several", "collection", "items", "group"]):
detected_concepts.append("list")
return list(set(detected_concepts))
# Extract entities from story using regex
def extract_entities(story):
entities = {
"characters": [],
"objects": [],
"actions": [],
"locations": []
}
# Extract characters (proper nouns)
entities["characters"] = list(set(re.findall(r'\b[A-Z][a-z]+\b', story)))
# Extract objects (nouns after "the")
entities["objects"] = list(set(re.findall(r'\bthe\s+(\w+)', story, re.I)))
# Extract actions (verbs)
entities["actions"] = list(set(re.findall(r'\b(\w+ed|\w+ing)\b', story)))
# Extract locations (nouns after "in", "on", "at")
locations = re.findall(r'\b(in|on|at)\s+the?\s?(\w+)', story)
entities["locations"] = list(set([loc[1] for loc in locations]))
# Filter out common words
common_words = ["the", "a", "an", "and", "or", "but", "if", "then", "when", "where"]
for key in entities:
entities[key] = [word for word in entities[key]
if isinstance(word, str) and
word.lower() not in common_words and
len(word) > 2]
return entities
# Generate game concept using Groq
def generate_game_concept(story, concepts, entities):
groq_client = get_groq_client()
if not groq_client:
# Fallback concept
return {
"title": "Adventure Game",
"description": "An exciting game based on your story!",
"mechanics": "Collect items and avoid obstacles",
"code_concepts": ", ".join(concepts),
"instructions": "Use arrow keys to move and space to jump"
}
try:
prompt = f"""
You are a game designer creating educational games for kids aged 6-12.
Create a simple 2D game concept based on the following story:
Story: "{story}"
Programming concepts to include: {', '.join(concepts)}
Extracted entities:
- Characters: {', '.join(entities['characters'])}
- Objects: {', '.join(entities['objects'])}
- Actions: {', '.join(entities['actions'])}
- Locations: {', '.join(entities['locations'])}
Respond in JSON format with these keys:
- title: Game title (max 5 words)
- description: Game description (1-2 sentences)
- mechanics: Core game mechanics (1 sentence)
- code_concepts: How programming concepts are used in the game
- instructions: Simple game controls (max 10 words)
"""
response = groq_client.chat.completions.create(
model="llama3-8b-8192",
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=512,
response_format={"type": "json_object"}
)
game_data = json.loads(response.choices[0].message.content)
return game_data
except Exception as e:
st.error(f"Game concept generation error: {str(e)}")
return {
"title": "Adventure Game",
"description": "An exciting game based on your story!",
"mechanics": "Collect items and avoid obstacles",
"code_concepts": ", ".join(concepts),
"instructions": "Use arrow keys to move and space to jump"
}
# Generate game code using Groq
def generate_game_code(story, game_concept):
groq_client = get_groq_client()
if not groq_client:
# Fallback game code
return """# Simple Game Template
import pygame
import sys
# Initialize pygame
pygame.init()
# Set up display
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Your Adventure Game")
# Colors
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# Player
player = pygame.Rect(400, 300, 50, 50)
player_speed = 5
# Game loop
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Player movement
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= player_speed
if keys[pygame.K_RIGHT]:
player.x += player_speed
if keys[pygame.K_UP]:
player.y -= player_speed
if keys[pygame.K_DOWN]:
player.y += player_speed
# Drawing
screen.fill(WHITE)
pygame.draw.rect(screen, BLUE, player)
# Display instructions
font = pygame.font.SysFont(None, 36)
text = font.render("Your Adventure Game - Move with arrow keys", True, (0, 0, 0))
screen.blit(text, (50, 50))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
""", "Success"
try:
prompt = f"""
You are a Python game developer creating educational games for kids using Pygame.
Create a simple 2D game based on the following concept:
Game Title: {game_concept['title']}
Description: {game_concept['description']}
Mechanics: {game_concept['mechanics']}
Instructions: {game_concept['instructions']}
Requirements:
- Use Pygame library
- Simple graphics (shapes and colors)
- Include at least one character the player controls
- Include collectible items
- Include obstacles to avoid
- Score tracking
- Game over condition
Output ONLY the Python code with no additional text or explanations.
"""
response = groq_client.chat.completions.create(
model="llama3-70b-8192",
messages=[{"role": "user", "content": prompt}],
temperature=0.5,
max_tokens=2048
)
game_code = response.choices[0].message.content
# Clean up the code
if "```python" in game_code:
game_code = game_code.split("```python")[1].split("```")[0]
elif "```" in game_code:
game_code = game_code.split("```")[1]
return game_code, "Success"
except Exception as e:
return f"# Error generating game code\nprint('{str(e)}')", str(e)
# Create placeholder game preview
def generate_game_preview(game_concept):
try:
# Create a simple placeholder image
img = Image.new('RGB', (600, 400), color=(73, 109, 137))
# Add text to the image
try:
from PIL import ImageDraw, ImageFont
draw = ImageDraw.Draw(img)
# Try to use a default font
try:
font = ImageFont.truetype("arial.ttf", 30)
except:
# Fallback to basic font
font = ImageFont.load_default()
title = f"{game_concept['title']}"
draw.text((150, 150), title, fill=(255, 255, 0), font=font)
draw.text((100, 200), "Game Preview", fill=(255, 255, 255), font=font)
except:
pass
# Convert to bytes
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
return buf
except Exception as e:
st.error(f"Game preview generation error: {str(e)}")
return None
# Create interactive visualization of game mechanics
def create_game_visualization(game_concept):
try:
# Create a simple visualization using Plotly
fig = go.Figure()
# Add player character
fig.add_trace(go.Scatter(
x=[0.5], y=[0.5],
mode='markers+text',
marker=dict(size=50, color='blue'),
text='Player',
textposition='bottom center',
name='Player'
))
# Add collectibles
for i in range(5):
fig.add_trace(go.Scatter(
x=[random.uniform(0.2, 0.8)], y=[random.uniform(0.2, 0.8)],
mode='markers',
marker=dict(size=30, color='gold', symbol='star'),
name='Collectible'
))
# Add obstacles
for i in range(3):
fig.add_trace(go.Scatter(
x=[random.uniform(0.1, 0.9)], y=[random.uniform(0.1, 0.9)],
mode='markers',
marker=dict(size=40, color='red', symbol='x'),
name='Obstacle'
))
# Update layout
fig.update_layout(
title=f"Game Visualization: {game_concept.get('title', 'Your Game')}",
xaxis=dict(showgrid=False, zeroline=False, visible=False, range=[0, 1]),
yaxis=dict(showgrid=False, zeroline=False, visible=False, range=[0, 1]),
showlegend=False,
margin=dict(l=20, r=20, t=40, b=20),
height=400,
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)'
)
return fig
except Exception as e:
st.error(f"Visualization error: {str(e)}")
return None
def main():
"""Main application function"""
st.title("๐Ÿง™โ€โ™‚๏ธ StoryCoder - Learn Coding Through Games!")
st.subheader("Turn your story into a game and discover coding secrets!")
# Initialize session state
if 'story' not in st.session_state:
st.session_state.story = ""
if 'concepts' not in st.session_state:
st.session_state.concepts = []
if 'entities' not in st.session_state:
st.session_state.entities = {}
if 'game_concept' not in st.session_state:
st.session_state.game_concept = {}
if 'game_code' not in st.session_state:
st.session_state.game_code = ""
if 'game_preview' not in st.session_state:
st.session_state.game_preview = None
if 'active_tab' not in st.session_state:
st.session_state.active_tab = "story"
# Create tabs
tab_cols = st.columns(5)
with tab_cols[0]:
if st.button("๐Ÿ“– Create Story"):
st.session_state.active_tab = "story"
with tab_cols[1]:
if st.button("๐ŸŽฎ Game"):
st.session_state.active_tab = "game"
with tab_cols[2]:
if st.button("๐Ÿ” Concepts"):
st.session_state.active_tab = "concepts"
with tab_cols[3]:
if st.button("๐Ÿ’ป Code"):
st.session_state.active_tab = "code"
with tab_cols[4]:
if st.button("๐Ÿ”„ Reset"):
for key in list(st.session_state.keys()):
if key != 'active_tab':
del st.session_state[key]
st.session_state.active_tab = "story"
st.rerun()
# Story creation tab
if st.session_state.active_tab == "story":
with st.container():
st.header("๐Ÿ“– Create Your Story")
st.write("Write a short story (2-5 sentences) and I'll turn it into a game!")
story = st.text_area(
"Your story:",
height=200,
placeholder="Once upon a time, a rabbit named Ruby needed to collect 5 magical carrots in the enchanted forest while avoiding mischievous squirrels...",
value=st.session_state.story,
key="story_input"
)
if st.button("Create Game!", use_container_width=True):
if len(story) < 20:
st.error("Your story needs to be at least 20 characters long!")
else:
st.session_state.story = story
with st.spinner("๐Ÿง  Analyzing your story..."):
st.session_state.concepts = analyze_story(story)
st.session_state.entities = extract_entities(story)
with st.spinner("๐ŸŽฎ Designing your game..."):
st.session_state.game_concept = generate_game_concept(
story,
st.session_state.concepts,
st.session_state.entities
)
with st.spinner("๐Ÿ‘พ Generating game preview..."):
st.session_state.game_preview = generate_game_preview(
st.session_state.game_concept
)
with st.spinner("๐Ÿ’ป Creating game code..."):
st.session_state.game_code, error = generate_game_code(
story,
st.session_state.game_concept
)
if error and "Error" in error:
st.error(f"Code generation error: {error}")
st.session_state.active_tab = "game"
st.rerun()
# Show examples
st.subheader("โœจ Story Examples")
col1, col2, col3 = st.columns(3)
with col1:
st.caption("Loop Example")
st.code('"Ruby the rabbit needs to collect 5 magical carrots that appear every 10 seconds in the enchanted forest"', language="text")
st.image(GAME_EXAMPLES["loop"]["image"],
use_container_width=True,
caption=GAME_EXAMPLES["loop"]["description"])
with col2:
st.caption("Conditional Example")
st.code('"If Alex the wizard sees a dragon, he casts a fire spell, otherwise he walks forward to find treasures"', language="text")
st.image(GAME_EXAMPLES["conditional"]["image"],
use_container_width=True,
caption=GAME_EXAMPLES["conditional"]["description"])
with col3:
st.caption("Function Example")
st.code('"Wizard Lily creates magic spells to solve puzzles - each spell is a special function"', language="text")
st.image(GAME_EXAMPLES["function"]["image"],
use_container_width=True,
caption=GAME_EXAMPLES["function"]["description"])
# Game tab
elif st.session_state.active_tab == "game":
st.header("๐ŸŽฎ Your Story Game")
if not st.session_state.get('story'):
st.warning("Please create a story first!")
st.session_state.active_tab = "story"
st.rerun()
# Display game concept
st.subheader(f"โœจ {st.session_state.game_concept.get('title', 'Your Adventure Game')}")
st.write(st.session_state.game_concept.get('description', 'An exciting game based on your story!'))
# Display game preview
st.subheader("๐Ÿ–ผ๏ธ Game Preview")
if st.session_state.game_preview:
st.image(st.session_state.game_preview, use_container_width=True)
else:
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
st.image(GAME_EXAMPLES[concept]["image"],
use_container_width=True,
caption="Example game preview")
# Game mechanics visualization
st.subheader("๐ŸŽฎ Game Mechanics")
if st.session_state.game_concept:
fig = create_game_visualization(st.session_state.game_concept)
if fig:
st.plotly_chart(fig, use_container_width=True)
else:
st.info("Game mechanics visualization would appear here")
# Game instructions
st.subheader("๐Ÿ“œ How to Play")
st.markdown(f"""
<div class="game-instructions">
<h4>Game Controls:</h4>
<p>{st.session_state.game_concept.get('instructions', 'Use arrow keys to move and space to jump')}</p>
<h4>Game Mechanics:</h4>
<p>{st.session_state.game_concept.get('mechanics', 'Collect items and avoid obstacles')}</p>
<h4>Coding Concepts:</h4>
<p>{st.session_state.game_concept.get('code_concepts', 'Loops, conditionals, and functions')}</p>
</div>
""", unsafe_allow_html=True)
if st.button("Show Coding Secrets!", use_container_width=True):
st.session_state.active_tab = "concepts"
st.rerun()
# Concepts tab
elif st.session_state.active_tab == "concepts":
st.header("๐Ÿ” Coding Concepts in Your Game")
st.subheader("We used these programming concepts in your game:")
if not st.session_state.concepts:
st.warning("No concepts detected in your story! Try adding words like '3 times', 'if', or 'make'.")
else:
for concept in st.session_state.concepts:
if concept in CONCEPTS:
details = CONCEPTS[concept]
st.markdown(f"""
<div class="concept-card" style="border-left: 5px solid {details['color']};">
<div style="display:flex; align-items:center; gap:15px;">
<span style="font-size:36px;">{details['emoji']}</span>
<h3 style="color:{details['color']};">{details['name']}</h3>
</div>
<p>{details['description']}</p>
<pre style="background:#f0f0f0; padding:10px; border-radius:8px;">{details['example']}</pre>
</div>
""", unsafe_allow_html=True)
st.subheader("๐ŸŽฎ How Concepts Are Used in Your Game")
st.write(st.session_state.game_concept.get('code_concepts', 'These concepts power the mechanics of your game'))
if st.button("See the Game Code!", use_container_width=True):
st.session_state.active_tab = "code"
st.rerun()
# Code tab
elif st.session_state.active_tab == "code":
st.header("๐Ÿ’ป Game Code")
st.write("Here's the Python code for your game. You can run it on your computer!")
if st.session_state.game_code:
st.subheader("๐ŸŽฎ Game Implementation")
st.code(st.session_state.game_code, language="python")
# Download button
st.download_button(
label="Download Game Code",
data=st.session_state.game_code,
file_name="story_game.py",
mime="text/python",
use_container_width=True
)
st.info("๐Ÿ’ก How to run your game:")
st.markdown("""
1. Install Python from [python.org](https://python.org)
2. Install Pygame: `pip install pygame`
3. Download the code above
4. Run it with: `python story_game.py`
""")
st.success("๐ŸŽ‰ When you run this code, you'll see your story come to life as a playable game!")
# Game preview
st.subheader("๐ŸŽฎ What Your Game Looks Like")
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
st.image(GAME_EXAMPLES[concept]["image"],
caption="Your game will look similar to this",
use_container_width=True)
else:
st.warning("No game code generated yet!")
if st.button("Create Another Game!", use_container_width=True):
st.session_state.active_tab = "story"
st.session_state.story = ""
st.rerun()
if __name__ == "__main__":
main()