#!/usr/bin/env python3 """ Modified scan handler for Tag Collector Game with Enkephalin rewards for rare tags """ import streamlit as st import torch import time import math from tag_categories import ( get_unlocked_categories, get_collection_power_level ) from game_constants import TAG_CURRENCY_NAME, ENKEPHALIN_CURRENCY_NAME, ENKEPHALIN_ICON, RARITY_LEVELS, TAG_POWER_BONUSES from state_manager import update_game_state def enhanced_scan_button_handler(image_path): """ Enhanced scan button handler with Enkephalin rewards for rare tag discoveries """ try: # Run inference results = st.session_state.model.predict( image_path=image_path, threshold=st.session_state.threshold ) # Get probabilities probs = results['refined_probabilities'][0] # Remove batch dimension # All categories are now unlocked unlocked_categories = get_unlocked_categories(st.session_state) # No limit on tags max_tags = float('inf') # Unlimited # Get collection power bonus power_info = get_collection_power_level(st.session_state) coin_multiplier = power_info['current_level']['coin_bonus'] # Store all detected tags for display all_tags = {} # Format: {category: [(tag, probability, rarity), ...]} found_tags = [] # Tags that pass the threshold total_currency_earned = 0 total_enkephalin_earned = 0 new_tag_count = 0 # Minimum probability to display (can be lower than threshold) min_display_prob = 0.1 # Track any newly completed combinations previously_unlocked = set(st.session_state.unlocked_combinations) if hasattr(st.session_state, 'unlocked_combinations') else set() # Collect all tags that are above threshold candidates = [] for idx in range(len(probs)): prob_value = probs[idx].item() # Only process tags above threshold if prob_value >= st.session_state.threshold: tag, category = st.session_state.model.dataset.get_tag_info(idx) candidates.append({ "idx": idx, "tag": tag, "probability": prob_value, "category": category }) # Sort by probability (highest first) candidates.sort(key=lambda x: x["probability"], reverse=True) # Add all tags to appropriate categories (for display purposes) for idx in range(len(probs)): prob_value = probs[idx].item() # Skip if below display threshold if prob_value < min_display_prob: continue # Get tag information tag, category = st.session_state.model.dataset.get_tag_info(idx) # Determine rarity rarity = determine_tag_rarity(tag, prob_value, category) # Add to category in all_tags if category not in all_tags: all_tags[category] = [] # Check if this tag is above threshold (collected) is_collected = prob_value >= st.session_state.threshold # Add to all_tags with appropriate status if is_collected: all_tags[category].append((tag, prob_value, rarity, "collected")) else: all_tags[category].append((tag, prob_value, rarity, "displayed")) # Process all candidates for collection and currency for candidate in candidates: tag = candidate["tag"] prob_value = candidate["probability"] category = candidate["category"] # Determine rarity rarity = determine_tag_rarity(tag, prob_value, category) # Add tag to collection and get currency currency_earned, enkephalin_earned, is_new_tag = add_tag_to_collection(tag, prob_value, category, coin_multiplier) # Count new tags if is_new_tag: new_tag_count += 1 # Add to total currency earned if currency_earned > 0: total_currency_earned += currency_earned # Add to total enkephalin earned if enkephalin_earned > 0: total_enkephalin_earned += enkephalin_earned # Add to found tags list found_tags.append({ "tag": tag, "probability": prob_value, "category": category, "currency": currency_earned, "enkephalin": enkephalin_earned, "rarity": rarity, "is_new": is_new_tag }) # Sort each category by probability for category in all_tags: all_tags[category].sort(key=lambda x: x[1], reverse=True) # Update game stats - IMPORTANT: Direct modification to ensure updates # are properly tracked by Streamlit's session state mechanism st.session_state.game_stats["images_processed"] += 1 st.session_state.game_stats["total_tags_found"] += len(found_tags) # Update enkephalin stats if we earned any if total_enkephalin_earned > 0: if "enkephalin_generated" not in st.session_state.game_stats: st.session_state.game_stats["enkephalin_generated"] = 0 st.session_state.game_stats["enkephalin_generated"] += total_enkephalin_earned # Update tag power ONCE threshold_bonus, coin_bonus = calculate_tag_power() st.session_state.tag_power_bonus = threshold_bonus st.session_state.coin_multiplier = coin_bonus # Force update of state_version to trigger UI refresh if 'state_version' not in st.session_state: st.session_state.state_version = 0 st.session_state.state_version += 1 # Store results in session state st.session_state.current_scan = { "all_tags": all_tags, "found_tags": found_tags, "threshold": st.session_state.threshold, "total_currency_earned": total_currency_earned, "total_enkephalin_earned": total_enkephalin_earned, "new_tag_count": new_tag_count, } # # Save game state explicitly # save_game_state() return True except Exception as e: st.error(f"Error scanning image: {str(e)}") import traceback st.code(traceback.format_exc()) return False def determine_tag_rarity(tag, probability, category): """ Determine the rarity of a tag based on the metadata if available, otherwise use a simplified probability-based approach. Always returns a valid rarity level. """ # Set a default rarity (safeguard against returning None) default_rarity = "Canard" try: # If we have the rarity metadata loaded, use it if hasattr(st.session_state, 'tag_rarity_metadata') and st.session_state.tag_rarity_metadata: # Check if this tag exists in our metadata if tag in st.session_state.tag_rarity_metadata: tag_info = st.session_state.tag_rarity_metadata[tag] # Handle both new and old format if isinstance(tag_info, dict) and "rarity" in tag_info: # New format with rarity and sample_count rarity = tag_info["rarity"] else: # Old format where tag_info is the rarity string directly rarity = tag_info # Verify the rarity is valid if rarity in RARITY_LEVELS: return rarity # Special handling for rating_* and year_* tags if tag.startswith("rating_") or tag.startswith("year_"): for metadata_tag in st.session_state.tag_rarity_metadata: if metadata_tag == tag: tag_info = st.session_state.tag_rarity_metadata[metadata_tag] if isinstance(tag_info, dict) and "rarity" in tag_info: rarity = tag_info["rarity"] else: rarity = tag_info if rarity in RARITY_LEVELS: return rarity except Exception as e: # Log the error but don't crash print(f"Error determining rarity for tag {tag}: {str(e)}") # Return the default rarity as a fallback return default_rarity def get_enkephalin_reward(rarity): """ Determine the Enkephalin reward based on tag rarity Args: rarity (str): The tag rarity level Returns: int: Amount of Enkephalin to award """ # Get enkephalin reward from TAG_POWER_BONUSES if rarity in TAG_POWER_BONUSES: return TAG_POWER_BONUSES[rarity]["enkephalin_reward"] return 0 def add_tag_to_collection(tag, probability, category, coin_multiplier=1.0): """ Add a tag to the user's collection and award currency and enkephalin for new discoveries. """ try: # Get the tag's rarity rarity = determine_tag_rarity(tag, probability, category) # Check if this is a new tag discovery is_new_tag = tag not in st.session_state.collected_tags # Check if tag was previously sacrificed was_sacrificed = hasattr(st.session_state, 'sacrificed_tags') and tag in st.session_state.sacrificed_tags # Get base currency value for this rarity base_currency_value = RARITY_LEVELS[rarity]["value"] # Basic debug info - always print this print(f"Processing tag: {tag}, rarity: {rarity}, is_new: {is_new_tag}, base_value: {base_currency_value}") # Initialize currency earned currency_earned = 0 enkephalin_earned = 0 # Record the tag acquisition timestamp = time.strftime("%H:%M:%S") # Update tag collection if tag in st.session_state.collected_tags: # If already collected, just increment the count, no new currency st.session_state.collected_tags[tag]["count"] += 1 else: # New tag discovery - create entry in collection st.session_state.collected_tags[tag] = { "count": 1, "rarity": rarity, "category": category, "discovery_time": timestamp } # Award currency and enkephalin only if not previously sacrificed and is a new tag if not was_sacrificed and is_new_tag: # Calculate tag power bonuses tag_power_bonus, base_coin_multiplier = calculate_tag_power() # Now print the debug info AFTER calculating base_coin_multiplier print(f" Base multiplier: {base_coin_multiplier}, Collection multiplier: {coin_multiplier}") # Apply collection power multiplier total_multiplier = base_coin_multiplier * coin_multiplier # Check if category is mastered for additional bonus if hasattr(st.session_state, 'mastered_categories') and category in st.session_state.mastered_categories: from tag_categories import CATEGORY_MASTERY_BONUS total_multiplier += CATEGORY_MASTERY_BONUS.get('coin_multiplier', 0) # Award currency with multiplier for new discoveries currency_earned = int(base_currency_value * total_multiplier) # Award enkephalin for new tag discoveries based on rarity enkephalin_earned = get_enkephalin_reward(rarity) # Apply any enkephalin bonus from achievements if it exists if hasattr(st.session_state, 'enkephalin_bonus') and st.session_state.enkephalin_bonus > 0: enkephalin_earned = int(enkephalin_earned * (1 + st.session_state.enkephalin_bonus)) print(f" Final multiplier: {total_multiplier}, Final award: {currency_earned} {TAG_CURRENCY_NAME}, {enkephalin_earned} {ENKEPHALIN_CURRENCY_NAME}") # Add to player's currency st.session_state.tag_currency += currency_earned # Add to player's enkephalin if earned if enkephalin_earned > 0: st.session_state.enkephalin += enkephalin_earned # Track in stats st.session_state.game_stats["total_currency_earned"] += currency_earned # Add to history for new discoveries if not hasattr(st.session_state, 'tag_history'): st.session_state.tag_history = [] st.session_state.tag_history.append({ "tag": tag, "rarity": rarity, "value": currency_earned, "enkephalin": enkephalin_earned, "time": timestamp, "is_new": True }) # Always increment total tags found count st.session_state.game_stats["total_tags_found"] += 1 # Update tag power calculations with the new tag threshold_bonus, coin_bonus = calculate_tag_power() st.session_state.tag_power_bonus = threshold_bonus st.session_state.coin_multiplier = coin_bonus update_game_state(tag_currency=st.session_state.tag_currency, enkephalin=st.session_state.enkephalin) # Return the earned currency, enkephalin, and new tag status return currency_earned, enkephalin_earned, is_new_tag except Exception as e: # Log the error print(f"Error adding tag '{tag}' to collection: {str(e)}") import traceback traceback.print_exc() # Return safe defaults to prevent crashing return 0, 0, False def calculate_tag_power(): """ Calculate tag power bonuses based on collected tags (not sacrificed ones). Tags that are sacrificed no longer contribute to tag power until recollected. Returns: (threshold_reduction, coin_multiplier) """ if not hasattr(st.session_state, 'collected_tags'): return 0, 1.0 total_threshold_reduction = 0 total_coin_multiplier = 1.0 # Calculate bonuses from individual tags for tag, info in st.session_state.collected_tags.items(): rarity = info["rarity"] if rarity in TAG_POWER_BONUSES: # Add threshold reduction (scales with tag count, but with diminishing returns) count = info["count"] # Use logarithmic scaling to prevent excessive bonuses from farming scaling_factor = 1 + (math.log(count) / 2) if count > 1 else 1 # Apply the bonus total_coin_multiplier += TAG_POWER_BONUSES[rarity]["coin_multiplier"] * scaling_factor # Apply collection power coin bonus if hasattr(st.session_state, 'collection_power_coin_bonus'): total_coin_multiplier *= st.session_state.collection_power_coin_bonus return total_threshold_reduction, total_coin_multiplier