Spaces:
Build error
Build error
| import gradio as gr | |
| import random | |
| import copy | |
| from typing import List, Tuple, Dict, Any | |
| # 角色屬性基類 | |
| class 角色屬性: | |
| def __init__(self, 職業, 陣營, 血量, 魔力值, 消耗魔量, 一般攻擊, 大絕招, 防禦, 角色等級): | |
| self.__Profession = 職業 | |
| self.__campaign = 陣營 | |
| self.__hp = 血量 | |
| self.__max_hp = 血量 # 保存最大血量 | |
| self.__mp = 魔力值 | |
| self.__max_mp = 魔力值 # 保存最大魔力 | |
| self.__expend_mp = 消耗魔量 | |
| self.__normal_attack = 一般攻擊 | |
| self.__ultimate_attack = 大絕招 | |
| self.__defense = 防禦 | |
| self.__level = 角色等級 | |
| def getProfession(self): | |
| return self.__Profession | |
| def getCampaign(self): | |
| return self.__campaign | |
| def getHp(self): | |
| return self.__hp | |
| def getMaxHp(self): | |
| return self.__max_hp | |
| def setHp(self, hp): | |
| self.__hp = max(0, hp) | |
| def getMp(self): | |
| return self.__mp | |
| def getMaxMp(self): | |
| return self.__max_mp | |
| def setMp(self, mp): | |
| self.__mp = max(0, mp) | |
| def getNormalAttack(self): | |
| return self.__normal_attack | |
| def getUltimateAttack(self): | |
| return self.__ultimate_attack | |
| def getDefense(self): | |
| return self.__defense | |
| def getExpendMp(self): | |
| return self.__expend_mp | |
| def reset_stats(self): | |
| """重置角色狀態""" | |
| self.__hp = self.__max_hp | |
| self.__mp = self.__max_mp | |
| # 角色類別 | |
| class 海靈術士(角色屬性): | |
| def __init__(self, *args): | |
| super().__init__(*args) | |
| def normal_attack(self): | |
| return self.getNormalAttack(), "使出「水刃擊」!" | |
| def ultimate_attack(self): | |
| return self.getUltimateAttack(), "使出「海嘯術」!" | |
| class 烈焰武士(角色屬性): | |
| def __init__(self, *args): | |
| super().__init__(*args) | |
| def normal_attack(self): | |
| return self.getNormalAttack(), "使出「火焰斬」!" | |
| def ultimate_attack(self): | |
| return self.getUltimateAttack(), "使出「烈焰風暴」!" | |
| class 鋼鐵守衛(角色屬性): | |
| def __init__(self, *args): | |
| super().__init__(*args) | |
| def normal_attack(self): | |
| return self.getNormalAttack(), "使出「重錘打擊」!" | |
| def ultimate_attack(self): | |
| return self.getUltimateAttack(), "使出「鋼鐵壁壘」!" | |
| class 森林牧者(角色屬性): | |
| def __init__(self, *args): | |
| super().__init__(*args) | |
| def normal_attack(self): | |
| return self.getNormalAttack(), "使出「藤蔓鞭擊」!" | |
| def ultimate_attack(self): | |
| return self.getUltimateAttack(), "使出「自然恩賜」!" | |
| class 大地咒師(角色屬性): | |
| def __init__(self, *args): | |
| super().__init__(*args) | |
| def normal_attack(self): | |
| return self.getNormalAttack(), "使出「岩石投擲」!" | |
| def ultimate_attack(self): | |
| return self.getUltimateAttack(), "使出「大地震擊」!" | |
| # 屬性相剋設定 | |
| campaign_advantage = { | |
| "水": "火", # 水剋火 | |
| "火": "木", # 火剋木 | |
| "木": "土", # 木剋土 | |
| "土": "金", # 土剋金 | |
| "金": "水" # 金剋水 | |
| } | |
| def 對戰時屬性相剋比較(攻擊者, 防守者, 攻擊力): | |
| """計算屬性相剋後的攻擊力""" | |
| 攻擊者陣營 = 攻擊者.getCampaign() | |
| 防守者陣營 = 防守者.getCampaign() | |
| if campaign_advantage[攻擊者陣營] == 防守者陣營: | |
| return 攻擊力 * 1.2, f"{攻擊者.getProfession()} 對 {防守者.getProfession()} 有屬性克制!攻擊力提升 20%!" | |
| elif campaign_advantage[防守者陣營] == 攻擊者陣營: | |
| return 攻擊力 * 0.8, f"{攻擊者.getProfession()} 被 {防守者.getProfession()} 屬性克制!攻擊力降低 20%!" | |
| else: | |
| return 攻擊力, "" | |
| # 遊戲狀態管理 | |
| class GameState: | |
| def __init__(self): | |
| self.reset_game() | |
| def reset_game(self): | |
| # 初始化所有角色 | |
| self.all_characters = { | |
| "海靈術士": 海靈術士("海靈術士", "水", 3500, 5000, 1500, 200, 2000, 0.20, 0), | |
| "烈焰武士": 烈焰武士("烈焰武士", "火", 4000, 3000, 1200, 500, 3000, 0.10, 0), | |
| "鋼鐵守衛": 鋼鐵守衛("鋼鐵守衛", "金", 5000, 2000, 800, 350, 1800, 0.50, 0), | |
| "森林牧者": 森林牧者("森林牧者", "木", 3000, 4500, 1000, 250, 1500, 0.15, 0), | |
| "大地咒師": 大地咒師("大地咒師", "土", 4500, 3500, 1000, 300, 2500, 0.30, 0) | |
| } | |
| self.player_team = [] | |
| self.computer_team = [] | |
| self.current_player_character = None | |
| self.current_computer_character = None | |
| self.battle_log = [] | |
| self.game_phase = "選擇角色" # 選擇角色, 選擇出戰角色, 戰鬥中, 遊戲結束 | |
| self.selected_count = 0 | |
| # 全域遊戲狀態 | |
| game_state = GameState() | |
| def get_character_info(character): | |
| """獲取角色資訊字串""" | |
| return f""" | |
| 職業: {character.getProfession()} | |
| 陣營: {character.getCampaign()} | |
| HP: {character.getHp()}/{character.getMaxHp()} | |
| MP: {character.getMp()}/{character.getMaxMp()} | |
| 普攻: {character.getNormalAttack()} | |
| 絕招: {character.getUltimateAttack()} | |
| 防禦: {character.getDefense()} | |
| """ | |
| def get_team_status(): | |
| """獲取隊伍狀態""" | |
| player_info = "玩家隊伍:\n" | |
| for i, char in enumerate(game_state.player_team, 1): | |
| status = "💀" if char.getHp() <= 0 else "⚔️" if char == game_state.current_player_character else "🛡️" | |
| player_info += f"{i}. {status} {char.getProfession()} (HP: {char.getHp()}/{char.getMaxHp()}, MP: {char.getMp()}/{char.getMaxMp()})\n" | |
| computer_info = "電腦隊伍:\n" | |
| for i, char in enumerate(game_state.computer_team, 1): | |
| status = "💀" if char.getHp() <= 0 else "⚔️" if char == game_state.current_computer_character else "🛡️" | |
| computer_info += f"{i}. {status} {char.getProfession()} (HP: {char.getHp()}/{char.getMaxHp()}, MP: {char.getMp()}/{char.getMaxMp()})\n" | |
| return player_info + "\n" + computer_info | |
| def select_character(character_name): | |
| """選擇角色""" | |
| if game_state.game_phase != "選擇角色": | |
| return "現在不是選擇角色的時間!", get_team_status(), "" | |
| if game_state.selected_count >= 3: | |
| return "已經選擇了3個角色!", get_team_status(), "" | |
| if any(char.getProfession() == character_name for char in game_state.player_team): | |
| return f"已經選擇過{character_name}了!", get_team_status(), "" | |
| # 複製角色避免共用狀態 | |
| selected_char = copy.deepcopy(game_state.all_characters[character_name]) | |
| game_state.player_team.append(selected_char) | |
| game_state.selected_count += 1 | |
| message = f"選擇了{character_name}!({game_state.selected_count}/3)" | |
| # 如果選擇了3個角色,自動進入下一階段 | |
| if game_state.selected_count == 3: | |
| # 電腦隨機選擇3個角色 | |
| computer_choices = random.sample(list(game_state.all_characters.keys()), 3) | |
| game_state.computer_team = [copy.deepcopy(game_state.all_characters[name]) for name in computer_choices] | |
| message += f"\n\n電腦選擇了: {', '.join(computer_choices)}" | |
| message += "\n\n請選擇出戰角色!" | |
| game_state.game_phase = "選擇出戰角色" | |
| return message, get_team_status(), "選擇出戰角色" | |
| return message, get_team_status(), "" | |
| def select_battle_character(player_choice): | |
| """選擇出戰角色""" | |
| if game_state.game_phase != "選擇出戰角色": | |
| return "現在不是選擇出戰角色的時間!", get_team_status(), "", gr.update(), gr.update() | |
| try: | |
| choice_index = int(player_choice) - 1 | |
| if choice_index < 0 or choice_index >= len(game_state.player_team): | |
| return "無效的選擇!", get_team_status(), "", gr.update(), gr.update() | |
| selected_char = game_state.player_team[choice_index] | |
| if selected_char.getHp() <= 0: | |
| return "該角色已經陣亡!", get_team_status(), "", gr.update(), gr.update() | |
| game_state.current_player_character = selected_char | |
| # 電腦隨機選擇一個存活的角色 | |
| alive_computer_chars = [char for char in game_state.computer_team if char.getHp() > 0] | |
| if alive_computer_chars: | |
| game_state.current_computer_character = random.choice(alive_computer_chars) | |
| game_state.game_phase = "戰鬥中" | |
| battle_message = f"玩家派出 {game_state.current_player_character.getProfession()}!\n" | |
| battle_message += f"電腦派出 {game_state.current_computer_character.getProfession()}!\n" | |
| battle_message += "戰鬥開始!請選擇攻擊方式:" | |
| return battle_message, get_team_status(), "", gr.update(visible=True), gr.update(visible=True) | |
| except ValueError: | |
| return "請輸入有效的數字!", get_team_status(), "", gr.update(), gr.update() | |
| def battle_action(action): | |
| """戰鬥行動""" | |
| if game_state.game_phase != "戰鬥中": | |
| return "現在不是戰鬥時間!", get_team_status(), gr.update(), gr.update() | |
| if not game_state.current_player_character or not game_state.current_computer_character: | |
| return "戰鬥角色未設定!", get_team_status(), gr.update(), gr.update() | |
| battle_log = [] | |
| # 玩家攻擊 | |
| player_char = game_state.current_player_character | |
| computer_char = game_state.current_computer_character | |
| # 玩家選擇攻擊方式 | |
| if action == "普通攻擊": | |
| attack_power, attack_desc = player_char.normal_attack() | |
| battle_log.append(f"玩家{player_char.getProfession()} {attack_desc}") | |
| else: # 大絕招 | |
| if player_char.getMp() >= player_char.getExpendMp(): | |
| attack_power, attack_desc = player_char.ultimate_attack() | |
| player_char.setMp(player_char.getMp() - player_char.getExpendMp()) | |
| battle_log.append(f"玩家{player_char.getProfession()} {attack_desc} (消耗MP: {player_char.getExpendMp()})") | |
| else: | |
| attack_power, attack_desc = player_char.normal_attack() | |
| battle_log.append(f"玩家{player_char.getProfession()} MP不足,改用普通攻擊!{attack_desc}") | |
| # 計算屬性相剋 | |
| final_attack, element_msg = 對戰時屬性相剋比較(player_char, computer_char, attack_power) | |
| if element_msg: | |
| battle_log.append(element_msg) | |
| # 計算傷害 | |
| defense_value = computer_char.getDefense() * attack_power | |
| final_damage = max(0, final_attack - defense_value) | |
| computer_char.setHp(computer_char.getHp() - final_damage) | |
| battle_log.append(f"造成 {final_attack:.0f} 傷害,被防禦擋下 {defense_value:.0f},實際傷害 {final_damage:.0f}") | |
| battle_log.append(f"電腦{computer_char.getProfession()} 剩餘HP: {computer_char.getHp()}") | |
| # 檢查電腦角色是否陣亡 | |
| if computer_char.getHp() <= 0: | |
| battle_log.append(f"電腦{computer_char.getProfession()} 陣亡!") | |
| game_state.current_computer_character = None | |
| # 檢查電腦是否還有存活角色 | |
| alive_computer_chars = [char for char in game_state.computer_team if char.getHp() > 0] | |
| if not alive_computer_chars: | |
| battle_log.append("🎉 恭喜!玩家獲得勝利! 🎉") | |
| game_state.game_phase = "遊戲結束" | |
| return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False) | |
| else: | |
| battle_log.append("請選擇下一個出戰角色!") | |
| game_state.game_phase = "選擇出戰角色" | |
| return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False) | |
| # 電腦反擊 | |
| battle_log.append(f"\n電腦{computer_char.getProfession()} 反擊!") | |
| # 電腦隨機選擇攻擊方式 | |
| computer_action = random.choice(["普通攻擊", "大絕招"]) | |
| if computer_action == "普通攻擊": | |
| attack_power, attack_desc = computer_char.normal_attack() | |
| battle_log.append(f"電腦{computer_char.getProfession()} {attack_desc}") | |
| else: | |
| if computer_char.getMp() >= computer_char.getExpendMp(): | |
| attack_power, attack_desc = computer_char.ultimate_attack() | |
| computer_char.setMp(computer_char.getMp() - computer_char.getExpendMp()) | |
| battle_log.append(f"電腦{computer_char.getProfession()} {attack_desc} (消耗MP: {computer_char.getExpendMp()})") | |
| else: | |
| attack_power, attack_desc = computer_char.normal_attack() | |
| battle_log.append(f"電腦{computer_char.getProfession()} MP不足,改用普通攻擊!{attack_desc}") | |
| # 計算電腦攻擊的屬性相剋 | |
| final_attack, element_msg = 對戰時屬性相剋比較(computer_char, player_char, attack_power) | |
| if element_msg: | |
| battle_log.append(element_msg) | |
| # 計算對玩家的傷害 | |
| defense_value = player_char.getDefense() * attack_power | |
| final_damage = max(0, final_attack - defense_value) | |
| player_char.setHp(player_char.getHp() - final_damage) | |
| battle_log.append(f"造成 {final_attack:.0f} 傷害,被防禦擋下 {defense_value:.0f},實際傷害 {final_damage:.0f}") | |
| battle_log.append(f"玩家{player_char.getProfession()} 剩餘HP: {player_char.getHp()}") | |
| # 檢查玩家角色是否陣亡 | |
| if player_char.getHp() <= 0: | |
| battle_log.append(f"玩家{player_char.getProfession()} 陣亡!") | |
| game_state.current_player_character = None | |
| # 檢查玩家是否還有存活角色 | |
| alive_player_chars = [char for char in game_state.player_team if char.getHp() > 0] | |
| if not alive_player_chars: | |
| battle_log.append("💀 遊戲結束!電腦獲得勝利! 💀") | |
| game_state.game_phase = "遊戲結束" | |
| return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False) | |
| else: | |
| battle_log.append("請選擇下一個出戰角色!") | |
| game_state.game_phase = "選擇出戰角色" | |
| return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False) | |
| battle_log.append("\n請選擇下一個行動:") | |
| return "\n".join(battle_log), get_team_status(), gr.update(), gr.update() | |
| def restart_game(): | |
| """重新開始遊戲""" | |
| game_state.reset_game() | |
| return "遊戲重新開始!請選擇3個角色組成你的隊伍:", "", "選擇角色", gr.update(visible=False), gr.update(visible=False) | |
| # 創建Gradio界面 | |
| def create_interface(): | |
| with gr.Blocks(title="決鬥生死戰", theme=gr.themes.Soft()) as demo: | |
| gr.HTML(""" | |
| <div style="text-align: center; padding: 20px;"> | |
| <h1 style="color: #2196F3; font-size: 3em; margin-bottom: 10px;">⚔️ 決鬥生死戰 ⚔️</h1> | |
| <p style="font-size: 1.2em; color: #666;">選擇你的戰士,擊敗入侵的敵軍!</p> | |
| <p style="color: #888;">💧水剋🔥火 | 🔥火剋🌳木 | 🌳木剋🌍土 | 🌍土剋⚡金 | ⚡金剋💧水</p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| battle_output = gr.Textbox( | |
| label="戰鬥訊息", | |
| value="歡迎來到決鬥生死戰!請選擇3個角色組成你的隊伍:", | |
| lines=15, | |
| interactive=False | |
| ) | |
| with gr.Row(): | |
| normal_attack_btn = gr.Button("🗡️ 普通攻擊", visible=False, variant="secondary") | |
| ultimate_attack_btn = gr.Button("💥 大絕招", visible=False, variant="primary") | |
| battle_choice = gr.Textbox( | |
| label="選擇出戰角色 (輸入數字 1-3)", | |
| placeholder="輸入數字選擇角色...", | |
| visible=False | |
| ) | |
| with gr.Row(): | |
| submit_choice = gr.Button("確認選擇", visible=False) | |
| restart_btn = gr.Button("🔄 重新開始", variant="secondary") | |
| with gr.Column(scale=1): | |
| team_status = gr.Textbox( | |
| label="隊伍狀態", | |
| lines=12, | |
| interactive=False | |
| ) | |
| gr.HTML(""" | |
| <div style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; margin: 10px 0;"> | |
| <h3>🏆 角色介紹</h3> | |
| <p><strong>💧海靈術士</strong> - 高魔力水系法師</p> | |
| <p><strong>🔥烈焰武士</strong> - 高攻擊火系戰士</p> | |
| <p><strong>⚡鋼鐵守衛</strong> - 高血量金系坦克</p> | |
| <p><strong>🌳森林牧者</strong> - 平衡型木系治療師</p> | |
| <p><strong>🌍大地咒師</strong> - 全能型土系法師</p> | |
| </div> | |
| """) | |
| # 角色選擇按鈕 | |
| with gr.Row(): | |
| char_buttons = [] | |
| for char_name in ["海靈術士", "烈焰武士", "鋼鐵守衛", "森林牧者", "大地咒師"]: | |
| btn = gr.Button(f"選擇 {char_name}", variant="primary") | |
| char_buttons.append(btn) | |
| # 隱藏狀態 | |
| phase_state = gr.State("選擇角色") | |
| # 事件處理 | |
| for i, btn in enumerate(char_buttons): | |
| char_name = ["海靈術士", "烈焰武士", "鋼鐵守衛", "森林牧者", "大地咒師"][i] | |
| btn.click( | |
| select_character, | |
| inputs=[gr.State(char_name)], | |
| outputs=[battle_output, team_status, phase_state] | |
| ).then( | |
| lambda phase: (gr.update(visible=phase=="選擇出戰角色"), | |
| gr.update(visible=phase=="選擇出戰角色")), | |
| inputs=[phase_state], | |
| outputs=[battle_choice, submit_choice] | |
| ) | |
| submit_choice.click( | |
| select_battle_character, | |
| inputs=[battle_choice], | |
| outputs=[battle_output, team_status, phase_state, normal_attack_btn, ultimate_attack_btn] | |
| ).then( | |
| lambda: gr.update(value=""), | |
| outputs=[battle_choice] | |
| ) | |
| normal_attack_btn.click( | |
| battle_action, | |
| inputs=[gr.State("普通攻擊")], | |
| outputs=[battle_output, team_status, normal_attack_btn, ultimate_attack_btn] | |
| ) | |
| ultimate_attack_btn.click( | |
| battle_action, | |
| inputs=[gr.State("大絕招")], | |
| outputs=[battle_output, team_status, normal_attack_btn, ultimate_attack_btn] | |
| ) | |
| restart_btn.click( | |
| restart_game, | |
| outputs=[battle_output, team_status, phase_state, normal_attack_btn, ultimate_attack_btn] | |
| ).then( | |
| lambda: (gr.update(visible=False), gr.update(visible=False)), | |
| outputs=[battle_choice, submit_choice] | |
| ) | |
| # 當階段改變時更新界面可見性 | |
| phase_state.change( | |
| lambda phase: ( | |
| gr.update(visible=phase=="選擇出戰角色"), | |
| gr.update(visible=phase=="選擇出戰角色") | |
| ), | |
| inputs=[phase_state], | |
| outputs=[battle_choice, submit_choice] | |
| ) | |
| return demo | |
| # 啟動應用 | |
| if __name__ == "__main__": | |
| demo = create_interface() | |
| # 針對 Hugging Face Spaces 優化的設定 | |
| demo.launch( | |
| share=False, # Hugging Face 會自動提供公開網址 | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| show_error=True, # 顯示錯誤訊息便於除錯 | |
| quiet=False # 顯示啟動訊息 | |
| ) |