import streamlit as st import pandas as pd import plotly.express as px import random import json from datetime import datetime from streamlit_flow import streamlit_flow from streamlit_flow.elements import StreamlitFlowNode, StreamlitFlowEdge from streamlit_flow.layouts import TreeLayout # Rich data structures for game content SITUATIONS = [ { "id": "village_peril", "name": "The Village in Peril", "description": "A once-peaceful village is shrouded in dark miasma. Villagers cower in fear as shadowy figures lurk in the mist.", "emoji": "šŸ˜ļø" }, { "id": "cursed_artifact", "name": "The Cursed Artifact", "description": "Deep in an ancient tomb, a glowing artifact pulses with malevolent energy, its whispers echoing in your mind.", "emoji": "šŸŗ" }, { "id": "sacred_pact", "name": "The Sacred Pact", "description": "At a moonlit shrine, spirits of the land gather, awaiting a mediator to forge a new covenant between realms.", "emoji": "šŸŒ™" }, { "id": "shapeshifter_challenge", "name": "The Shapeshifter's Challenge", "description": "A rival kitsune issues a challenge, their forms flickering between human and fox. The air crackles with competitive energy.", "emoji": "šŸ¦Š" }, { "id": "fox_fire_trial", "name": "The Fox Fire Trial", "description": "Sacred blue flames dance before you, a test of your mastery over kitsune magic. The heat is both inviting and intimidating.", "emoji": "šŸ”„" } ] ACTIONS = [ { "id": "fox_fire", "name": "Use Fox Fire", "description": "Summon mystical flames to illuminate the darkness or ward off evil spirits.", "emoji": "šŸ”„" }, { "id": "shapeshift", "name": "Shapeshift", "description": "Transform your appearance to blend in or deceive others.", "emoji": "šŸ¦Š" }, { "id": "possess_object", "name": "Possess an Object", "description": "Infuse your spirit into an inanimate object to manipulate or gather information.", "emoji": "šŸ‘»" }, { "id": "call_spirits", "name": "Call Upon Ancient Spirits", "description": "Invoke the wisdom and power of your ancestors to guide you.", "emoji": "šŸŒŸ" }, { "id": "use_artifact", "name": "Use Mystical Artifact", "description": "Activate a powerful magical item to unleash its effects.", "emoji": "šŸ—”ļø" } ] def generate_situation(): return random.choice(SITUATIONS) def generate_actions(): return random.sample(ACTIONS, 3) def evaluate_action(action, power_level, mystical_energy, history): base_success_chance = (power_level + mystical_energy) / 2 if action['id'] in history: success_chance = base_success_chance + (history[action['id']] * 2) else: success_chance = base_success_chance outcome = random.randint(1, 100) <= success_chance return outcome, success_chance def update_graph(nodes, edges, situation, action, outcome, timestamp): new_node_id = f"{len(nodes)}-{situation['id']}-{action['id']}" content = f"{situation['emoji']} {situation['name']}\n{action['emoji']} {action['name']}\nOutcome: {'āœ… Success' if outcome else 'āŒ Failure'}\nšŸ•’ {timestamp}" new_node = StreamlitFlowNode(new_node_id, (0, 0), {'content': content}, 'output', 'bottom', 'top') nodes.append(new_node) if len(nodes) > 1: new_edge = StreamlitFlowEdge(f"{nodes[-2].id}-{new_node.id}", nodes[-2].id, new_node.id, animated=True, dashed=True) edges.append(new_edge) return nodes, edges def save_game_state(state): return json.dumps(state, default=str) # Use default=str to handle datetime objects def load_game_state(state_json): return json.loads(state_json) def app(): st.markdown("# šŸ¦Š Kitsune - The Mystical Shape-Shifter Game šŸ¦Š") if 'game_state' not in st.session_state: st.session_state.game_state = { 'nodes': [], 'edges': [], 'score': 0, 'history': {}, 'power_level': 50, 'mystical_energy': 75 } # Game Stats st.sidebar.markdown("## šŸ“Š Game Stats") st.sidebar.markdown(f"**Score:** {st.session_state.game_state['score']}") # Character Stats power_level = st.sidebar.slider('Power Level āš”', 0, 100, st.session_state.game_state['power_level']) mystical_energy = st.sidebar.slider('Mystical Energy āœØ', 0, 100, st.session_state.game_state['mystical_energy']) # Game Loop situation = generate_situation() actions = generate_actions() st.markdown(f"## {situation['emoji']} Current Situation: {situation['name']}") st.markdown(situation['description']) st.markdown("### šŸŽ­ Choose your action:") cols = st.columns(3) for i, action in enumerate(actions): if cols[i].button(f"{action['emoji']} {action['name']}"): outcome, success_chance = evaluate_action(action, power_level, mystical_energy, st.session_state.game_state['history']) timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") st.markdown(f"You decided to: **{action['name']}**") st.markdown(action['description']) st.markdown(f"**Outcome:** {'āœ… Success!' if outcome else 'āŒ Failure.'}") st.markdown(f"**Success Chance:** {success_chance:.2f}%") if outcome: st.session_state.game_state['score'] += 1 # Update history if action['id'] in st.session_state.game_state['history']: st.session_state.game_state['history'][action['id']] += 1 if outcome else -1 else: st.session_state.game_state['history'][action['id']] = 1 if outcome else -1 st.session_state.game_state['nodes'], st.session_state.game_state['edges'] = update_graph( st.session_state.game_state['nodes'], st.session_state.game_state['edges'], situation, action, outcome, timestamp ) # Display Graph if st.session_state.game_state['nodes']: st.markdown("## šŸŒ³ Your Journey") streamlit_flow('kitsune_game_flow', st.session_state.game_state['nodes'], st.session_state.game_state['edges'], layout=TreeLayout(direction='down'), fit_view=True, height=600) # Character Stats Visualization data = {"Stat": ["Power Level āš”", "Mystical Energy āœØ"], "Value": [power_level, mystical_energy]} df = pd.DataFrame(data) fig = px.bar(df, x='Stat', y='Value', title="Kitsune Stats šŸ“Š") st.plotly_chart(fig) # Save and Load Game State st.markdown("## šŸ’¾ Save/Load Game") if st.button("Save Game"): game_state_json = save_game_state(st.session_state.game_state) st.download_button( label="Download Game State", data=game_state_json, file_name="kitsune_game_state.json", mime="application/json" ) uploaded_file = st.file_uploader("Load Game State", type="json") if uploaded_file is not None: game_state_json = uploaded_file.getvalue().decode("utf-8") st.session_state.game_state = load_game_state(game_state_json) st.success("Game state loaded successfully!") if __name__ == "__main__": app()