# 1. Configuration import streamlit as st import pandas as pd import plotly.express as px import random import uuid import os import re from datetime import datetime from streamlit_flow import streamlit_flow from streamlit_flow.elements import StreamlitFlowNode, StreamlitFlowEdge from streamlit_flow.layouts import RadialLayout # Set page configuration to wide mode Site_Name = '๐ŸฆCatRider๐Ÿˆ' title="๐ŸฆCatRider๐Ÿˆby๐Ÿ‘คAaron Wacker" helpURL='https://huggingface.co/awacke1' bugURL='https://huggingface.co/spaces/awacke1' icons='๐Ÿฆ' useConfig=True if useConfig: st.set_page_config( page_title=title, page_icon=icons, layout="wide", initial_sidebar_state="auto", menu_items={ 'Get Help': helpURL, 'Report a bug': bugURL, 'About': title } ) # ๐Ÿฑ Cat Rider and Gear Data CAT_RIDERS = [ {"name": "Whiskers", "type": "Speed", "emoji": "๐Ÿพ", "strength": 3, "skill": 7}, {"name": "Fluffy", "type": "Bravery", "emoji": "๐Ÿฆ", "strength": 5, "skill": 5}, {"name": "Midnight", "type": "Stealth", "emoji": "๐ŸŒ‘", "strength": 4, "skill": 6}, {"name": "Bella", "type": "Charm", "emoji": "๐Ÿ˜บ", "strength": 2, "skill": 8}, {"name": "Shadow", "type": "Mystery", "emoji": "๐Ÿ‘ค", "strength": 4, "skill": 6}, {"name": "Simba", "type": "Royalty", "emoji": "๐Ÿฆ", "strength": 5, "skill": 7}, {"name": "Luna", "type": "Magic", "emoji": "๐ŸŒ™", "strength": 3, "skill": 8}, {"name": "Leo", "type": "Courage", "emoji": "๐Ÿฏ", "strength": 6, "skill": 5}, {"name": "Milo", "type": "Playful", "emoji": "๐Ÿ˜ผ", "strength": 4, "skill": 7}, {"name": "Nala", "type": "Grace", "emoji": "๐Ÿˆ", "strength": 5, "skill": 6} ] RIDING_GEAR = [ {"name": "Feathered Boots", "type": "Agility", "strength": 2}, {"name": "Golden Armor", "type": "Defense", "strength": 4}, {"name": "Magic Whisker Wand", "type": "Magic", "strength": 3}, {"name": "Sleek Shadow Cape", "type": "Stealth", "strength": 1} ] # ๐ŸŒ Game World Data (Expanded to 10 Situations) SITUATIONS = [ {"id": "feline_escape", "name": "The Great Feline Escape", "description": "Your cat rider is trapped in an old mansion...", "emoji": "๐Ÿšช", "type": "escape", "preferred_action": "agility"}, {"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"}, {"id": "royal_tournament", "name": "The Royal Tournament", "description": "Compete in a grand tournament...", "emoji": "๐Ÿ‘‘", "type": "competition", "preferred_action": "bravery"}, {"id": "sky_race", "name": "The Sky Race", "description": "Compete in the annual Sky Race...", "emoji": "โ˜๏ธ", "type": "competition", "preferred_action": "agility"}, {"id": "cheese_heist", "name": "The Great Cheese Heist", "description": "Your cat rider must sneak into the royal pantry...", "emoji": "๐Ÿง€", "type": "heist", "preferred_action": "stealth"}, {"id": "pirate_cove", "name": "The Pirate Cove", "description": "Sail the high seas with your trusty crew...", "emoji": "๐Ÿดโ€โ˜ ๏ธ", "type": "exploration", "preferred_action": "bravery"}, {"id": "feline_moon_mission", "name": "The Feline Moon Mission", "description": "Blast off into space...", "emoji": "๐ŸŒ•", "type": "exploration", "preferred_action": "resourcefulness"}, {"id": "purr_summit", "name": "The Purr Summit", "description": "Join a secret gathering of the most intellectual cats...", "emoji": "๐Ÿ“œ", "type": "debate", "preferred_action": "insight"}, {"id": "feline_invasion", "name": "The Feline Invasion", "description": "Aliens have invaded Earth...", "emoji": "๐Ÿ‘ฝ", "type": "battle", "preferred_action": "strategy"}, {"id": "eternal_catnap", "name": "The Eternal Catnap", "description": "You've entered a sacred temple...", "emoji": "๐Ÿ’ค", "type": "exploration", "preferred_action": "stealth"} ] # ๐Ÿง  Expanded Actions (10 Actions) ACTIONS = [ {"id": "stealth", "name": "Use Stealth", "description": "Sneak past obstacles...", "emoji": "๐Ÿคซ", "type": "skill"}, {"id": "agility", "name": "Showcase Agility", "description": "Perform impressive acrobatic maneuvers...", "emoji": "๐Ÿƒ", "type": "physical"}, {"id": "charm", "name": "Charm Others", "description": "Use your cat's natural charisma...", "emoji": "๐Ÿ˜ป", "type": "social"}, {"id": "resourcefulness", "name": "Be Resourceful", "description": "Utilize the environment or items...", "emoji": "๐Ÿง ", "type": "mental"}, {"id": "bravery", "name": "Show Bravery", "description": "Face dangers head-on...", "emoji": "๐Ÿฆ", "type": "physical"}, {"id": "strategy", "name": "Develop a Strategy", "description": "Use tactical thinking...", "emoji": "๐Ÿง ", "type": "mental"}, {"id": "speed", "name": "Sprint Away", "description": "Run faster than you've ever run...", "emoji": "๐Ÿƒโ€โ™€๏ธ", "type": "physical"}, {"id": "insight", "name": "Use Insight", "description": "Tap into ancient feline wisdom...", "emoji": "๐Ÿ”ฎ", "type": "mental"}, {"id": "distraction", "name": "Create a Distraction", "description": "Use cunning tricks...", "emoji": "๐Ÿช„", "type": "mental"}, {"id": "negotiation", "name": "Negotiate", "description": "Use diplomacy and clever negotiation...", "emoji": "๐Ÿ’ผ", "type": "social"} ] # Expanded conclusions for outcomes - 10 items each for success and failure SUCCESS_CONCLUSIONS = [ "Your swift paws led you to victory! ๐ŸŽ‰", "You pounced at the perfect moment! ๐Ÿ†", "The stars aligned for your cat rider! ๐ŸŒŸ", "You navigated the challenge like a true feline champion! ๐Ÿฑ", "Victory is sweet, just like a bowl of fresh milk! ๐Ÿฅ›", "Your opponents are left in awe of your skills! ๐Ÿ˜บ", "Youโ€™ve earned the title of Cat Commander! ๐Ÿ…", "All the other cats are jealous of your agility! ๐Ÿƒโ€โ™‚๏ธ", "Your strategy was flawless, and the victory is yours! ๐ŸŽ–๏ธ", "Your cat rider is now a legend in the feline world! ๐Ÿ‘‘" ] FAILURE_CONCLUSIONS = [ "You tried your best, but it just wasnโ€™t enough. ๐Ÿ˜ฟ", "Maybe next time, kitty. Keep your tail up! ๐Ÿพ", "That didnโ€™t go as planned. Time for a catnap to recover! ๐Ÿ’ค", "Even the best cats have their off days. ๐Ÿ˜”", "The challenge was too great this time. Better luck next time! ๐Ÿ€", "You might need more than nine lives to get through this. ๐Ÿˆ", "The enemy was too clever for your plan. ๐Ÿง ", "You tripped over your own paws! ๐Ÿพ", "The cat gods were not in your favor today. ๐Ÿ™€", "Itโ€™s okay, every cat has a learning curve. ๐Ÿ“š" ] # Function to save history to a markdown file def save_history_to_file(history_df, user_id): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"history_{user_id}_{timestamp}.md" markdown, _, _ = process_journey_history(history_df) with open(filename, 'w', encoding='utf-8') as f: f.write(markdown) return filename # Function to load saved histories and reconstruct game state def load_saved_histories(user_id): history_files = [f for f in os.listdir('.') if f.startswith(f'history_{user_id}_') and f.endswith('.md')] history_files.sort() # Ensure files are sorted by timestamp combined_history = '' for history_file in history_files: with open(history_file, 'r', encoding='utf-8') as f: combined_history += f.read() + '\n' return combined_history # Function to parse markdown history and reconstruct history DataFrame def parse_history_markdown(markdown_text): lines = markdown_text.split('\n') data = [] current_situation = None for line in lines: situation_match = re.match(r"### (\S+) \*\*(.+)\*\*", line) if situation_match: situation_emoji = situation_match.group(1) situation_name = situation_match.group(2) continue attempt_match = re.match(r"Attempt (\d+): (\S+) \*\*(.+)\*\*: (โœ… Success|โŒ Failure) (\S*)", line) if attempt_match: attempt = int(attempt_match.group(1)) action_emoji = attempt_match.group(2) action_name = attempt_match.group(3) outcome_str = attempt_match.group(4) stars = attempt_match.group(5) outcome = True if outcome_str == 'โœ… Success' else False score = len(stars) conclusion_line_index = lines.index(line) + 1 conclusion = lines[conclusion_line_index] if conclusion_line_index < len(lines) else '' data.append({ 'situation_name': situation_name, 'situation_emoji': situation_emoji, 'attempt': attempt, 'action_name': action_name, 'action_emoji': action_emoji, 'outcome': outcome, 'score': score, 'conclusion': conclusion }) history_df = pd.DataFrame(data) return history_df # ๐Ÿง  Game Mechanics def generate_situation(): return random.choice(SITUATIONS) def generate_actions(): return random.sample(ACTIONS, min(3, len(ACTIONS))) def evaluate_action(situation, action, gear_strength, rider_skill, history): # Adjusted base success chance to around 50% base_success_chance = 50 # Adjust success chance based on gear strength and rider skill stat_modifier = (gear_strength + rider_skill - 10) * 2 # Assuming average stats sum to 10 success_chance = base_success_chance + stat_modifier # Boost success chance for preferred action if action['id'] == situation['preferred_action']: success_chance += 10 # Adjust based on action history if action['id'] in history: success_chance += history[action['id']] * 2 # Clamp success chance between 5% and 95% success_chance = max(5, min(95, success_chance)) outcome = random.randint(1, 100) <= success_chance return outcome, success_chance def generate_encounter_conclusion(situation, action, outcome): if outcome: return random.choice(SUCCESS_CONCLUSIONS) else: return random.choice(FAILURE_CONCLUSIONS) # ๐Ÿ”„ Update character stats based on outcome def update_character_stats(game_state, outcome): if outcome: # Increase stats on success game_state['rider_skill'] += 0.5 game_state['gear_strength'] += 0.2 else: # Decrease stats on failure, but not below 1 game_state['rider_skill'] = max(1, game_state['rider_skill'] - 0.3) game_state['gear_strength'] = max(1, game_state['gear_strength'] - 0.1) return game_state # ๐ŸŒณ Process Journey History to Create Markdown and Graph Data def process_journey_history(history_df): markdown = "## ๐ŸŒณ Journey Preview\n\n" nodes = [] edges = [] node_ids = {} score = 0 # To keep track of the score for success nodes grouped = history_df.groupby(['situation_name'], sort=False) # Main node to connect all situations main_node_id = "main" main_node = StreamlitFlowNode( main_node_id, pos=(0, 0), data={'content': "# Your Journey"}, type='input', target_position='bottom', width=200 ) nodes.append(main_node) for situation_name, group in grouped: situation_emoji = group.iloc[0]['situation_emoji'] situation_id = group.iloc[0].get('situation_id', str(uuid.uuid4())) situation_node_id = f"situation_{situation_id}" markdown += f"### {situation_emoji} **{situation_name}**\n" # Create situation node if not already created if situation_node_id not in node_ids: situation_node = StreamlitFlowNode( situation_node_id, pos=(0, 0), data={'content': f"### {situation_emoji} {situation_name}"}, type='default', target_position='top', source_position='bottom', width=200 ) nodes.append(situation_node) node_ids[situation_node_id] = situation_node_id # Edge from main node to situation node edges.append(StreamlitFlowEdge( id=f"edge_{main_node_id}_{situation_node_id}", source=main_node_id, target=situation_node_id, animated=True )) for idx, row in group.iterrows(): attempt = row['attempt'] action_emoji = row['action_emoji'] action_name = row['action_name'] outcome = row['outcome'] outcome_str = 'โœ… Success' if outcome else 'โŒ Failure' stars = 'โญ' * int(row.get('score', 0)) if outcome else '' conclusion = row.get('conclusion', '') markdown += f"Attempt {attempt}: {action_emoji} **{action_name}**: {outcome_str} {stars}\n" markdown += f"{conclusion}\n" # Create attempt node attempt_node_id = f"attempt_{situation_id}_{attempt}_{idx}" attempt_content = f"Attempt {attempt}: {action_emoji} {action_name}\n{outcome_str} {stars}\n{conclusion}" attempt_node = StreamlitFlowNode( attempt_node_id, pos=(0, 0), data={'content': attempt_content}, type='output', target_position='top', source_position='bottom', width=250 ) nodes.append(attempt_node) # Edge from situation to attempt edges.append(StreamlitFlowEdge( id=f"edge_{situation_node_id}_{attempt_node_id}", source=situation_node_id, target=attempt_node_id, animated=True )) markdown += "\n" return markdown, nodes, edges # ๐Ÿ”„ Update game state with the result of the action def update_game_state(game_state, situation, action, outcome, timestamp): # Generate the encounter conclusion (success or failure) conclusion = generate_encounter_conclusion(situation, action, outcome) # Update stats based on the outcome game_state = update_character_stats(game_state, outcome) # Update attempt count attempt = game_state['current_attempt'] # Update score if outcome: game_state['score'] += 1 # Create a new record for the history new_record = pd.DataFrame({ 'user_id': [game_state['user_id']], 'timestamp': [timestamp], 'situation_id': [situation['id']], 'situation_name': [situation['name']], 'situation_emoji': [situation['emoji']], 'situation_type': [situation['type']], 'attempt': [attempt], 'action_id': [action['id']], 'action_name': [action['name']], 'action_emoji': [action['emoji']], 'action_type': [action['type']], 'outcome': [outcome], 'conclusion': [conclusion], 'gear_strength': [game_state['gear_strength']], 'rider_skill': [game_state['rider_skill']], 'score': [game_state['score']] }) # Add the new record to the game history DataFrame game_state['history_df'] = pd.concat([game_state['history_df'], new_record], ignore_index=True) # Update the history of actions (tracking how many times each action was used) if action['id'] in game_state['history']: game_state['history'][action['id']] += 1 if outcome else -1 else: game_state['history'][action['id']] = 1 if outcome else -1 # Automatically save history after each action save_history_to_file(game_state['history_df'], game_state['user_id']) return game_state # ๐Ÿ… Display Scoreboard with Star Emojis and Buckyball Outline def display_scoreboard(game_state): # Calculate number of star emojis based on score score = game_state['score'] stars = 'โญ' * int(score) # Create buckyball style outline (simplified) outline = '' if score > 0: outline = ''' โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก โฌก ''' else: outline = 'No successes yet.' st.markdown("## ๐Ÿ… Scoreboard") st.markdown(f"**Score:** {stars} ({score})") st.markdown(outline) # ๐ŸŽฎ Main Game Application def main(): st.title("๐Ÿฑ Cat Rider ๐Ÿ‡") # ๐Ÿ“œ Game Rules st.markdown(""" ### ๐Ÿ“œ Game Rules | Step | Description | |------|-------------| | 1๏ธโƒฃ | Choose your Cat Rider | | 2๏ธโƒฃ | Select your Riding Gear | | 3๏ธโƒฃ | Set off on an Adventure | | 4๏ธโƒฃ | Encounter Challenges and Make Decisions | | 5๏ธโƒฃ | Complete the Quest and Grow Stronger | """) # ๐Ÿ Initialize game state if 'game_state' not in st.session_state: st.session_state.game_state = { 'user_id': None, 'score': 0, 'history': {}, 'gear_strength': 0, 'rider_skill': 0, 'cat_rider': None, 'riding_gear': None, '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']), 'current_situation': None, 'current_attempt': 1, 'actions': [], 'succeeded': False } game_state = st.session_state.game_state # ๐Ÿฑ Cat Rider Selection or Loading Previous State if game_state['cat_rider'] is None: st.markdown("## Choose Your Cat Rider or Load Previous Journey:") # Check for existing histories existing_riders = [f.split('_')[1] for f in os.listdir('.') if f.startswith('history_')] existing_riders = list(set(existing_riders)) cols = st.columns(len(CAT_RIDERS)) for i, rider in enumerate(CAT_RIDERS): if cols[i].button(f"{rider['emoji']} {rider['name']} ({rider['type']})", key=f"rider_{i}"): game_state['cat_rider'] = rider game_state['rider_skill'] = rider['skill'] game_state['user_id'] = rider['name'] # Use rider name as user ID # Load existing history if available existing_history = load_saved_histories(game_state['user_id']) if existing_history: history_df = parse_history_markdown(existing_history) game_state['history_df'] = history_df game_state['score'] = history_df['score'].max() if not history_df.empty else 0 # ๐Ÿ‡ Riding Gear Selection if game_state['riding_gear'] is None and game_state['cat_rider'] is not None: st.markdown("## Select Your Riding Gear:") cols = st.columns(len(RIDING_GEAR)) for i, gear in enumerate(RIDING_GEAR): if cols[i].button(f"{gear['name']} ({gear['type']})", key=f"gear_{i}"): game_state['riding_gear'] = gear game_state['gear_strength'] = gear['strength'] # ๐ŸŽญ Game Loop if game_state['cat_rider'] is not None and game_state['riding_gear'] is not None: # Check if current_situation is None or if the player succeeded in the previous situation if game_state['current_situation'] is None or game_state['succeeded']: # Generate a new situation game_state['current_situation'] = generate_situation() game_state['current_attempt'] = 1 game_state['succeeded'] = False # Clear actions for new situation game_state['actions'] = [] situation = game_state['current_situation'] st.markdown(f"## {situation['emoji']} Current Situation: {situation['name']} ({situation['type']})") st.markdown(situation['description']) st.markdown("### ๐ŸŽญ Choose your action:") # Generate actions if not already generated if not game_state['actions']: game_state['actions'] = generate_actions() cols = st.columns(3) action_chosen = False for i, action in enumerate(game_state['actions']): if cols[i].button(f"{action['emoji']} {action['name']} ({action['type']})", key=f"action_{i}_{game_state['current_attempt']}"): outcome, success_chance = evaluate_action(situation, action, game_state['gear_strength'], game_state['rider_skill'], game_state['history']) timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") st.markdown(f"You decided to: **{action['name']}** ({action['type']})") st.markdown(action['description']) st.markdown(f"**Outcome:** {'โœ… Success!' if outcome else 'โŒ Failure.'}") st.markdown(f"**Success Chance:** {success_chance:.2f}%") if outcome: game_state['succeeded'] = True # Clear actions for next situation game_state['actions'] = [] else: game_state['current_attempt'] += 1 # Generate new actions for retry game_state['actions'] = generate_actions() # ๐Ÿ”„ Update game state game_state = update_game_state( game_state, situation, action, outcome, timestamp ) # Display conclusion conclusion = game_state['history_df'].iloc[-1]['conclusion'] st.markdown(f"**Encounter Conclusion:** {conclusion}") # Display updated stats st.markdown(f"**Updated Stats:**") st.markdown(f"๐Ÿ’ช Gear Strength: {game_state['gear_strength']:.2f}") st.markdown(f"๐Ÿ‹๏ธ Rider Skill: {game_state['rider_skill']:.2f}") # ๐Ÿ… Display Scoreboard display_scoreboard(game_state) action_chosen = True break # Exit the loop after action is taken # If no action was chosen, show a message if not action_chosen: st.markdown("Please choose an action to proceed.") # Integration point for both functions if not game_state['history_df'].empty: # ๐Ÿ“ Process Journey History to get markdown and graph data markdown_preview, nodes, edges = process_journey_history(game_state['history_df']) # ๐Ÿ“ Display Markdown Preview st.markdown(markdown_preview) # ๐ŸŒณ Display Knowledge Journey Graph st.markdown("## ๐ŸŒณ Your Journey (Knowledge Graph)") try: streamlit_flow('cat_rider_flow', nodes, edges, layout=RadialLayout(), fit_view=True, height=1000) except Exception as e: st.error(f"An error occurred while rendering the journey graph: {str(e)}") st.markdown("Please try refreshing the page if the graph doesn't appear.") # ๐Ÿ“Š Character Stats Visualization data = {"Stat": ["Gear Strength ๐Ÿ›ก๏ธ", "Rider Skill ๐Ÿ‡"], "Value": [game_state['gear_strength'], game_state['rider_skill']]} df = pd.DataFrame(data) fig = px.bar(df, x='Stat', y='Value', title="Cat Rider Stats ๐Ÿ“Š") st.plotly_chart(fig) # Automatically load saved history if available if game_state['user_id'] and not game_state['history_df'].empty: existing_history = load_saved_histories(game_state['user_id']) if existing_history: st.markdown("## ๐Ÿ“‚ Loaded Journey History") st.markdown(existing_history) if __name__ == "__main__": main()