Game_Guardian / app.py
petertulip86's picture
Update app.py
c77b5ff verified
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 # 顯示啟動訊息
)