import streamlit as st import random import re from gtts import gTTS from PIL import Image, ImageDraw, ImageFont import io import base64 # Default word lists for storytelling classes default_word_lists = { "Location": ["quiet town", "small village", "city", "forest", "mountain"], "Actions": ["walking", "pedaling", "running", "dancing", "exploring"], "Thoughts": ["chasing shadows", "what if", "brilliance of years", "echoes", "secrets"], "Emotions": ["joy", "pain", "trembling smile", "storm", "silent art"], "Dialogue": ["\"Keep moving, dare to feel;\"", "\"Am I chasing shadows?\"", "\"The dawn awaits!\"", "\"I love you.\"", "\"Let’s go!\""] } # Suit properties for narrative flavor suit_properties = { "Hearts": "emotional or romantic", "Diamonds": "wealthy or luxurious", "Clubs": "conflict or struggle", "Spades": "mysterious or dangerous" } # Sentence templates for story generation sentence_templates = { "Location": "The story unfolded in a {property} {word}.", "Actions": "Suddenly, a {property} {word} changed everything.", "Thoughts": "A {property} thought, '{word}', crossed their mind.", "Emotions": "A {property} wave of {word} surged through them.", "Dialogue": "Someone spoke with a {property} tone: {word}" } # Choice templates for branching narrative choice_templates = { "Location": ["Explore the {word} further.", "Leave the {word} behind."], "Actions": ["Continue {word} despite the risk.", "Stop {word} and reconsider."], "Thoughts": ["Pursue the '{word}' idea.", "Ignore the '{word}' thought."], "Emotions": ["Embrace the {word}.", "Suppress the {word}."], "Dialogue": ["Respond to {word}.", "Ignore {word} and move on."] } # Pure Python function to augment word lists from user input def augment_word_lists(user_input): augmented_lists = {key: list(set(val)) for key, val in default_word_lists.items()} words = user_input.lower().split() location_keywords = ["town", "village", "city", "forest", "mountain", "place", "land"] action_keywords = ["walk", "run", "dance", "pedal", "explore", "move", "jump"] emotion_keywords = ["joy", "pain", "smile", "storm", "fear", "love", "anger"] dialogues = re.findall(r'"[^"]*"', user_input) augmented_lists["Dialogue"].extend(dialogues) for word in words: if any(keyword in word for keyword in location_keywords): augmented_lists["Location"].append(word) elif any(keyword in word for keyword in action_keywords): augmented_lists["Actions"].append(word) elif any(keyword in word for keyword in emotion_keywords): augmented_lists["Emotions"].append(word) elif "?" in word or "what" in word or "why" in word: augmented_lists["Thoughts"].append(word) for key in augmented_lists: augmented_lists[key] = list(set(augmented_lists[key])) return augmented_lists # Create a 52-card deck def create_deck(): suits = ["Hearts", "Diamonds", "Clubs", "Spades"] ranks = list(range(1, 14)) deck = [(suit, rank) for suit in suits for rank in ranks] random.shuffle(deck) return deck # Assign cards to classes def assign_card_to_class(card_index): if 0 <= card_index < 10: return "Location" elif 10 <= card_index < 20: return "Actions" elif 20 <= card_index < 30: return "Thoughts" elif 30 <= card_index < 40: return "Emotions" else: return "Dialogue" # Generate card visualization with p5.js def generate_card_visualization(suit, rank, story_class, word, property): num_balls = rank * 5 # More balls for higher rank jelly_size = {"Hearts": 40, "Diamonds": 50, "Clubs": 30, "Spades": 60}[suit] # Suit affects jellyfish size rotation_speed = {"Location": 0.005, "Actions": 0.01, "Thoughts": 0.003, "Emotions": 0.007, "Dialogue": 0.009}[story_class] hue_base = {"Hearts": 0, "Diamonds": 120, "Clubs": 240, "Spades": 300}[suit] # Suit affects color html_code = f"""
""" return html_code # Generate story sentence def generate_story_sentence(story_class, word, property): return sentence_templates[story_class].format(word=word, property=property) # Generate choice options def generate_choices(story_class, word): return [template.format(word=word) for template in choice_templates[story_class]] # Generate song lyrics def generate_song_lyrics(story_text): words = story_text.split() key_elements = [word for word in words if len(word) > 3][:12] lyrics = "\n".join([f"{key_elements[i]} {key_elements[i+1]}" for i in range(0, len(key_elements)-1, 2)]) return lyrics # Main app def main(): st.set_page_config(page_title="StoryForge: The Animated Adventure", page_icon="🎴", layout="wide") st.title("🎴 StoryForge: A Choose Your Own Adventure Game 🎴") # User input st.markdown("## πŸ“ Your Story Seed") user_input = st.text_area("Paste your story inspiration here:", height=200) # Session state initialization if "augmented_lists" not in st.session_state: st.session_state.augmented_lists = default_word_lists if "deck" not in st.session_state: st.session_state.deck = create_deck() if "story" not in st.session_state: st.session_state.story = [] if "drawn_cards" not in st.session_state: st.session_state.drawn_cards = 0 if "history" not in st.session_state: st.session_state.history = [] if "current_choices" not in st.session_state: st.session_state.current_choices = [] if "last_card" not in st.session_state: st.session_state.last_card = None # Process input if st.button("Start Game"): if user_input: st.session_state.augmented_lists = augment_word_lists(user_input) st.session_state.deck = create_deck() st.session_state.story = [] st.session_state.history = [] st.session_state.drawn_cards = 0 st.session_state.current_choices = [] st.session_state.last_card = None st.success("Game started! Draw your first card.") # Layout col1, col2 = st.columns([2, 3]) with col1: # Draw card if st.button("Draw Card") and st.session_state.drawn_cards < 52: card_index = st.session_state.drawn_cards suit, rank = st.session_state.deck[card_index] story_class = assign_card_to_class(card_index) word = random.choice(st.session_state.augmented_lists[story_class]) property = suit_properties[suit] # Generate and display animated visualization viz_html = generate_card_visualization(suit, rank, story_class, word, property) st.components.v1.html(viz_html, height=420, scrolling=False) # Generate story sentence and choices sentence = generate_story_sentence(story_class, word, property) st.session_state.story.append(sentence) st.session_state.current_choices = generate_choices(story_class, word) st.session_state.last_card = (suit, rank, story_class, word, property) st.session_state.drawn_cards += 1 # Display choices if st.session_state.current_choices: st.markdown("#### Make Your Choice:") choice = st.radio("What happens next?", st.session_state.current_choices) if st.button("Confirm Choice"): st.session_state.history.append((st.session_state.story[-1], choice)) st.session_state.current_choices = [] st.success(f"Choice made: {choice}") with col2: st.markdown("### πŸ“œ Your Story Unfolds") if st.session_state.story: st.write("\n".join(st.session_state.story)) st.markdown("### πŸ•°οΈ Adventure History") if st.session_state.history: for i, (event, choice) in enumerate(st.session_state.history): st.write(f"**Step {i+1}**: {event} β†’ *Choice: {choice}*") # Song generation if st.session_state.drawn_cards == 52: full_story = "\n".join(st.session_state.story) st.markdown("### 🎡 Story Song") lyrics = generate_song_lyrics(full_story) st.write(lyrics) tts = gTTS(text=lyrics, lang="en") audio_file = "story_song.mp3" tts.save(audio_file) st.audio(audio_file, format="audio/mp3") if __name__ == "__main__": main()