File size: 7,190 Bytes
1c26e4c
e211a6f
 
 
 
 
 
 
 
1c26e4c
e211a6f
 
1a70ad2
e211a6f
 
 
 
 
 
 
 
1a70ad2
e211a6f
 
 
 
 
 
 
1a70ad2
e211a6f
 
 
 
 
 
 
 
1a70ad2
e211a6f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a70ad2
e211a6f
 
 
 
 
 
 
1c26e4c
e211a6f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c26e4c
e211a6f
 
 
 
1c26e4c
e211a6f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c26e4c
 
e211a6f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import streamlit as st
import spacy
import random
import re
from gtts import gTTS
from PIL import Image, ImageDraw, ImageFont
import io
import numpy as np
import base64

# Load spaCy model for NLP
nlp = spacy.load("en_core_web_sm")

# Default word lists for storytelling classes
default_word_lists = {
    "Characters": ["Hero", "Villain", "Protagonist", "Antagonist", "Wizard", "Knight"],
    "Settings": ["Forest", "City", "Castle", "Beach", "Mountain", "Space"],
    "Events": ["Adventure", "Romance", "Mystery", "Conflict", "Discovery", "Escape"],
    "Objects": ["Sword", "Magic potion", "Treasure chest", "Book", "Ring", "Spaceship"],
    "Dialogues": ["\"Hello, how are you?\"", "\"Let's go!\"", "\"Beware the danger!\"", "\"I love you.\""]
}

# 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 = {
    "Characters": "There was a {word} who was {property}.",
    "Settings": "The {word} was {property}, stretching far into the distance.",
    "Events": "Then, a {property} {word} unfolded.",
    "Objects": "A {property} {word} appeared before them.",
    "Dialogues": "One of them said, {word}, with a {property} tone."
}

# Function to process user input and augment word lists
def augment_word_lists(user_input):
    doc = nlp(user_input)
    augmented_lists = {key: list(set(val)) for key, val in default_word_lists.items()}  # Start with defaults
    
    # Extract elements from user input
    for ent in doc.ents:
        if ent.label_ == "PERSON":
            augmented_lists["Characters"].append(ent.text)
        elif ent.label_ in ["GPE", "LOC"]:
            augmented_lists["Settings"].append(ent.text)
    
    for token in doc:
        if token.pos_ == "VERB" and token.text not in augmented_lists["Events"]:
            augmented_lists["Events"].append(token.text)
        elif token.pos_ == "NOUN" and token.text not in augmented_lists["Characters"] + augmented_lists["Settings"]:
            augmented_lists["Objects"].append(token.text)
    
    dialogues = re.findall(r'"[^"]*"', user_input)
    augmented_lists["Dialogues"].extend(dialogues)
    
    return augmented_lists

# Function to create a 52-card deck
def create_deck():
    suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
    ranks = list(range(1, 14))  # 1-13 for each suit
    deck = [(suit, rank) for suit in suits for rank in ranks]
    random.shuffle(deck)
    return deck

# Assign cards to classes based on position
def assign_card_to_class(card_index):
    if 0 <= card_index < 10:
        return "Characters"
    elif 10 <= card_index < 20:
        return "Settings"
    elif 20 <= card_index < 30:
        return "Events"
    elif 30 <= card_index < 40:
        return "Objects"
    else:
        return "Dialogues"

# Generate a card image with details
def generate_card_image(suit, rank, story_class, word, property):
    img = Image.new("RGB", (200, 300), color="white")
    draw = ImageDraw.Draw(img)
    font = ImageFont.load_default()
    
    draw.text((10, 10), f"{rank} of {suit}", fill="black", font=font)
    draw.text((10, 50), f"Class: {story_class}", fill="black", font=font)
    draw.text((10, 90), f"Word: {word}", fill="black", font=font)
    draw.text((10, 130), f"Property: {property}", fill="black", font=font)
    
    buffer = io.BytesIO()
    img.save(buffer, format="PNG")
    return buffer.getvalue()

# Generate story sentence based on card
def generate_story_sentence(story_class, word, property):
    template = sentence_templates[story_class]
    return template.format(word=word, property=property)

# Generate song lyrics from story
def generate_song_lyrics(story_text):
    doc = nlp(story_text)
    key_elements = [token.text for token in doc if token.pos_ in ["NOUN", "VERB", "ADJ"]][:10]  # Top 10 elements
    lyrics = " ".join(key_elements)  # Simple concatenation for 60-second duration
    return lyrics

# Main Streamlit app
def main():
    st.title("StoryForge: Card-Based Story Generator")
    
    # User input for story seed
    user_input = st.text_area("Paste your story seed here (large text accepted):", height=200)
    
    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
    
    # Process input and augment word lists
    if st.button("Process Input"):
        if user_input:
            st.session_state.augmented_lists = augment_word_lists(user_input)
            st.session_state.deck = create_deck()  # Reset deck
            st.session_state.story = []
            st.session_state.drawn_cards = 0
            st.success("Input processed! Ready to draw cards.")
    
    # Draw card button
    if st.button("Draw a 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_list = st.session_state.augmented_lists[story_class]
        word = random.choice(word_list)
        property = suit_properties[suit]
        
        # Generate and display card image
        card_image = generate_card_image(suit, rank, story_class, word, property)
        st.image(card_image, caption=f"Card {card_index + 1}")
        
        # Generate and append story sentence
        sentence = generate_story_sentence(story_class, word, property)
        st.session_state.story.append(sentence)
        st.session_state.drawn_cards += 1
        
        # Display current story
        st.write("### Current Story")
        st.write("\n".join(st.session_state.story))
    
    # Generate song when deck is fully drawn
    if st.session_state.drawn_cards == 52:
        st.write("### Full Story Generated!")
        full_story = "\n".join(st.session_state.story)
        st.write(full_story)
        
        # Generate and play song
        lyrics = generate_song_lyrics(full_story)
        st.write("### Song Lyrics")
        st.write(lyrics)
        
        tts = gTTS(text=lyrics, lang="en")
        audio_file = "song.mp3"
        tts.save(audio_file)
        audio_bytes = open(audio_file, "rb").read()
        st.audio(audio_bytes, format="audio/mp3")
    
    # Basic p5.js integration for interactivity (placeholder)
    st.components.v1.html("""
        <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
        <div id="sketch-holder"></div>
        <script>
            function setup() {
                let canvas = createCanvas(400, 100);
                canvas.parent('sketch-holder');
                background(220);
                textSize(16);
                text('Draw cards to build your story!', 10, 50);
            }
        </script>
    """, height=120)

if __name__ == "__main__":
    main()