Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -3,6 +3,7 @@ import pandas as pd
|
|
3 |
import plotly.express as px
|
4 |
import random
|
5 |
import uuid
|
|
|
6 |
from datetime import datetime
|
7 |
from streamlit_flow import streamlit_flow
|
8 |
from streamlit_flow.elements import StreamlitFlowNode, StreamlitFlowEdge
|
@@ -13,7 +14,13 @@ CAT_RIDERS = [
|
|
13 |
{"name": "Whiskers", "type": "Speed", "emoji": "πΎ", "strength": 3, "skill": 7},
|
14 |
{"name": "Fluffy", "type": "Bravery", "emoji": "π¦", "strength": 5, "skill": 5},
|
15 |
{"name": "Midnight", "type": "Stealth", "emoji": "π", "strength": 4, "skill": 6},
|
16 |
-
{"name": "Bella", "type": "Charm", "emoji": "πΊ", "strength": 2, "skill": 8}
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
]
|
18 |
|
19 |
RIDING_GEAR = [
|
@@ -78,6 +85,20 @@ FAILURE_CONCLUSIONS = [
|
|
78 |
"Itβs okay, every cat has a learning curve. π"
|
79 |
]
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
# π§ Game Mechanics
|
82 |
def generate_situation():
|
83 |
return random.choice(SITUATIONS)
|
@@ -86,16 +107,24 @@ def generate_actions():
|
|
86 |
return random.sample(ACTIONS, min(3, len(ACTIONS)))
|
87 |
|
88 |
def evaluate_action(situation, action, gear_strength, rider_skill, history):
|
89 |
-
#
|
90 |
-
base_success_chance =
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
if action['id'] == situation['preferred_action']:
|
92 |
-
success_chance
|
93 |
-
else:
|
94 |
-
success_chance = base_success_chance
|
95 |
|
|
|
96 |
if action['id'] in history:
|
97 |
success_chance += history[action['id']] * 2
|
98 |
|
|
|
|
|
|
|
99 |
outcome = random.randint(1, 100) <= success_chance
|
100 |
return outcome, success_chance
|
101 |
|
@@ -123,7 +152,7 @@ def create_heterogeneous_graph(history_df):
|
|
123 |
edges = []
|
124 |
node_ids = {}
|
125 |
for index, row in history_df.iterrows():
|
126 |
-
situation_node_id = f"situation_{row['situation_id']}_{
|
127 |
action_node_id = f"action_{row['action_id']}_{index}"
|
128 |
outcome_node_id = f"outcome_{index}"
|
129 |
|
@@ -186,10 +215,10 @@ def create_heterogeneous_graph(history_df):
|
|
186 |
# π Markdown Preview with Subpoints for Each Action
|
187 |
def create_markdown_preview(history_df):
|
188 |
markdown = "## π³ Journey Preview\n\n"
|
189 |
-
grouped = history_df.groupby('situation_name', sort=False)
|
190 |
|
191 |
-
for situation_name, group in grouped:
|
192 |
-
markdown += f"### {group.iloc[0]['situation_emoji']} **{situation_name}
|
193 |
for _, row in group.iterrows():
|
194 |
outcome_str = 'β
Success' if row['outcome'] else 'β Failure'
|
195 |
stars = 'β' * int(row['score'])
|
@@ -206,6 +235,9 @@ def update_game_state(game_state, situation, action, outcome, timestamp):
|
|
206 |
# Update stats based on the outcome
|
207 |
game_state = update_character_stats(game_state, outcome)
|
208 |
|
|
|
|
|
|
|
209 |
# Create a new record for the history
|
210 |
new_record = pd.DataFrame({
|
211 |
'user_id': [game_state['user_id']],
|
@@ -214,6 +246,7 @@ def update_game_state(game_state, situation, action, outcome, timestamp):
|
|
214 |
'situation_name': [situation['name']],
|
215 |
'situation_emoji': [situation['emoji']],
|
216 |
'situation_type': [situation['type']],
|
|
|
217 |
'action_id': [action['id']],
|
218 |
'action_name': [action['name']],
|
219 |
'action_emoji': [action['emoji']],
|
@@ -285,9 +318,22 @@ def main():
|
|
285 |
'rider_skill': 0,
|
286 |
'cat_rider': None,
|
287 |
'riding_gear': None,
|
288 |
-
'history_df': pd.DataFrame(columns=['user_id', 'timestamp', 'situation_id', 'situation_name', 'situation_emoji', 'situation_type', 'action_id', 'action_name', 'action_emoji', 'action_type', 'outcome', 'conclusion', 'gear_strength', 'rider_skill', 'score'])
|
|
|
|
|
|
|
289 |
}
|
290 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
# π± Cat Rider Selection
|
292 |
if st.session_state.game_state['cat_rider'] is None:
|
293 |
st.markdown("## Choose Your Cat Rider:")
|
@@ -296,6 +342,7 @@ def main():
|
|
296 |
if cols[i].button(f"{rider['emoji']} {rider['name']} ({rider['type']})", key=f"rider_{i}"):
|
297 |
st.session_state.game_state['cat_rider'] = rider
|
298 |
st.session_state.game_state['rider_skill'] = rider['skill']
|
|
|
299 |
|
300 |
# π Riding Gear Selection
|
301 |
if st.session_state.game_state['riding_gear'] is None and st.session_state.game_state['cat_rider'] is not None:
|
@@ -307,18 +354,30 @@ def main():
|
|
307 |
st.session_state.game_state['gear_strength'] = gear['strength']
|
308 |
|
309 |
# π Game Loop
|
310 |
-
|
311 |
-
situation = generate_situation()
|
312 |
-
actions = generate_actions()
|
313 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
st.markdown(f"## {situation['emoji']} Current Situation: {situation['name']} ({situation['type']})")
|
315 |
st.markdown(situation['description'])
|
316 |
st.markdown("### π Choose your action:")
|
317 |
|
|
|
|
|
|
|
|
|
318 |
cols = st.columns(3)
|
319 |
-
|
320 |
-
|
321 |
-
|
|
|
322 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
323 |
|
324 |
st.markdown(f"You decided to: **{action['name']}** ({action['type']})")
|
@@ -327,11 +386,18 @@ def main():
|
|
327 |
st.markdown(f"**Success Chance:** {success_chance:.2f}%")
|
328 |
|
329 |
if outcome:
|
330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
|
332 |
# π Update game state
|
333 |
-
|
334 |
-
|
335 |
situation,
|
336 |
action,
|
337 |
outcome,
|
@@ -339,42 +405,65 @@ def main():
|
|
339 |
)
|
340 |
|
341 |
# Display conclusion
|
342 |
-
conclusion =
|
343 |
st.markdown(f"**Encounter Conclusion:** {conclusion}")
|
344 |
|
345 |
# Display updated stats
|
346 |
st.markdown(f"**Updated Stats:**")
|
347 |
-
st.markdown(f"πͺ Gear Strength: {
|
348 |
-
st.markdown(f"ποΈ Rider Skill: {
|
349 |
|
350 |
# π
Display Scoreboard
|
351 |
-
display_scoreboard(
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
378 |
|
379 |
if __name__ == "__main__":
|
380 |
main()
|
|
|
3 |
import plotly.express as px
|
4 |
import random
|
5 |
import uuid
|
6 |
+
import os
|
7 |
from datetime import datetime
|
8 |
from streamlit_flow import streamlit_flow
|
9 |
from streamlit_flow.elements import StreamlitFlowNode, StreamlitFlowEdge
|
|
|
14 |
{"name": "Whiskers", "type": "Speed", "emoji": "πΎ", "strength": 3, "skill": 7},
|
15 |
{"name": "Fluffy", "type": "Bravery", "emoji": "π¦", "strength": 5, "skill": 5},
|
16 |
{"name": "Midnight", "type": "Stealth", "emoji": "π", "strength": 4, "skill": 6},
|
17 |
+
{"name": "Bella", "type": "Charm", "emoji": "πΊ", "strength": 2, "skill": 8},
|
18 |
+
{"name": "Shadow", "type": "Mystery", "emoji": "π€", "strength": 4, "skill": 6},
|
19 |
+
{"name": "Simba", "type": "Royalty", "emoji": "π¦", "strength": 5, "skill": 7},
|
20 |
+
{"name": "Luna", "type": "Magic", "emoji": "π", "strength": 3, "skill": 8},
|
21 |
+
{"name": "Leo", "type": "Courage", "emoji": "π―", "strength": 6, "skill": 5},
|
22 |
+
{"name": "Milo", "type": "Playful", "emoji": "πΌ", "strength": 4, "skill": 7},
|
23 |
+
{"name": "Nala", "type": "Grace", "emoji": "π", "strength": 5, "skill": 6}
|
24 |
]
|
25 |
|
26 |
RIDING_GEAR = [
|
|
|
85 |
"Itβs okay, every cat has a learning curve. π"
|
86 |
]
|
87 |
|
88 |
+
# Function to save history to a markdown file
|
89 |
+
def save_history_to_file(history_df, user_id, scenario_name):
|
90 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
91 |
+
filename = f"history_{user_id}_{scenario_name}_{timestamp}.md"
|
92 |
+
markdown = create_markdown_preview(history_df)
|
93 |
+
with open(filename, 'w', encoding='utf-8') as f:
|
94 |
+
f.write(markdown)
|
95 |
+
return filename
|
96 |
+
|
97 |
+
# Function to load saved histories
|
98 |
+
def load_saved_histories():
|
99 |
+
history_files = [f for f in os.listdir('.') if f.startswith('history_') and f.endswith('.md')]
|
100 |
+
return history_files
|
101 |
+
|
102 |
# π§ Game Mechanics
|
103 |
def generate_situation():
|
104 |
return random.choice(SITUATIONS)
|
|
|
107 |
return random.sample(ACTIONS, min(3, len(ACTIONS)))
|
108 |
|
109 |
def evaluate_action(situation, action, gear_strength, rider_skill, history):
|
110 |
+
# Adjusted base success chance to around 50%
|
111 |
+
base_success_chance = 50
|
112 |
+
|
113 |
+
# Adjust success chance based on gear strength and rider skill
|
114 |
+
stat_modifier = (gear_strength + rider_skill - 10) * 2 # Assuming average stats sum to 10
|
115 |
+
success_chance = base_success_chance + stat_modifier
|
116 |
+
|
117 |
+
# Boost success chance for preferred action
|
118 |
if action['id'] == situation['preferred_action']:
|
119 |
+
success_chance += 10
|
|
|
|
|
120 |
|
121 |
+
# Adjust based on action history
|
122 |
if action['id'] in history:
|
123 |
success_chance += history[action['id']] * 2
|
124 |
|
125 |
+
# Clamp success chance between 5% and 95%
|
126 |
+
success_chance = max(5, min(95, success_chance))
|
127 |
+
|
128 |
outcome = random.randint(1, 100) <= success_chance
|
129 |
return outcome, success_chance
|
130 |
|
|
|
152 |
edges = []
|
153 |
node_ids = {}
|
154 |
for index, row in history_df.iterrows():
|
155 |
+
situation_node_id = f"situation_{row['situation_id']}_{row['attempt']}"
|
156 |
action_node_id = f"action_{row['action_id']}_{index}"
|
157 |
outcome_node_id = f"outcome_{index}"
|
158 |
|
|
|
215 |
# π Markdown Preview with Subpoints for Each Action
|
216 |
def create_markdown_preview(history_df):
|
217 |
markdown = "## π³ Journey Preview\n\n"
|
218 |
+
grouped = history_df.groupby(['situation_name', 'attempt'], sort=False)
|
219 |
|
220 |
+
for (situation_name, attempt), group in grouped:
|
221 |
+
markdown += f"### {group.iloc[0]['situation_emoji']} **{situation_name}** (Attempt {attempt})\n"
|
222 |
for _, row in group.iterrows():
|
223 |
outcome_str = 'β
Success' if row['outcome'] else 'β Failure'
|
224 |
stars = 'β' * int(row['score'])
|
|
|
235 |
# Update stats based on the outcome
|
236 |
game_state = update_character_stats(game_state, outcome)
|
237 |
|
238 |
+
# Update attempt count
|
239 |
+
attempt = game_state['current_attempt']
|
240 |
+
|
241 |
# Create a new record for the history
|
242 |
new_record = pd.DataFrame({
|
243 |
'user_id': [game_state['user_id']],
|
|
|
246 |
'situation_name': [situation['name']],
|
247 |
'situation_emoji': [situation['emoji']],
|
248 |
'situation_type': [situation['type']],
|
249 |
+
'attempt': [attempt],
|
250 |
'action_id': [action['id']],
|
251 |
'action_name': [action['name']],
|
252 |
'action_emoji': [action['emoji']],
|
|
|
318 |
'rider_skill': 0,
|
319 |
'cat_rider': None,
|
320 |
'riding_gear': None,
|
321 |
+
'history_df': pd.DataFrame(columns=['user_id', 'timestamp', 'situation_id', 'situation_name', 'situation_emoji', 'situation_type', 'attempt', 'action_id', 'action_name', 'action_emoji', 'action_type', 'outcome', 'conclusion', 'gear_strength', 'rider_skill', 'score']),
|
322 |
+
'current_situation': None,
|
323 |
+
'current_attempt': 1,
|
324 |
+
'actions': []
|
325 |
}
|
326 |
|
327 |
+
# Sidebar functionality
|
328 |
+
st.sidebar.header("π Saved Histories")
|
329 |
+
history_files = load_saved_histories()
|
330 |
+
if history_files:
|
331 |
+
selected_history = st.sidebar.selectbox("Select a history to load:", history_files)
|
332 |
+
if st.sidebar.button("Load Selected History"):
|
333 |
+
with open(selected_history, 'r', encoding='utf-8') as f:
|
334 |
+
st.session_state.game_state['loaded_history'] = f.read()
|
335 |
+
st.sidebar.success(f"Loaded {selected_history}")
|
336 |
+
|
337 |
# π± Cat Rider Selection
|
338 |
if st.session_state.game_state['cat_rider'] is None:
|
339 |
st.markdown("## Choose Your Cat Rider:")
|
|
|
342 |
if cols[i].button(f"{rider['emoji']} {rider['name']} ({rider['type']})", key=f"rider_{i}"):
|
343 |
st.session_state.game_state['cat_rider'] = rider
|
344 |
st.session_state.game_state['rider_skill'] = rider['skill']
|
345 |
+
st.session_state.game_state['user_id'] = rider['name'] # Use rider name as user ID
|
346 |
|
347 |
# π Riding Gear Selection
|
348 |
if st.session_state.game_state['riding_gear'] is None and st.session_state.game_state['cat_rider'] is not None:
|
|
|
354 |
st.session_state.game_state['gear_strength'] = gear['strength']
|
355 |
|
356 |
# π Game Loop
|
357 |
+
game_state = st.session_state.game_state
|
|
|
|
|
358 |
|
359 |
+
if game_state['cat_rider'] is not None and game_state['riding_gear'] is not None:
|
360 |
+
# Check if current_situation is None or if the player succeeded in the previous situation
|
361 |
+
if game_state['current_situation'] is None or game_state['succeeded']:
|
362 |
+
# Generate a new situation
|
363 |
+
game_state['current_situation'] = generate_situation()
|
364 |
+
game_state['current_attempt'] = 1
|
365 |
+
game_state['succeeded'] = False
|
366 |
+
|
367 |
+
situation = game_state['current_situation']
|
368 |
st.markdown(f"## {situation['emoji']} Current Situation: {situation['name']} ({situation['type']})")
|
369 |
st.markdown(situation['description'])
|
370 |
st.markdown("### π Choose your action:")
|
371 |
|
372 |
+
# Generate actions if not already generated
|
373 |
+
if not game_state['actions']:
|
374 |
+
game_state['actions'] = generate_actions()
|
375 |
+
|
376 |
cols = st.columns(3)
|
377 |
+
action_chosen = False
|
378 |
+
for i, action in enumerate(game_state['actions']):
|
379 |
+
if cols[i].button(f"{action['emoji']} {action['name']} ({action['type']})", key=f"action_{i}_{game_state['current_attempt']}"):
|
380 |
+
outcome, success_chance = evaluate_action(situation, action, game_state['gear_strength'], game_state['rider_skill'], game_state['history'])
|
381 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
382 |
|
383 |
st.markdown(f"You decided to: **{action['name']}** ({action['type']})")
|
|
|
386 |
st.markdown(f"**Success Chance:** {success_chance:.2f}%")
|
387 |
|
388 |
if outcome:
|
389 |
+
game_state['score'] += 1
|
390 |
+
game_state['succeeded'] = True
|
391 |
+
# Clear actions for next situation
|
392 |
+
game_state['actions'] = []
|
393 |
+
else:
|
394 |
+
game_state['current_attempt'] += 1
|
395 |
+
# Generate new actions for retry
|
396 |
+
game_state['actions'] = generate_actions()
|
397 |
|
398 |
# π Update game state
|
399 |
+
game_state = update_game_state(
|
400 |
+
game_state,
|
401 |
situation,
|
402 |
action,
|
403 |
outcome,
|
|
|
405 |
)
|
406 |
|
407 |
# Display conclusion
|
408 |
+
conclusion = game_state['history_df'].iloc[-1]['conclusion']
|
409 |
st.markdown(f"**Encounter Conclusion:** {conclusion}")
|
410 |
|
411 |
# Display updated stats
|
412 |
st.markdown(f"**Updated Stats:**")
|
413 |
+
st.markdown(f"πͺ Gear Strength: {game_state['gear_strength']:.2f}")
|
414 |
+
st.markdown(f"ποΈ Rider Skill: {game_state['rider_skill']:.2f}")
|
415 |
|
416 |
# π
Display Scoreboard
|
417 |
+
display_scoreboard(game_state)
|
418 |
+
|
419 |
+
action_chosen = True
|
420 |
+
break # Exit the loop after action is taken
|
421 |
+
|
422 |
+
# If no action was chosen, show a message
|
423 |
+
if not action_chosen:
|
424 |
+
st.markdown("Please choose an action to proceed.")
|
425 |
+
|
426 |
+
# Integration point for both functions
|
427 |
+
if not game_state['history_df'].empty:
|
428 |
+
# π Display Markdown Preview
|
429 |
+
st.markdown(create_markdown_preview(game_state['history_df']))
|
430 |
+
|
431 |
+
# π³ Display Heterogeneous Journey Graph
|
432 |
+
st.markdown("## π³ Your Journey (Heterogeneous Graph)")
|
433 |
+
nodes, edges = create_heterogeneous_graph(game_state['history_df'])
|
434 |
+
try:
|
435 |
+
streamlit_flow('cat_rider_flow',
|
436 |
+
nodes,
|
437 |
+
edges,
|
438 |
+
layout=TreeLayout(direction='down'),
|
439 |
+
fit_view=True,
|
440 |
+
height=600)
|
441 |
+
except Exception as e:
|
442 |
+
st.error(f"An error occurred while rendering the journey graph: {str(e)}")
|
443 |
+
st.markdown("Please try refreshing the page if the graph doesn't appear.")
|
444 |
+
|
445 |
+
# π Character Stats Visualization
|
446 |
+
data = {"Stat": ["Gear Strength π‘οΈ", "Rider Skill π"],
|
447 |
+
"Value": [game_state['gear_strength'], game_state['rider_skill']]}
|
448 |
+
df = pd.DataFrame(data)
|
449 |
+
fig = px.bar(df, x='Stat', y='Value', title="Cat Rider Stats π")
|
450 |
+
st.plotly_chart(fig)
|
451 |
+
|
452 |
+
# Save history to file
|
453 |
+
if st.button("πΎ Save Game History"):
|
454 |
+
filename = save_history_to_file(game_state['history_df'], game_state['user_id'], situation['name'])
|
455 |
+
st.success(f"History saved to {filename}")
|
456 |
+
|
457 |
+
# Provide download button for saved history
|
458 |
+
st.sidebar.markdown("### π₯ Download Histories")
|
459 |
+
for history_file in history_files:
|
460 |
+
with open(history_file, 'r', encoding='utf-8') as f:
|
461 |
+
st.sidebar.download_button(
|
462 |
+
label=f"Download {history_file}",
|
463 |
+
data=f.read(),
|
464 |
+
file_name=history_file,
|
465 |
+
mime='text/markdown'
|
466 |
+
)
|
467 |
|
468 |
if __name__ == "__main__":
|
469 |
main()
|