awacke1 commited on
Commit
3e43c6c
·
verified ·
1 Parent(s): 451847d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +487 -0
app.py ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
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
10
+ from streamlit_flow.layouts import TreeLayout
11
+
12
+ # 1. Configuration
13
+ Site_Name = '🦁CatRider🐈'
14
+ title="🦁CatRider🐈by👤Aaron Wacker"
15
+ helpURL='https://huggingface.co/awacke1'
16
+ bugURL='https://huggingface.co/spaces/awacke1'
17
+ icons='🦁'
18
+
19
+ useConfig=True
20
+ if useConfig: # Component code - useConfig=False should allow it to work if re-evaluating UI elements due to code modify
21
+ st.set_page_config(
22
+ page_title=title,
23
+ page_icon=icons,
24
+ layout="wide",
25
+ #initial_sidebar_state="expanded",
26
+ initial_sidebar_state="auto",
27
+ menu_items={
28
+ 'Get Help': helpURL,
29
+ 'Report a bug': bugURL,
30
+ 'About': title
31
+ }
32
+ )
33
+ # 🐱 Cat Rider and Gear Data
34
+ CAT_RIDERS = [
35
+ {"name": "Whiskers", "type": "Speed", "emoji": "🐾", "strength": 3, "skill": 7},
36
+ {"name": "Fluffy", "type": "Bravery", "emoji": "🦁", "strength": 5, "skill": 5},
37
+ {"name": "Midnight", "type": "Stealth", "emoji": "🌑", "strength": 4, "skill": 6},
38
+ {"name": "Bella", "type": "Charm", "emoji": "😺", "strength": 2, "skill": 8},
39
+ {"name": "Shadow", "type": "Mystery", "emoji": "👤", "strength": 4, "skill": 6},
40
+ {"name": "Simba", "type": "Royalty", "emoji": "🦁", "strength": 5, "skill": 7},
41
+ {"name": "Luna", "type": "Magic", "emoji": "🌙", "strength": 3, "skill": 8},
42
+ {"name": "Leo", "type": "Courage", "emoji": "🐯", "strength": 6, "skill": 5},
43
+ {"name": "Milo", "type": "Playful", "emoji": "😼", "strength": 4, "skill": 7},
44
+ {"name": "Nala", "type": "Grace", "emoji": "🐈", "strength": 5, "skill": 6}
45
+ ]
46
+
47
+ RIDING_GEAR = [
48
+ {"name": "Feathered Boots", "type": "Agility", "strength": 2},
49
+ {"name": "Golden Armor", "type": "Defense", "strength": 4},
50
+ {"name": "Magic Whisker Wand", "type": "Magic", "strength": 3},
51
+ {"name": "Sleek Shadow Cape", "type": "Stealth", "strength": 1}
52
+ ]
53
+
54
+ # 🌍 Game World Data (Expanded to 10 Situations)
55
+ SITUATIONS = [
56
+ {"id": "feline_escape", "name": "The Great Feline Escape", "description": "Your cat rider is trapped in an old mansion...", "emoji": "🚪", "type": "escape", "preferred_action": "agility"},
57
+ {"id": "lost_temple", "name": "The Treasure of the Lost Temple", "description": "On a quest to retrieve an ancient artifact...", "emoji": "🏛️", "type": "exploration", "preferred_action": "resourcefulness"},
58
+ {"id": "royal_tournament", "name": "The Royal Tournament", "description": "Compete in a grand tournament...", "emoji": "👑", "type": "competition", "preferred_action": "bravery"},
59
+ {"id": "sky_race", "name": "The Sky Race", "description": "Compete in the annual Sky Race...", "emoji": "☁️", "type": "competition", "preferred_action": "agility"},
60
+ {"id": "cheese_heist", "name": "The Great Cheese Heist", "description": "Your cat rider must sneak into the royal pantry...", "emoji": "🧀", "type": "heist", "preferred_action": "stealth"},
61
+ {"id": "pirate_cove", "name": "The Pirate Cove", "description": "Sail the high seas with your trusty crew...", "emoji": "🏴‍☠️", "type": "exploration", "preferred_action": "bravery"},
62
+ {"id": "feline_moon_mission", "name": "The Feline Moon Mission", "description": "Blast off into space...", "emoji": "🌕", "type": "exploration", "preferred_action": "resourcefulness"},
63
+ {"id": "purr_summit", "name": "The Purr Summit", "description": "Join a secret gathering of the most intellectual cats...", "emoji": "📜", "type": "debate", "preferred_action": "insight"},
64
+ {"id": "feline_invasion", "name": "The Feline Invasion", "description": "Aliens have invaded Earth...", "emoji": "👽", "type": "battle", "preferred_action": "strategy"},
65
+ {"id": "eternal_catnap", "name": "The Eternal Catnap", "description": "You've entered a sacred temple...", "emoji": "💤", "type": "exploration", "preferred_action": "stealth"}
66
+ ]
67
+
68
+ # 🧠 Expanded Actions (10 Actions)
69
+ ACTIONS = [
70
+ {"id": "stealth", "name": "Use Stealth", "description": "Sneak past obstacles...", "emoji": "🤫", "type": "skill"},
71
+ {"id": "agility", "name": "Showcase Agility", "description": "Perform impressive acrobatic maneuvers...", "emoji": "🏃", "type": "physical"},
72
+ {"id": "charm", "name": "Charm Others", "description": "Use your cat's natural charisma...", "emoji": "😻", "type": "social"},
73
+ {"id": "resourcefulness", "name": "Be Resourceful", "description": "Utilize the environment or items...", "emoji": "🧠", "type": "mental"},
74
+ {"id": "bravery", "name": "Show Bravery", "description": "Face dangers head-on...", "emoji": "🦁", "type": "physical"},
75
+ {"id": "strategy", "name": "Develop a Strategy", "description": "Use tactical thinking...", "emoji": "🧠", "type": "mental"},
76
+ {"id": "speed", "name": "Sprint Away", "description": "Run faster than you've ever run...", "emoji": "🏃‍♀️", "type": "physical"},
77
+ {"id": "insight", "name": "Use Insight", "description": "Tap into ancient feline wisdom...", "emoji": "🔮", "type": "mental"},
78
+ {"id": "distraction", "name": "Create a Distraction", "description": "Use cunning tricks...", "emoji": "🪄", "type": "mental"},
79
+ {"id": "negotiation", "name": "Negotiate", "description": "Use diplomacy and clever negotiation...", "emoji": "💼", "type": "social"}
80
+ ]
81
+
82
+ # Expanded conclusions for outcomes - 10 items each for success and failure
83
+ SUCCESS_CONCLUSIONS = [
84
+ "Your swift paws led you to victory! 🎉",
85
+ "You pounced at the perfect moment! 🏆",
86
+ "The stars aligned for your cat rider! 🌟",
87
+ "You navigated the challenge like a true feline champion! 🐱",
88
+ "Victory is sweet, just like a bowl of fresh milk! 🥛",
89
+ "Your opponents are left in awe of your skills! 😺",
90
+ "You’ve earned the title of Cat Commander! 🏅",
91
+ "All the other cats are jealous of your agility! 🏃‍♂️",
92
+ "Your strategy was flawless, and the victory is yours! 🎖️",
93
+ "Your cat rider is now a legend in the feline world! 👑"
94
+ ]
95
+
96
+ FAILURE_CONCLUSIONS = [
97
+ "You tried your best, but it just wasn’t enough. 😿",
98
+ "Maybe next time, kitty. Keep your tail up! 🐾",
99
+ "That didn’t go as planned. Time for a catnap to recover! 💤",
100
+ "Even the best cats have their off days. 😔",
101
+ "The challenge was too great this time. Better luck next time! 🍀",
102
+ "You might need more than nine lives to get through this. 🐈",
103
+ "The enemy was too clever for your plan. 🧠",
104
+ "You tripped over your own paws! 🐾",
105
+ "The cat gods were not in your favor today. 🙀",
106
+ "It’s okay, every cat has a learning curve. 📚"
107
+ ]
108
+
109
+ # Function to save history to a markdown file
110
+ def save_history_to_file(history_df, user_id):
111
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
112
+ filename = f"history_{user_id}_{timestamp}.md"
113
+ markdown = create_markdown_preview(history_df)
114
+ with open(filename, 'w', encoding='utf-8') as f:
115
+ f.write(markdown)
116
+ return filename
117
+
118
+ # Function to load saved histories
119
+ def load_saved_histories():
120
+ history_files = [f for f in os.listdir('.') if f.startswith('history_') and f.endswith('.md')]
121
+ return history_files
122
+
123
+ # 🧠 Game Mechanics
124
+ def generate_situation():
125
+ return random.choice(SITUATIONS)
126
+
127
+ def generate_actions():
128
+ return random.sample(ACTIONS, min(3, len(ACTIONS)))
129
+
130
+ def evaluate_action(situation, action, gear_strength, rider_skill, history):
131
+ # Adjusted base success chance to around 50%
132
+ base_success_chance = 50
133
+
134
+ # Adjust success chance based on gear strength and rider skill
135
+ stat_modifier = (gear_strength + rider_skill - 10) * 2 # Assuming average stats sum to 10
136
+ success_chance = base_success_chance + stat_modifier
137
+
138
+ # Boost success chance for preferred action
139
+ if action['id'] == situation['preferred_action']:
140
+ success_chance += 10
141
+
142
+ # Adjust based on action history
143
+ if action['id'] in history:
144
+ success_chance += history[action['id']] * 2
145
+
146
+ # Clamp success chance between 5% and 95%
147
+ success_chance = max(5, min(95, success_chance))
148
+
149
+ outcome = random.randint(1, 100) <= success_chance
150
+ return outcome, success_chance
151
+
152
+ def generate_encounter_conclusion(situation, action, outcome):
153
+ if outcome:
154
+ return random.choice(SUCCESS_CONCLUSIONS)
155
+ else:
156
+ return random.choice(FAILURE_CONCLUSIONS)
157
+
158
+ # 🔄 Update character stats based on outcome
159
+ def update_character_stats(game_state, outcome):
160
+ if outcome:
161
+ # Increase stats on success
162
+ game_state['rider_skill'] += 0.5
163
+ game_state['gear_strength'] += 0.2
164
+ else:
165
+ # Decrease stats on failure, but not below 1
166
+ game_state['rider_skill'] = max(1, game_state['rider_skill'] - 0.3)
167
+ game_state['gear_strength'] = max(1, game_state['gear_strength'] - 0.1)
168
+ return game_state
169
+
170
+ # 🌳 Journey Visualization
171
+ def create_knowledge_graph(history_df):
172
+ nodes = []
173
+ edges = []
174
+ node_ids = {}
175
+ score = 0 # To keep track of the score for success nodes
176
+
177
+ for index, row in history_df.iterrows():
178
+ situation_node_id = f"situation_{row['situation_id']}"
179
+ attempt_node_id = f"attempt_{index}"
180
+
181
+ # Situation node
182
+ if situation_node_id not in node_ids:
183
+ situation_node = StreamlitFlowNode(
184
+ situation_node_id,
185
+ pos=(0, 0),
186
+ data={'content': f"{row['situation_emoji']} {row['situation_name']}"},
187
+ type='default',
188
+ shape='ellipse'
189
+ )
190
+ nodes.append(situation_node)
191
+ node_ids[situation_node_id] = situation_node_id
192
+
193
+ # Update score if outcome is success
194
+ if row['outcome']:
195
+ score = row['score']
196
+
197
+ # Attempt node
198
+ outcome_content = '✅ Success' if row['outcome'] else '❌ Failure'
199
+ stars = '⭐' * int(row['score'])
200
+ if row['outcome']:
201
+ # For success nodes, show current score and journey icons
202
+ attempt_content = f"Attempt {row['attempt']}: {row['action_emoji']} {row['action_name']} - {outcome_content}\n{stars}"
203
+ else:
204
+ attempt_content = f"Attempt {row['attempt']}: {row['action_emoji']} {row['action_name']} - {outcome_content}"
205
+
206
+ attempt_node = StreamlitFlowNode(
207
+ attempt_node_id,
208
+ pos=(0, 0),
209
+ data={'content': attempt_content},
210
+ type='default',
211
+ shape='rectangle'
212
+ )
213
+ nodes.append(attempt_node)
214
+
215
+ # Edge from situation to attempt
216
+ edges.append(StreamlitFlowEdge(
217
+ id=f"edge_{situation_node_id}_{attempt_node_id}",
218
+ source=situation_node_id,
219
+ target=attempt_node_id,
220
+ animated=True
221
+ ))
222
+
223
+ return nodes, edges
224
+
225
+ # 📝 Markdown Preview with Subpoints for Each Action
226
+ def create_markdown_preview(history_df):
227
+ markdown = "## 🌳 Journey Preview\n\n"
228
+ grouped = history_df.groupby(['situation_name'], sort=False)
229
+
230
+ for situation_name, group in grouped:
231
+ markdown += f"### {group.iloc[0]['situation_emoji']} **{situation_name}**\n"
232
+ for _, row in group.iterrows():
233
+ outcome_str = '✅ Success' if row['outcome'] else '❌ Failure'
234
+ stars = '⭐' * int(row['score']) if row['outcome'] else ''
235
+ markdown += f"Attempt {row['attempt']}: {row['action_emoji']} **{row['action_name']}**: {outcome_str} {stars}\n"
236
+ markdown += f"{row['conclusion']}\n"
237
+ markdown += "\n"
238
+ return markdown
239
+
240
+ # 🔄 Update game state with the result of the action
241
+ def update_game_state(game_state, situation, action, outcome, timestamp):
242
+ # Generate the encounter conclusion (success or failure)
243
+ conclusion = generate_encounter_conclusion(situation, action, outcome)
244
+
245
+ # Update stats based on the outcome
246
+ game_state = update_character_stats(game_state, outcome)
247
+
248
+ # Update attempt count
249
+ attempt = game_state['current_attempt']
250
+
251
+ # Update score
252
+ if outcome:
253
+ game_state['score'] += 1
254
+
255
+ # Create a new record for the history
256
+ new_record = pd.DataFrame({
257
+ 'user_id': [game_state['user_id']],
258
+ 'timestamp': [timestamp],
259
+ 'situation_id': [situation['id']],
260
+ 'situation_name': [situation['name']],
261
+ 'situation_emoji': [situation['emoji']],
262
+ 'situation_type': [situation['type']],
263
+ 'attempt': [attempt],
264
+ 'action_id': [action['id']],
265
+ 'action_name': [action['name']],
266
+ 'action_emoji': [action['emoji']],
267
+ 'action_type': [action['type']],
268
+ 'outcome': [outcome],
269
+ 'conclusion': [conclusion],
270
+ 'gear_strength': [game_state['gear_strength']],
271
+ 'rider_skill': [game_state['rider_skill']],
272
+ 'score': [game_state['score']]
273
+ })
274
+
275
+ # Add the new record to the game history DataFrame
276
+ game_state['history_df'] = pd.concat([game_state['history_df'], new_record], ignore_index=True)
277
+
278
+ # Update the history of actions (tracking how many times each action was used)
279
+ if action['id'] in game_state['history']:
280
+ game_state['history'][action['id']] += 1 if outcome else -1
281
+ else:
282
+ game_state['history'][action['id']] = 1 if outcome else -1
283
+
284
+ return game_state
285
+
286
+ # 🏅 Display Scoreboard with Star Emojis and Buckyball Outline
287
+ def display_scoreboard(game_state):
288
+ # Calculate number of star emojis based on score
289
+ score = game_state['score']
290
+ stars = '⭐' * int(score)
291
+
292
+ # Create buckyball style outline (simplified)
293
+ outline = ''
294
+ if score > 0:
295
+ outline = '''
296
+ ⬡ ⬡ ⬡ ⬡ ⬡
297
+ ⬡ ⬡ ⬡ ⬡
298
+ ⬡ ⬡ ⬡ ⬡ ⬡
299
+ ⬡ ⬡ ⬡ ⬡
300
+ ⬡ ⬡ ⬡ ⬡ ⬡
301
+ '''
302
+ else:
303
+ outline = 'No successes yet.'
304
+
305
+ st.markdown("## 🏅 Scoreboard")
306
+ st.markdown(f"**Score:** {stars} ({score})")
307
+ st.markdown(outline)
308
+
309
+ # 🎮 Main Game Application
310
+ def main():
311
+ st.title("🐱 Cat Rider 🏇")
312
+
313
+ # 📜 Game Rules
314
+ st.markdown("""
315
+ ### 📜 Game Rules
316
+ | Step | Description |
317
+ |------|-------------|
318
+ | 1️⃣ | Choose your Cat Rider |
319
+ | 2️⃣ | Select your Riding Gear |
320
+ | 3️⃣ | Set off on an Adventure |
321
+ | 4️⃣ | Encounter Challenges and Make Decisions |
322
+ | 5️⃣ | Complete the Quest and Grow Stronger |
323
+ """)
324
+
325
+ # 🏁 Initialize game state
326
+ if 'game_state' not in st.session_state:
327
+ st.session_state.game_state = {
328
+ 'user_id': str(uuid.uuid4()),
329
+ 'score': 0,
330
+ 'history': {},
331
+ 'gear_strength': 0,
332
+ 'rider_skill': 0,
333
+ 'cat_rider': None,
334
+ 'riding_gear': None,
335
+ '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']),
336
+ 'current_situation': None,
337
+ 'current_attempt': 1,
338
+ 'actions': [],
339
+ 'succeeded': False
340
+ }
341
+
342
+ # Sidebar functionality
343
+ st.sidebar.header("📂 Saved Histories")
344
+ history_files = load_saved_histories()
345
+ if history_files:
346
+ selected_history = st.sidebar.selectbox("Select a history to load:", history_files)
347
+ if st.sidebar.button("Load Selected History"):
348
+ with open(selected_history, 'r', encoding='utf-8') as f:
349
+ st.session_state.game_state['loaded_history'] = f.read()
350
+ st.sidebar.success(f"Loaded {selected_history}")
351
+
352
+ # 🐱 Cat Rider Selection
353
+ if st.session_state.game_state['cat_rider'] is None:
354
+ st.markdown("## Choose Your Cat Rider:")
355
+ cols = st.columns(len(CAT_RIDERS))
356
+ for i, rider in enumerate(CAT_RIDERS):
357
+ if cols[i].button(f"{rider['emoji']} {rider['name']} ({rider['type']})", key=f"rider_{i}"):
358
+ st.session_state.game_state['cat_rider'] = rider
359
+ st.session_state.game_state['rider_skill'] = rider['skill']
360
+ st.session_state.game_state['user_id'] = rider['name'] # Use rider name as user ID
361
+
362
+ # 🏇 Riding Gear Selection
363
+ if st.session_state.game_state['riding_gear'] is None and st.session_state.game_state['cat_rider'] is not None:
364
+ st.markdown("## Select Your Riding Gear:")
365
+ cols = st.columns(len(RIDING_GEAR))
366
+ for i, gear in enumerate(RIDING_GEAR):
367
+ if cols[i].button(f"{gear['name']} ({gear['type']})", key=f"gear_{i}"):
368
+ st.session_state.game_state['riding_gear'] = gear
369
+ st.session_state.game_state['gear_strength'] = gear['strength']
370
+
371
+ # 🎭 Game Loop
372
+ game_state = st.session_state.game_state
373
+
374
+ if game_state['cat_rider'] is not None and game_state['riding_gear'] is not None:
375
+ # Check if current_situation is None or if the player succeeded in the previous situation
376
+ if game_state['current_situation'] is None or game_state['succeeded']:
377
+ # Generate a new situation
378
+ game_state['current_situation'] = generate_situation()
379
+ game_state['current_attempt'] = 1
380
+ game_state['succeeded'] = False
381
+ # Clear actions for new situation
382
+ game_state['actions'] = []
383
+
384
+ situation = game_state['current_situation']
385
+ st.markdown(f"## {situation['emoji']} Current Situation: {situation['name']} ({situation['type']})")
386
+ st.markdown(situation['description'])
387
+ st.markdown("### 🎭 Choose your action:")
388
+
389
+ # Generate actions if not already generated
390
+ if not game_state['actions']:
391
+ game_state['actions'] = generate_actions()
392
+
393
+ cols = st.columns(3)
394
+ action_chosen = False
395
+ for i, action in enumerate(game_state['actions']):
396
+ if cols[i].button(f"{action['emoji']} {action['name']} ({action['type']})", key=f"action_{i}_{game_state['current_attempt']}"):
397
+ outcome, success_chance = evaluate_action(situation, action, game_state['gear_strength'], game_state['rider_skill'], game_state['history'])
398
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
399
+
400
+ st.markdown(f"You decided to: **{action['name']}** ({action['type']})")
401
+ st.markdown(action['description'])
402
+ st.markdown(f"**Outcome:** {'✅ Success!' if outcome else '❌ Failure.'}")
403
+ st.markdown(f"**Success Chance:** {success_chance:.2f}%")
404
+
405
+ if outcome:
406
+ game_state['succeeded'] = True
407
+ # Clear actions for next situation
408
+ game_state['actions'] = []
409
+ else:
410
+ game_state['current_attempt'] += 1
411
+ # Generate new actions for retry
412
+ game_state['actions'] = generate_actions()
413
+
414
+ # 🔄 Update game state
415
+ game_state = update_game_state(
416
+ game_state,
417
+ situation,
418
+ action,
419
+ outcome,
420
+ timestamp
421
+ )
422
+
423
+ # Display conclusion
424
+ conclusion = game_state['history_df'].iloc[-1]['conclusion']
425
+ st.markdown(f"**Encounter Conclusion:** {conclusion}")
426
+
427
+ # Display updated stats
428
+ st.markdown(f"**Updated Stats:**")
429
+ st.markdown(f"💪 Gear Strength: {game_state['gear_strength']:.2f}")
430
+ st.markdown(f"🏋️ Rider Skill: {game_state['rider_skill']:.2f}")
431
+
432
+ # 🏅 Display Scoreboard
433
+ display_scoreboard(game_state)
434
+
435
+ action_chosen = True
436
+ break # Exit the loop after action is taken
437
+
438
+ # If no action was chosen, show a message
439
+ if not action_chosen:
440
+ st.markdown("Please choose an action to proceed.")
441
+
442
+ # Integration point for both functions
443
+ if not game_state['history_df'].empty:
444
+ # 📝 Display Markdown Preview
445
+ st.markdown(create_markdown_preview(game_state['history_df']))
446
+
447
+ # 🌳 Display Knowledge Journey Graph
448
+ st.markdown("## 🌳 Your Journey (Knowledge Graph)")
449
+ nodes, edges = create_knowledge_graph(game_state['history_df'])
450
+ try:
451
+ streamlit_flow('cat_rider_flow',
452
+ nodes,
453
+ edges,
454
+ layout=TreeLayout(direction='down'),
455
+ fit_view=True,
456
+ height=600)
457
+ except Exception as e:
458
+ st.error(f"An error occurred while rendering the journey graph: {str(e)}")
459
+ st.markdown("Please try refreshing the page if the graph doesn't appear.")
460
+
461
+ # 📊 Character Stats Visualization
462
+ data = {"Stat": ["Gear Strength 🛡️", "Rider Skill 🏇"],
463
+ "Value": [game_state['gear_strength'], game_state['rider_skill']]}
464
+ df = pd.DataFrame(data)
465
+ fig = px.bar(df, x='Stat', y='Value', title="Cat Rider Stats 📊")
466
+ st.plotly_chart(fig)
467
+
468
+ # Save history to file
469
+ if st.button("💾 Save Game History"):
470
+ filename = save_history_to_file(game_state['history_df'], game_state['user_id'])
471
+ st.success(f"History saved to {filename}")
472
+ # Update history files list
473
+ history_files = load_saved_histories()
474
+
475
+ # Provide download buttons for saved histories
476
+ st.sidebar.markdown("### 📥 Download Histories")
477
+ for history_file in history_files:
478
+ with open(history_file, 'r', encoding='utf-8') as f:
479
+ st.sidebar.download_button(
480
+ label=f"Download {history_file}",
481
+ data=f.read(),
482
+ file_name=history_file,
483
+ mime='text/markdown'
484
+ )
485
+
486
+ if __name__ == "__main__":
487
+ main()