import gradio as gr import pandas as pd import numpy as np from xgboost import Booster, DMatrix import requests # Generate card_numbers card_numbers = { "Archers": 1, "Archer Queen": 2, "Baby Dragon": 3, "Balloon": 4, "Bandit": 5, "Barbarians": 6, "Bats": 7, "Battle Healer": 8, "Battle Ram": 9, "Bomber": 10, "Bowler": 11, "Bush Goblins": 12, "Cannon Cart": 13, "Cursed Hog": 14, "Dark Prince": 15, "Dart Goblin": 16, "Electro Dragon": 17, "Electro Giant": 18, "Electro Spirit": 19, "Electro Wizard": 20, "Elite Barbarians": 21, "Elixir Blob": 22, "Elixir Golem": 23, "Elixir Golemite": 24, "Executioner": 25, "Firecracker": 26, "Fire Spirit": 27, "Fisherman": 28, "Flying Machine": 29, "Giant": 30, "Giant Skeleton": 31, "Goblin Brawler": 32, "Goblin Gang": 33, "Goblin Demolisher": 34, "Goblin Giant": 35, "Goblin Machine": 36, "Goblins": 37, "Goblinstein": 38, "Golden Knight": 39, "Golem": 40, "Golemite": 41, "Guardienne": 42, "Guards": 43, "Hog Rider": 44, "Hunter": 45, "Heal Spirit": 46, "Ice Golem": 47, "Ice Spirit": 48, "Ice Wizard": 49, "Inferno Dragon": 50, "Knight": 51, "Lava Hound": 52, "Lava Pup": 53, "Little Prince": 54, "Lumberjack": 55, "Magic Archer": 56, "Mega Knight": 57, "Mega Minion": 58, "Mighty Miner": 59, "Miner": 60, "Mini P.E.K.K.A.": 61, "Minion Horde": 62, "Minions": 63, "Monk": 64, "Mother Witch": 65, "Monster": 66, "Musketeer": 67, "Night Witch": 68, "P.E.K.K.A.": 69, "Phoenix": 70, "Reborn Phoenix": 71, "Prince": 72, "Princess": 73, "Ram Rider": 74, "Rascal Boy": 75, "Rascal Girl": 76, "Royal Ghost": 77, "Royal Giant": 78, "Royal Hogs": 79, "Royal Recruits": 80, "Skeleton Army": 81, "Skeleton Barrel": 82, "Skeleton Dragons": 83, "Skeleton King": 84, "Skeletons": 85, "Sparky": 86, "Spear Goblins": 87, "Suspicious Bush": 88, "Three Musketeers": 89, "Valkyrie": 90, "Wall Breakers": 91, "Witch": 92, "Wizard": 93, "Zappies": 94, "Bomb Tower": 95, "Cannon": 96, "Inferno Tower": 98, "Mortar": 99, "Tesla": 100, "X-Bow": 101, "Barbarian Hut": 102, "Elixir Collector": 103, "Furnace": 104, "Goblin Cage": 105, "Goblin Drill": 106, "Goblin Hut": 107, "Phoenix Egg": 108, "Tombstone": 109, "Arrows": 110, "Barbarian Barrel": 111, "Earthquake": 112, "Fireball": 113, "Freeze": 114, "Giant Snowball": 115, "Lightning": 117, "Poison": 118, "Rage": 119, "Rocket": 120, "Royal Delivery": 121, "The Log": 122, "Tornado": 123, "Zap": 125, "Goblin Barrel": 132, "Graveyard": 139 } # Generate card_images with normalized filenames base_url = "https://raw.githubusercontent.com/RoyaleAPI/cr-api-assets/master/cards/" card_images = { card_name: f"{base_url}{card_name.lower().replace(' ', '-').replace('/', '-').replace('.', '')}.png" for card_name in card_numbers.keys() } # Validate URLs valid_card_images = {} for card, url in card_images.items(): response = requests.head(url) if response.status_code == 200: valid_card_images[card] = url # Define model-related functions MODEL_PATH = "model.json" def load_model(model_path): """Load the saved XGBoost model.""" model = Booster() model.load_model(model_path) return model def deck_to_ids(deck, mapping): """Convert card names to IDs based on the mapping.""" return [mapping.get(card, 0) - 1 for card in deck] def preprocess_deck(deck): """Prepare the selected deck for the model.""" deck_ids = deck_to_ids(deck, card_numbers) num_choices = len(card_numbers) one_hot = np.zeros(num_choices, dtype=int) one_hot[np.array(deck_ids)] = 1 features = np.concatenate(([0, 0], one_hot)) return pd.DataFrame([features]) def predict_outcome(opponent_deck): """Make a prediction based on the opponent's deck.""" deck_data = preprocess_deck(opponent_deck) dmatrix = DMatrix(deck_data) prediction = model.predict(dmatrix) return f"Probability of Winning: {prediction[0] * 100:.2f}%" # Load the model model = load_model(MODEL_PATH) # Create Gradio Interface with gr.Blocks(css=""" .card-container img { width: 80px !important; height: 80px !important; object-fit: contain; margin: 5px auto; } .card-container { text-align: center; padding: 5px; border: 1px solid #ddd; border-radius: 8px; margin: 5px; } .checkbox-container { margin-top: 5px; } """) as interface: gr.Markdown("## Clash Royale Prediction") gr.Markdown("Select 8 cards from the opponent's deck to predict the probability of winning!") # State for tracking selected cards selected_cards_display = gr.Markdown("Selected cards: 0/8") def update_selection(*checkbox_values): selected_count = sum(checkbox_values) return f"Selected cards: {selected_count}/8" # Create card grid using rows and columns cards_per_row = 8 cards_list = list(valid_card_images.items()) all_checkboxes = [] for i in range(0, len(cards_list), cards_per_row): with gr.Row(): for card, url in cards_list[i:i + cards_per_row]: with gr.Column(elem_classes="card-container"): gr.Image(value=url, show_label=False) checkbox = gr.Checkbox(label=card, elem_classes="checkbox-container") all_checkboxes.append(checkbox) with gr.Row(): result = gr.Textbox(label="Prediction Result:", interactive=False) clear_btn = gr.Button("Clear Selection") predict_btn = gr.Button("Make Prediction", variant="primary") def clear_selection(): return [False] * len(all_checkboxes) + ["Selected cards: 0/8", ""] clear_btn.click( clear_selection, outputs=all_checkboxes + [selected_cards_display, result] ) def validate_and_predict(*checkbox_values): selected_cards = [ card for card, checked in zip(valid_card_images.keys(), checkbox_values) if checked ] if len(selected_cards) != 8: return f"Error: Please select exactly 8 cards. You selected {len(selected_cards)}." return predict_outcome(selected_cards) predict_btn.click( validate_and_predict, inputs=all_checkboxes, outputs=result ) # Update the card count display for checkbox in all_checkboxes: checkbox.change( update_selection, inputs=all_checkboxes, outputs=selected_cards_display ) interface.launch()