Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import plotly.express as px
|
4 |
+
import random
|
5 |
+
import json
|
6 |
+
from streamlit_flow import streamlit_flow
|
7 |
+
from streamlit_flow.elements import StreamlitFlowNode, StreamlitFlowEdge
|
8 |
+
from streamlit_flow.layouts import TreeLayout
|
9 |
+
|
10 |
+
def generate_situation():
|
11 |
+
situations = [
|
12 |
+
"The Village in Peril",
|
13 |
+
"The Cursed Artifact",
|
14 |
+
"The Sacred Pact",
|
15 |
+
"The Shapeshifter's Challenge",
|
16 |
+
"The Fox Fire Trial"
|
17 |
+
]
|
18 |
+
return random.choice(situations)
|
19 |
+
|
20 |
+
def generate_actions():
|
21 |
+
actions = [
|
22 |
+
("Use Fox Fire", "π₯"),
|
23 |
+
("Shapeshift", "π¦"),
|
24 |
+
("Possess an Object", "π»"),
|
25 |
+
("Call Upon Ancient Spirits", "π"),
|
26 |
+
("Use Mystical Artifact", "π‘οΈ")
|
27 |
+
]
|
28 |
+
return random.sample(actions, 3)
|
29 |
+
|
30 |
+
def evaluate_action(action, power_level, mystical_energy, history):
|
31 |
+
base_success_chance = (power_level + mystical_energy) / 2
|
32 |
+
|
33 |
+
# Adjust success chance based on history
|
34 |
+
if action in history:
|
35 |
+
success_chance = base_success_chance + (history[action] * 2)
|
36 |
+
else:
|
37 |
+
success_chance = base_success_chance
|
38 |
+
|
39 |
+
outcome = random.randint(1, 100) <= success_chance
|
40 |
+
return outcome, success_chance
|
41 |
+
|
42 |
+
def update_graph(nodes, edges, situation, action, outcome):
|
43 |
+
new_node_id = f"{len(nodes)}-{situation}-{action}"
|
44 |
+
new_node = StreamlitFlowNode(new_node_id, (0, 0), {'content': f"{situation}\n{action}\nOutcome: {'Success' if outcome else 'Failure'}"}, 'output', 'bottom', 'top')
|
45 |
+
nodes.append(new_node)
|
46 |
+
|
47 |
+
if len(nodes) > 1:
|
48 |
+
new_edge = StreamlitFlowEdge(f"{nodes[-2].id}-{new_node.id}", nodes[-2].id, new_node.id, animated=True)
|
49 |
+
edges.append(new_edge)
|
50 |
+
|
51 |
+
return nodes, edges
|
52 |
+
|
53 |
+
def save_game_state(state):
|
54 |
+
return json.dumps(state)
|
55 |
+
|
56 |
+
def load_game_state(state_json):
|
57 |
+
return json.loads(state_json)
|
58 |
+
|
59 |
+
def app():
|
60 |
+
st.markdown("# Kitsune - The Mystical Shape-Shifter Game π¦")
|
61 |
+
|
62 |
+
if 'game_state' not in st.session_state:
|
63 |
+
st.session_state.game_state = {
|
64 |
+
'nodes': [],
|
65 |
+
'edges': [],
|
66 |
+
'score': 0,
|
67 |
+
'history': {},
|
68 |
+
'power_level': 50,
|
69 |
+
'mystical_energy': 75
|
70 |
+
}
|
71 |
+
|
72 |
+
# Game Stats
|
73 |
+
st.sidebar.markdown("## Game Stats")
|
74 |
+
st.sidebar.markdown(f"Score: {st.session_state.game_state['score']}")
|
75 |
+
|
76 |
+
# Character Stats
|
77 |
+
power_level = st.sidebar.slider('Power Level β‘', 0, 100, st.session_state.game_state['power_level'])
|
78 |
+
mystical_energy = st.sidebar.slider('Mystical Energy β¨', 0, 100, st.session_state.game_state['mystical_energy'])
|
79 |
+
|
80 |
+
# Game Loop
|
81 |
+
situation = generate_situation()
|
82 |
+
actions = generate_actions()
|
83 |
+
|
84 |
+
st.markdown(f"## Current Situation: {situation}")
|
85 |
+
st.markdown("Choose your action:")
|
86 |
+
|
87 |
+
cols = st.columns(3)
|
88 |
+
for i, (action, emoji) in enumerate(actions):
|
89 |
+
if cols[i].button(f"{emoji} {action}"):
|
90 |
+
outcome, success_chance = evaluate_action(action, power_level, mystical_energy, st.session_state.game_state['history'])
|
91 |
+
|
92 |
+
st.markdown(f"You decided to: {action}")
|
93 |
+
st.markdown(f"Outcome: {'Success!' if outcome else 'Failure.'}")
|
94 |
+
st.markdown(f"Success Chance: {success_chance:.2f}%")
|
95 |
+
|
96 |
+
if outcome:
|
97 |
+
st.session_state.game_state['score'] += 1
|
98 |
+
|
99 |
+
# Update history
|
100 |
+
if action in st.session_state.game_state['history']:
|
101 |
+
st.session_state.game_state['history'][action] += 1 if outcome else -1
|
102 |
+
else:
|
103 |
+
st.session_state.game_state['history'][action] = 1 if outcome else -1
|
104 |
+
|
105 |
+
st.session_state.game_state['nodes'], st.session_state.game_state['edges'] = update_graph(
|
106 |
+
st.session_state.game_state['nodes'],
|
107 |
+
st.session_state.game_state['edges'],
|
108 |
+
situation,
|
109 |
+
action,
|
110 |
+
outcome
|
111 |
+
)
|
112 |
+
|
113 |
+
# Display Graph
|
114 |
+
if st.session_state.game_state['nodes']:
|
115 |
+
streamlit_flow('kitsune_game_flow',
|
116 |
+
st.session_state.game_state['nodes'],
|
117 |
+
st.session_state.game_state['edges'],
|
118 |
+
layout=TreeLayout(),
|
119 |
+
fit_view=True,
|
120 |
+
height=600)
|
121 |
+
|
122 |
+
# Character Stats Visualization
|
123 |
+
data = {"Stat": ["Power Level", "Mystical Energy"],
|
124 |
+
"Value": [power_level, mystical_energy]}
|
125 |
+
df = pd.DataFrame(data)
|
126 |
+
fig = px.bar(df, x='Stat', y='Value', title="Kitsune Stats π")
|
127 |
+
st.plotly_chart(fig)
|
128 |
+
|
129 |
+
# Save and Load Game State
|
130 |
+
st.markdown("## Save/Load Game")
|
131 |
+
if st.button("Save Game"):
|
132 |
+
game_state_json = save_game_state(st.session_state.game_state)
|
133 |
+
st.download_button(
|
134 |
+
label="Download Game State",
|
135 |
+
data=game_state_json,
|
136 |
+
file_name="kitsune_game_state.json",
|
137 |
+
mime="application/json"
|
138 |
+
)
|
139 |
+
|
140 |
+
uploaded_file = st.file_uploader("Load Game State", type="json")
|
141 |
+
if uploaded_file is not None:
|
142 |
+
game_state_json = uploaded_file.getvalue().decode("utf-8")
|
143 |
+
st.session_state.game_state = load_game_state(game_state_json)
|
144 |
+
st.success("Game state loaded successfully!")
|
145 |
+
|
146 |
+
if __name__ == "__main__":
|
147 |
+
app()
|