Luotuo-Fighter / app.py
silk-road's picture
Initial Commit
aab5970
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
AIMessage,
HumanMessage,
SystemMessage
)
smart_system_prompt = """You are ChatGPT, a large language model trained by OpenAI.
Knowledge cutoff: 2021-09
Current date: 2023-03-15"""
ma_foo_answer = """李鲁鲁,山东逍遥派的游戏角色,擅长人工智能傀儡术,以下是为你设计的两套武功和一套身法(招架)以及每套的四个招式名字:
武功一:电脑幻影拳法
这套武功利用人工智能傀儡术,创建虚拟幻影来欺骗对手,令其难以捉摸。
电脑虚像拳 - 创造多个虚像,使对手难以分辨真伪。
数据掌控击 - 利用数据流动的速度来快速攻击对手。
模拟迷雾拳 - 释放模糊幻影,混淆对手的视线。
程序化拳法 - 不断变化的攻击组合,让对手无法预测。
武功二:智能预测刀法
这套武功充分利用你的智能傀儡术,通过对对手的行为进行分析和预测,达到精准打击的效果。
预测之刃 - 准确地预测对手的下一步行动,发动致命一击。
数据漩涡斩 - 利用对手的动向,将其引导至陷阱,然后发动猛烈攻击。
算法刀舞 - 刀法如算法一般精准,快速地切入对手的防线。
智能反击剑 - 反击对手的招式,使其付出惨痛的代价。
身法(招架):数码幻影步
这套身法让你能够迅速回避对手的攻击,同时保持对局掌握主动权。
数码闪避 - 迅速移动,如数码般闪烁,躲避对手的攻击。
傀儡幻影步 - 创造虚假的身影,使对手无法锁定你的位置。
数据跃迁 - 在一瞬间跳跃到对手的盲点,出其不意。
逍遥之法 - 以逍遥派的身法融入自然,消除敌人的攻击威胁。
希望这些武功和身法(招架)可以为你的李鲁鲁角色增添一些游戏中的精彩元素!
"""
attr_foo_answer = """{"name": "李鲁鲁","STR":5,"SPD":5,"VIT":5,"description": "山东逍遥派的李鲁鲁,擅长人工智能傀儡术,善于提前预测对方的行动进行战斗","weapon": "无","major_martial_art": "人工智能傀儡术","major_movements": ["预测之眼", "智慧编织", "虚实交融", "机巧纠缠"],"secondary_martial_art": "人工智能傀儡术","secondary_movements": ["预测之眼", "智慧编织", "虚实交融", "机巧纠缠"],"footwork_and_body_method": "山水行云"}"""
attr_lilulu = """{
"name": "李鲁鲁",
"description": "山东逍遥派的游戏角色,擅长人工智能傀儡术",
"weapon": "智能刀剑",
"major_martial_art": "电脑幻影拳法",
"major_movements": ["电脑虚像拳", "数据掌控击", "模拟迷雾拳", "程序化拳法"],
"major_damage": 75,
"secondary_martial_art": "智能预测刀法",
"secondary_movements": ["预测之刃", "数据漩涡斩", "算法刀舞", "智能反击剑"],
"secondary_damage": 85,
"footwork_and_body_method": "数码幻影步",
"STR": 6,
"VIT": 5,
"SPD": 18
}
"""
attr_lengziang = """{
"name": "冷子昂",
"description": "擅长冰系技能",
"weapon": "冰刃",
"major_martial_art": "冰霜寒刃功",
"major_movements": ["冰刃斩破", "冰风流转", "冰龙穿云", "冰心彻骨"],
"major_damage": 80,
"secondary_martial_art": "冰封无影步",
"secondary_movements": ["冰封幻影", "无影冻结", "冰刃闪耀", "冰封禅定"],
"secondary_damage": 70,
"footwork_and_body_method": "冰心护身法",
"STR": 8,
"VIT": 6,
"SPD": 15
}
"""
json_str_huangrong = """{
"name": "黄蓉",
"description": "丐帮帮主黄蓉,擅长打狗棒法和落英神剑掌,身法为逍遥游。",
"weapon": "棒、剑",
"major_martial_art": "打狗棒法",
"major_movements": ["狗啸山林", "棒影重重", "狗尾续貂", "狗急跳墙"],
"major_damage": 90,
"secondary_martial_art": "落英神剑掌",
"secondary_movements": ["落英如雨", "神剑破空", "飞花流水", "剑指苍穹"],
"secondary_damage": 80,
"footwork_and_body_method": "逍遥游",
"STR": 15,
"VIT": 10,
"SPD": 18
}"""
skill_foo_desc = """
{"招式":"预测之眼","description":"[player]凝神聚气,双眼闪烁着深邃的智慧,一招“预测之眼”发动,仿佛未来已经显现在眼前,[player]能够精准预测[target]的下一步动作。","missing":"[player]凝神聚气,双眼闪烁着深邃的智慧,一招“预测之眼”发动,仿佛未来已经显现在眼前,[target]的一举一动变得难以捉摸。"}
{"招式":"智慧编织","description":"[player]手中的线缠绕着[target],编织成一个巧妙的陷阱,一招“智慧编织”完成,如同织女编织云锦,[target]的行动受到了限制。","missing":"[player]手中的线缠绕着,编织成一个巧妙的陷阱,一招“智慧编织”完成,[target]似乎被某种力量限制住了行动。"}
{"招式":"虚实交融","description":"[player]身形忽明忽暗,虚实难辨,一招“虚实交融”使出,仿佛有数个[player]一同出招,[target]不知所措。","missing":"[player]身形忽明忽暗,虚实难辨,一招“虚实交融”使出,[target]眼前忽然出现了一片幻影,令其感到迷茫。"}
{"招式":"机巧纠缠","description":"[player]运用机巧傀儡术,手中的人工智能傀儡纷纷扑向[target],一招“机巧纠缠”展开,[target]被困在了傀儡的包围中。","missing":"[player]运用机巧傀儡术,手中的人工智能傀儡纷纷扑向,一招“机巧纠缠”展开,[target]四面八方都是傀儡的身影,形势险恶。"}
{"招式":"预测之眼","description":"[player]凝神聚气,双眼闪烁着深邃的智慧,一招“预测之眼”发动,仿佛未来已经显现在眼前,[player]能够精准预测[target]的下一步动作。","missing":"[player]凝神聚气,双眼闪烁着深邃的智慧,一招“预测之眼”发动,仿佛未来已经显现在眼前,[target]的一举一动变得难以捉摸。"}
{"招式":"智慧编织","description":"[player]手中的线缠绕着[target],编织成一个巧妙的陷阱,一招“智慧编织”完成,如同织女编织云锦,[target]的行动受到了限制。","missing":"[player]手中的线缠绕着,编织成一个巧妙的陷阱,一招“智慧编织”完成,[target]似乎被某种力量限制住了行动。"}
{"招式":"虚实交融","description":"[player]身形忽明忽暗,虚实难辨,一招“虚实交融”使出,仿佛有数个[player]一同出招,[target]不知所措。","missing":"[player]身形忽明忽暗,虚实难辨,一招“虚实交融”使出,[target]眼前忽然出现了一片幻影,令其感到迷茫。"}
{"招式":"机巧纠缠","description":"[player]运用机巧傀儡术,手中的人工智能傀儡纷纷扑向[target],一招“机巧纠缠”展开,[target]被困在了傀儡的包围中。","missing":"[player]运用机巧傀儡术,手中的人工智能傀儡纷纷扑向,一招“机巧纠缠”展开,[target]四面八方都是傀儡的身影,形势险恶。"}
"""
skill_lilulu = """
{"招式":"电脑虚像拳","description":"[player]运用电脑幻影拳法,双拳虚虚实实,制造出多个虚像,令[target]难以分辨真伪,招招不离其身。","missing":"[player]运用电脑幻影拳法,双拳虚虚实实,制造出多个虚像,令[target]难以分辨真伪。"}
{"招式":"数据掌控击","description":"[player]施展数据掌控击,利用数据流动的速度,犹如电光闪烁,快速攻击[target],剑招迅猛,如电一般。","missing":"[player]施展数据掌控击,利用数据流动的速度,犹如电光闪烁,快速攻击[target],剑招迅猛。"}
{"招式":"模拟迷雾拳","description":"[player]施展模拟迷雾拳,释放出模糊幻影,混淆[target]的视线,令其难以辨别攻击方向。","missing":"[player]施展模拟迷雾拳,释放出模糊幻影,混淆[target]的视线,令其难以辨别攻击方向。"}
{"招式":"程序化拳法","description":"[player]运用程序化拳法,以不断变化的攻击组合,让[target]难以预测招式,时而快时而慢,攻击变幻莫测。","missing":"[player]运用程序化拳法,以不断变化的攻击组合,让[target]难以预测招式,攻击变幻莫测。"}
{"招式":"预测之刃","description":"[player]使出预测之刃,准确地预测[target]的下一步行动,犹如镜中水月,剑锋准确地击中[target]的要害。","missing":"[player]使出预测之刃,准确地预测[target]的下一步行动,犹如镜中水月,剑锋迅速挥出。"}
{"招式":"数据漩涡斩","description":"[player]施展数据漩涡斩,利用[target]的动向,将其引导至剑刃之下,然后发动猛烈攻击,犹如漩涡将[target]吸入其中。","missing":"[player]施展数据漩涡斩,利用[target]的动向,将其引导至剑刃之下,然后发动猛烈攻击,剑招异常犀利。"}
{"招式":"算法刀舞","description":"[player]挥舞着长剑,使出算法刀舞,刀法如算法一般精准,快速地切入[target]的防线,剑锋连续不断地斩向[target]。","missing":"[player]挥舞着长剑,使出算法刀舞,刀法如算法一般精准,快速地切入[target]的防线,剑招变化多端。"}
{"招式":"智能反击剑","description":"[player]运用智能反击剑,反击[target]的招式,使其付出惨痛的代价,一瞬间的犹豫就足以让[target]受伤。","missing":"[player]运用智能反击剑,反击[target]的招式,使其陷入困境,一瞬间的犹豫足以让[target]付出代价。"}
"""
skill_lengziang = """
{"招式":"冰刃斩破","description":"[player]聚集冰之力量于剑上,使出冰刃斩破,剑锋犹如寒冰之刃斩向[target]的要害部位,寒气逼人,剑光炽烈。","missing":"[player]聚集冰之力量于剑上,使出冰刃斩破,剑锋犹如寒冰之刃,闪电般挥出。"}
{"招式":"冰风流转","description":"[player]身体如风一般旋转,同时释放出冰风刃,打击周围的敌人,冷飕飕的寒风环绕,[target]身不由己地被吹向一旁。","missing":"[player]身体如风一般旋转,同时释放出冰风刃,冷飕飕的寒风环绕,风刃在[target]周围飞舞。"}
{"招式":"冰龙穿云","description":"[player]召唤出一条冰龙,冰龙穿越云层直扑[target],寒冰之力凝聚在龙爪之上,将[target]冻结一切,一切都难以抵挡。","missing":"[player]召唤出一条冰龙,冰龙穿越云层,龙爪直扑[target],寒冰之力凝聚在龙爪之上,寒意森然。"}
{"招式":"冰心彻骨","description":"[player]凝聚冰之力于手,一触即可让[target]骨骼寒冷,动弹不得,彻底冰封,剑锋轻轻触及[target],寒意透骨。","missing":"[player]凝聚冰之力于手,一触即可让[target]骨骼寒冷,动弹不得,寒气透骨,剑锋轻轻触及[target]。"}
{"招式":"冰封幻影","description":"[player]运用冰封无影步,快速移动,留下一道冰封的幻影,使[target]无法辨别真伪,幻影与实体交替出现,令[target]不知所措。","missing":"[player]运用冰封无影步,快速移动,留下一道冰封的幻影,使[target]无法辨别真伪。"}
{"招式":"无影冻结","description":"[player]以极快的速度接近[target],然后冻结[target]的动作,使其陷入无法自拔的状态,如同身陷冰封之中,动弹不得。","missing":"[player]以极快的速度接近[target],然后冻结[target]的动作,使其陷入无法自拔的状态。"}
{"招式":"冰刃闪耀","description":"[player]利用冰之能量,瞬间闪现至[target]身边,发动迅猛的攻击,剑锋如星光般闪烁,将[target]包围其中。","missing":"[player]利用冰之能量,瞬间闪现至[target]身边,发动迅猛的攻击,剑锋如星光般闪烁。"}
{"招式":"冰封禅定","description":"[player]静心冥想,将身体冰封,使自己无法被[target]察觉,达到不可捉摸的状态,如同融入冰封禅定之中。","missing":"[player]静心冥想,将身体冰封,使自己无法被[target]察觉,达到不可捉摸的状态。"}
"""
skill_huangrong = """
{"招式": "狗啸山林", "description": "[player]手中长棒一挥,发出一声凄厉的狗啸,棒影如山林般扑向[target]", "missing": "[player]手中长棒一挥,发出一声凄厉的狗啸,棒影如山林般扑向[target]"}
{"招式": "棒影重重", "description": "[player]身形连转,手中长棒化作无数道棒影,重重叠叠地向[target]砸去", "missing": "[player]身形连转,手中长棒化作无数道棒影,重重叠叠地向[target]砸去"}
{"招式": "狗尾续貂", "description": "[player]手中长棒如狗尾续貂般灵动,连续变换方向,向[target]连续攻击", "missing": "[player]手中长棒如狗尾续貂般灵动,连续变换方向,向[target]连续攻击"}
{"招式": "狗急跳墙", "description": "[player]猛地一跃,手中长棒带起狂风,如狗急跳墙般向[target]砸去", "missing": "[player]猛地一跃,手中长棒带起狂风,如狗急跳墙般向[target]砸去"}
{"招式": "落英如雨", "description": "[player]身形一闪,双掌连续拍出,犹如漫天飘落的落英,掌风凌厉,瞬间覆盖[target]周身", "missing": "[player]身形一闪,双掌连续拍出,犹如漫天飘落的落英,掌风凌厉,向[target]周身袭去"}
{"招式": "神剑破空", "description": "[player]手中剑光闪烁,一剑刺出,剑气凌厉无比,犹如神剑破开虚空,直指[target]要害", "missing": "[player]手中剑光闪烁,一剑刺出,剑气凌厉无比,直向[target]要害刺去"}
{"招式": "飞花流水", "description": "[player]身形如飞,双掌连续拍击,掌风如流水般连绵不绝,将[target]团团围住", "missing": "[player]身形如飞,双掌连续拍击,掌风如流水般连绵不绝,向[target]团团包围"}
{"招式": "剑指苍穹", "description": "[player]手中长剑一指,剑光直冲云霄,气势如虹,剑指直指苍穹,威力惊人", "missing": "[player]手中长剑一指,剑光直冲云霄,气势如虹,直指苍穹"}
"""
def generate_detailed_description(name, description):
task_message = f"""我的名字叫“{name}”,假设我是一个武侠中的游戏角色,{description}
请基于我的名字和门派,先为我设计2套武功和一套身法或者招架。
再为每套武功设计4个对应招式的名字。"""
messages = [
SystemMessage(content=smart_system_prompt),
HumanMessage(content=task_message),
]
chat = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
response = chat(messages)
return response.content
def generate_attr_json(name, description, detailed_description):
task_message = f"""我正在为我的武侠游戏设计人物。给定一个人物和这个人物的背景描述(可选)
我希望你根据这个人物的背景和名字以及输入的suggestion信息,为我生成这个人的资料,以json的格式输出,包括
description: 这个角色的背景等信息
weapon: 这个人使用的武器种类,
major_martial_art: 这个人的主要武学
major_movements: 这个人主要武学的4个对应招式的名字。招式的名字需要尽可能和major_martial_art有关。movements的名字尽量不要使用重复的文字。可以使用形象的动作或者是一些动物的比喻。
major_damage: 主武学的威力,50到150之间的整数。重视速度的功法威力会低一些。重视攻击的功法威力会高一些
secondary_movements: 这个角色的次要武学
secondary_movements: 这个人次要武学的4个对应招式的名字。招式的名字需要尽可能和secondary_movements有关
secondary_damage: 次要武学的威力,50到150之间的整数。
footwork_and_body_method: 这个角色的轻功或者防御功法
STR: 角色的攻击力, 0-20之间的整数
VIT: 角色的体质, 0-20之间的整数
SPD: 角色的速度,0-20之间的整数
"""
example_input = """example_input:
name: 令狐冲
description: -
suggestion:-"""
example_output = """example_output:
{
"name": "令狐冲",
"description": "华山派的大师兄",
"weapon": "剑",
"major_martial_art": "独孤九剑",
"major_movements": ["独孤九剑破剑式","独孤九剑总诀式","独孤九剑破气式","独孤九剑破刀式"],
"major_damage": 70,
"secondary_martial_art": "华山剑法",
"secondary_movements": ["有凤来仪","飞沙走石","百丈飞瀑","青山隐隐"],
"secondary_damage":80,
"footwork_and_body_method": "华山身法",
"STR":10,
"VIT":3,
"SPD":20
}"""
input = f"""input:
name: {name},
description: {description},
suggestion: `{detailed_description}`
"""
messages = [
SystemMessage(content=smart_system_prompt),
HumanMessage(content=task_message),
HumanMessage(content=example_input),
AIMessage(content=example_output),
HumanMessage(content=input)]
chat = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1)
response = chat(messages)
return response.content
import json
def robust_parsing(input_str):
try:
return json.loads(input_str)
except json.JSONDecodeError:
start = input_str.find('{')
end = input_str.rfind('}') + 1
if start != -1 and end != -1:
try:
return json.loads(input_str[start:end])
except json.JSONDecodeError:
pass
return None
def generate_skill_detail(martial_art_name, movements):
task_message = """现在我希望你根据武侠小说中的武功和招式的名字。为招式补全合理的description和missing描述。
在description的描述中,player可以击中目标,可以有兵器撞击声响等描述。
在missing描述中,player并不一定击中目标,不可以有击落、击中、夺去这样的描述
在用[player]代表技能的使用者,用[target]代表技能的目标。结果需用合理的jsonl格式展示,下面是一个例子。"""
example_input = """example input:
martial_art_name = 华山剑法
{"招式":"百丈飞瀑"}
{"招式":"百鸟朝凤"}
{"招式":"青山隐隐"}
{"招式":"飞沙走石"}"""
example_output = """example output:
{"招式":"百丈飞瀑","description":"[player]翻身回剑,剑诀斜引,一招“百丈飞瀑”,剑锋从半空中直泻下来","missing":"[player]一咬牙,翻身回剑,剑诀斜引,一招“百丈飞瀑”,剑锋从半空中直泻下来"}
{"招式":"百鸟朝凤","description":"[player]长剑一起,使一招“百鸟朝凤”,但见剑尖乱颤,霎时间便如化为数十个剑尖,罩住[target]中盘","missing":"[player]使一招“百鸟朝凤”,但见剑尖乱颤,霎时间便如化为数十个剑尖,向[target]飞去"}
{"招式":"青山隐隐", "description":"[player]吸一口气,长剑中宫直进,剑尖不住颤动,剑到中途,忽然转而向上,乃是一招“青山隐隐”,端的是若有若无,变幻无方。","missing":"[player]吸一口气,长剑中宫直进,剑尖不住颤动,剑到中途,忽然转而向上,乃是一招“青山隐隐”,端的是若有若无,变幻无方。"}
{"招式":"飞沙走石", "description":"长剑挺起,使一招“飞沙走石”,内劲直贯剑尖,寒光点点,挡的一声,击中的[target]胸口。","missing":"长剑挺起,使一招“飞沙走石”,内劲直贯剑尖,寒光点点,直向[target]胸口刺去。"}"""
input = "input:\nmartial_art_name = " + martial_art_name + "\n"
for m in movements:
input += f"""{{"招式":"{m}"\}}\n"""
messages = [
SystemMessage(content=smart_system_prompt),
HumanMessage(content=task_message),
HumanMessage(content=example_input),
AIMessage(content=example_output),
HumanMessage(content=input)]
chat = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1)
response = chat(messages)
return response.content
def generate_skill_jsonl(json_data):
major_skills = generate_skill_detail(json_data["major_martial_art"], json_data["major_movements"])
secondary_skills = generate_skill_detail(json_data["secondary_martial_art"], json_data["secondary_movements"])
all_skills = major_skills + "\n" + secondary_skills
ans = ""
lines = all_skills.split("\n")
for line in lines:
json_data = robust_parsing(line)
if json_data is not None:
json_str = json.dumps(json_data, ensure_ascii=False)
ans += json_str + "\n"
return ans
import random
class Player:
def __init__(self, attr_json, skill_jsonl):
self.name = attr_json["name"]
self.STR = attr_json["STR"]
self.SPD = attr_json["SPD"]
self.VIT = attr_json["VIT"]
if "major_damage" in attr_json:
self.major_damage = attr_json["major_damage"]
else:
self.major_damage = 65
if "secondary_damage" in attr_json:
self.secondary_damage = attr_json["secondary_damage"]
else:
self.secondary_damage = 50
self.normalize_attr()
if "hp" in attr_json:
self.hp = attr_json["hp"]
else:
self.hp = (self.VIT + 10) * 30
if "skills" in attr_json:
self.skills = attr_json["skills"]
else:
self.skills = self.parsing_skill(skill_jsonl)
self.original_json = attr_json
def normalize_attr(self):
max_attr_value = 20
min_attr_value = 0
regular_attr_sum = 30
xs = [self.STR, self.SPD, self.VIT]
current_sum = 0
for i in range(3):
xs[i] = min(max_attr_value, xs[i])
xs[i] = max(min_attr_value, xs[i])
current_sum += xs[i]
if current_sum > regular_attr_sum:
for i in range(3):
xs[i] = round(xs[i] * regular_attr_sum / current_sum)
elif current_sum < regular_attr_sum:
for i in range(3):
xs[i] = round(xs[i] * regular_attr_sum / current_sum)
random_i = random.randint(0, 2)
xs[random_i] = xs[random_i] + regular_attr_sum - sum(xs)
self.STR = xs[0]
self.SPD = xs[1]
self.VIT = xs[2]
def get_new_json(self):
new_json = self.original_json
new_json["STR"] = self.STR
new_json["SPD"] = self.SPD
new_json["VIT"] = self.VIT
new_json["hp"] = self.hp
new_json["skills"] = self.skills
return new_json
def parsing_skill(self, skill_jsonl):
skills = []
lines = skill_jsonl.split("\n")
for line in lines:
if line.strip() == "":
continue
skill = json.loads(line)
skills.append(skill)
return skills
hit_map = {30: 131, 31: 131, 32: 131, 33: 131, 34: 131, 35: 120, 36: 112, 37: 107, 38: 103, 39: 100, 40: 97, 41: 95,
42: 93, 43: 91, 44: 89, 45: 87, 46: 86, 47: 85, 48: 83, 49: 82, 50: 81, 51: 80, 52: 79, 53: 78, 54: 77,
55: 76, 56: 75, 57: 74, 58: 73, 59: 72, 60: 71, 61: 70, 62: 69, 63: 69, 64: 68, 65: 67, 66: 66, 67: 65,
68: 65, 69: 64, 70: 63, 71: 63, 72: 62, 73: 61, 74: 61, 75: 60, 76: 59, 77: 59, 78: 58, 79: 58, 80: 57,
81: 57, 82: 56, 83: 55, 84: 55, 85: 54, 86: 54, 87: 53, 88: 53, 89: 52, 90: 52, 91: 52, 92: 51, 93: 51,
94: 50, 95: 50, 96: 49, 97: 49, 98: 49, 99: 48, 100: 48, 101: 47, 102: 47, 103: 47, 104: 46, 105: 46,
106: 46, 107: 45, 108: 45, 109: 44, 110: 44, 111: 44, 112: 43, 113: 43, 114: 43, 115: 43, 116: 42, 117: 42,
118: 42, 119: 41, 120: 41, 121: 41, 122: 40, 123: 40, 124: 40, 125: 40, 126: 39, 127: 39, 128: 39, 129: 39,
130: 38, 131: 38, 132: 38, 133: 38, 134: 37, 135: 37, 136: 37, 137: 37, 138: 36, 139: 36, 140: 36, 141: 36,
142: 36, 143: 35, 144: 35, 145: 35, 146: 35, 147: 34, 148: 34, 149: 34, 150: 34, 151: 34, 152: 33, 153: 33,
154: 33, 155: 33, 156: 33, 157: 33, 158: 32, 159: 32, 160: 32, 161: 32, 162: 32, 163: 31, 164: 31, 165: 31,
166: 31, 167: 31, 168: 31, 169: 30, 170: 30, 171: 30, 172: 30, 173: 30, 174: 30, 175: 30, 176: 29, 177: 29,
178: 29, 179: 29, 180: 29}
min_damage = 34
max_damage = 180
class GameManager:
def __init__(self, attr_json1, attr_json2, skill_jsonl1, skill_jsonl2):
self.player1 = Player(attr_json1, skill_jsonl1)
self.player2 = Player(attr_json2, skill_jsonl2)
def run(self):
speed_diff = self.player1.SPD - self.player2.SPD
if speed_diff == 0:
speed_diff = random.randint(0, 3) * 2 - 3
skill_id_1 = random.randint(0, len(self.player1.skills) - 1)
dmg_1 = 60
if skill_id_1 + skill_id_1 < len(self.player1.skills):
dmg_1 = self.player1.major_damage
else:
dmg_1 = self.player1.secondary_damage
skill_1_to_2 = self.player1.skills[skill_id_1]
skill_1_to_2["dmg"] = dmg_1
skill_id_2 = random.randint(0, len(self.player2.skills) - 1)
dmg_2 = 60
if skill_id_2 + skill_id_2 < len(self.player2.skills):
dmg_2 = self.player2.major_damage
else:
dmg_2 = self.player2.secondary_damage
skill_2_to_1 = self.player2.skills[skill_id_2]
skill_2_to_1["dmg"] = dmg_2
# skill_2_to_1 = random.choice(self.player2.skills)
# print(skill_1_to_2)
# print(skill_2_to_1)
damage_1_to_2 = self.compute_damage(skill_1_to_2, self.player1, self.player2)
damage_2_to_1 = self.compute_damage(skill_2_to_1, self.player2, self.player1)
ratio_1_to_2 = self.compute_ratio(skill_1_to_2, self.player1, self.player2)
ratio_2_to_1 = self.compute_ratio(skill_2_to_1, self.player2, self.player1)
if_hitted_1_to_2 = random.random() * 100.0 < ratio_1_to_2
if_hitted_2_to_1 = random.random() * 100.0 < ratio_2_to_1
desc_1_to_2 = skill_1_to_2["description"] if if_hitted_1_to_2 else skill_1_to_2["missing"]
desc_1_to_2 = desc_1_to_2.replace("[player]", self.player1.name).replace("[target]", self.player2.name)
if if_hitted_1_to_2:
desc_1_to_2 += f"造成了{round(damage_1_to_2)}的伤害。"
else:
desc_1_to_2 += f"{self.player2.name}躲了过去"
desc_2_to_1 = skill_2_to_1["description"] if if_hitted_2_to_1 else skill_2_to_1["missing"]
desc_2_to_1 = desc_2_to_1.replace("[player]", self.player2.name).replace("[target]", self.player1.name)
if if_hitted_2_to_1:
desc_2_to_1 += f"造成了{round(damage_2_to_1)}的伤害。"
else:
desc_2_to_1 += f"{self.player1.name}躲了过去"
self.flag = "continue"
ans_msg = []
if self.player2.hp <= 0:
ans_msg.append((None, f"{self.player2.name}战败了"))
return ans_msg, self.player1.get_new_json(), self.player2.get_new_json()
if self.player1.hp <= 0:
ans_msg.append((f"{self.player1.name}战败了", None))
return ans_msg, self.player1.get_new_json(), self.player2.get_new_json()
if speed_diff > 0:
if if_hitted_1_to_2:
self.player2.hp -= damage_1_to_2
ans_msg.append((desc_1_to_2, None))
if self.player2.hp > 0 and if_hitted_2_to_1:
self.player1.hp -= damage_2_to_1
if self.player2.hp > 0:
ans_msg.append((None, desc_2_to_1))
else:
if if_hitted_2_to_1:
self.player1.hp -= damage_2_to_1
ans_msg.append((None, desc_2_to_1))
if self.player1.hp > 0 and if_hitted_1_to_2:
self.player2.hp -= damage_1_to_2
if self.player1.hp > 0:
ans_msg.append((desc_1_to_2, None))
if self.player2.hp <= 0:
ans_msg.append((None, f"{self.player2.name}战败了"))
self.flag = "player1_win"
if self.player1.hp <= 0:
ans_msg.append((f"{self.player1.name}战败了", None))
self.flag = "player2_win"
return ans_msg, self.player1.get_new_json(), self.player2.get_new_json()
# default "damage": 60, "hit": 76,
def compute_damage(self, skill, player, target):
damage_ratio = skill["dmg"]
damage_ratio = max(min_damage, damage_ratio)
damage_ratio = min(max_damage, min_damage)
skill_damage = damage_ratio + player.STR * 2
attack_diff = player.STR - target.VIT * 0.5
damage = skill_damage * (1 + 0.95 * (-0.5 + 1.0 / (1.0 + exp(-attack_diff / 6.0))))
return damage
def compute_ratio(self, skill, player, target):
damage_ratio = skill["dmg"]
damage_ratio = round(damage_ratio)
damage_ratio = max(min_damage, damage_ratio)
damage_ratio = min(max_damage, min_damage)
skill_hit = hit_map[damage_ratio]
speed_diff = player.SPD - target.SPD
hit_factor = (-0.5 + 1.0 / (1.0 + exp(-speed_diff / 6.0))) / 2.0 * max(30.0, skill_hit)
return hit_factor + skill_hit
fighter_save_name = 'LuotuoFighter.txt'
import os
datas = []
if not os.path.exists(fighter_save_name):
data = {
"name": "李鲁鲁",
"attr_str": attr_lilulu,
"skills": skill_lilulu
}
datas.append(data)
data = {
"name": "冷子昂",
"attr_str": attr_lengziang,
"skills": skill_lengziang
}
datas.append(data)
data = {
"name": "黄蓉",
"attr_str": json_str_huangrong,
"skills": skill_huangrong
}
datas.append(data)
with open(fighter_save_name, 'w', encoding="utf-8") as f:
for data in datas:
f.write(json.dumps(data, ensure_ascii=False) + "\n")
else:
with open(fighter_save_name, 'r', encoding="utf-8") as f:
for line in f.readlines():
data = json.loads(line)
datas.append(data)
fighter_data = datas
elo_table = {}
def get_unique_from_data(data):
name = data["name"]
json_data = json.loads(data["attr_str"])
return get_unique_from_json(name, json_data)
def get_unique_from_json(name, json_data):
if "STR" in json_data and "VIT" in json_data and "SPD" in json_data and "major_damage" in json_data and "secondary_damage" in json_data:
return name + "_" + str(json_data["major_damage"]) + "_" + str(json_data["secondary_damage"])
else:
return None
unique_to_score = {}
name_to_top_unique = {}
from math import exp
elo_start = 1200
elo_K = 15
for data in fighter_data:
unique_name = get_unique_from_data(data)
score = elo_start
unique_to_score[unique_name] = elo_start
name = data["name"]
name_to_top_unique[name] = unique_name
def get_rank_str(top_n=20):
global unique_to_score
global name_to_top_unique
score_name_pair = []
for name in name_to_top_unique:
unique = name_to_top_unique[name]
score = unique_to_score[unique]
score_name_pair.append((unique, score))
top_n = min(top_n, len(name_to_top_unique))
score_name_pair.sort(key=lambda x: x[1], reverse=True)
ans = ""
for i in range(top_n):
name = score_name_pair[i][0].split("_")[0]
ans += f"第{i + 1}名" + name + " - " + str(round(score_name_pair[i][1])) + " | "
return ans
def update_elo(winner_unique, loser_unique):
print(winner_unique + " wins " + loser_unique)
update_winner = True
update_loser = True
global unique_to_score
global name_to_top_unique
if winner_unique not in unique_to_score:
unique_to_score[winner_unique] = elo_start
update_loser = False
if loser_unique not in unique_to_score:
unique_to_score[loser_unique] = elo_start
Ra = unique_to_score[winner_unique]
Rb = unique_to_score[loser_unique]
Ea = 1 / (1 + exp((Rb - Ra) / 400))
Eb = 1 / (1 + exp((Ra - Rb) / 400))
Sa = 1
Sb = 0
unique_to_score[winner_unique] = Ra + elo_K * (Sa - Ea)
unique_to_score[loser_unique] = Rb + elo_K * (Sb - Eb)
winner_name = winner_unique.split("_")[0]
if winner_name not in name_to_top_unique:
name_to_top_unique[winner_name] = winner_unique
winner_unique_on_rank = name_to_top_unique[winner_name]
if unique_to_score[winner_unique] > unique_to_score[winner_unique_on_rank]:
name_to_top_unique[winner_name] = winner_unique
def get_random_fighter():
global fighter_data
return random.choice(fighter_data)
def searching_fighter(name):
global fighter_data
for data in fighter_data:
if data["name"] == name:
return data
return get_random_fighter()
def add_fighter(attr_json_str, skill_jsonl):
attr_json = json.loads(attr_json_str)
if "skills" in attr_json:
del attr_json["skills"]
if "hp" in attr_json:
del attr_json["hp"]
new_attr_json_str = json.dumps(attr_json, ensure_ascii=False)
global fighter_data
name = attr_json["name"]
data = {
"name": name,
"attr_str": new_attr_json_str,
"skills": skill_jsonl
}
fighter_data.append(data)
print(json.dumps(data, ensure_ascii=False))
with open(fighter_save_name, 'a', encoding="utf-8") as f:
f.write(json.dumps(data, ensure_ascii=False) + "\n")
def searching_fighter_on_elo(name):
global fighter_data
global name_to_top_unique
if name in name_to_top_unique:
top_unique = name_to_top_unique[name]
for data in fighter_data:
if get_unique_from_data(data) == top_unique:
return data
return searching_fighter(name)
else:
return searching_fighter(name)
import gradio as gr
def get_attr_str_short(attr_json):
if "hp" in attr_json:
ans = "血量:" + str(attr_json["hp"]) + "\n"
else:
ans = ""
ans += "力量:" + str(attr_json["STR"]) + "\n体质:" + str(attr_json["VIT"]) + "\n速度:" + str(attr_json["SPD"])
return ans
def get_skill_str_short(attr_json):
ans = attr_json["major_martial_art"] + ":\n"
for skill in attr_json["major_movements"]:
ans += skill + "-"
ans += "\n" + attr_json["secondary_martial_art"] + ":\n"
for skill in attr_json["secondary_movements"]:
ans += skill + "-"
ans += "\n 防御:" + attr_json["footwork_and_body_method"]
return ans
def generate_ma(name, desc, display_board):
if name.strip() == "":
return "", "", "", "", display_board, "角色名不能为空,输入角色名后生成功法或者创建角色"
status = "请为角色进一步生成详细功法"
role_detail = ma_foo_answer
if name == "李鲁鲁":
json_answer = json.loads(attr_lilulu)
elif name == "冷子昂":
json_answer = json.loads(attr_lengziang)
else:
role_detail = generate_detailed_description(name, desc)
json_str = generate_attr_json(name, desc, role_detail)
json_answer = robust_parsing(json_str)
json_answer_str = json.dumps(json_answer, ensure_ascii=False)
skill_desc = get_skill_str_short(json_answer)
display_board.append(("生成人物" + json_answer["name"] + "\n" + skill_desc, None))
return role_detail, json_answer_str, get_attr_str_short(json_answer), skill_desc, display_board, status
def generate_madetail(player_attribute, display_board):
if player_attribute.strip() == "":
return "", display_board, "需要生成武功名称,再生成描述。或者直接随机召唤角色"
status = "确认两个角色都生成之后,可以进入战斗"
json_answer = json.loads(player_attribute)
generate_flag = False
ans = skill_foo_desc
if json_answer["name"] == "李鲁鲁":
ans = skill_lilulu
elif json_answer["name"] == "冷子昂":
ans = skill_lengziang
else:
ans = generate_skill_jsonl(json_answer)
generate_flag = True
display_board.append(("为" + json_answer["name"] + "生成详细的功法描述\n", None))
if generate_flag:
player = Player(json_answer, ans)
unique_name = get_unique_from_json(json_answer["name"], json_answer)
if len(player.skills) > 1 and unique_name != None:
add_fighter(player_attribute, ans)
display_board.append(("将新角色" + json_answer["name"] + "录入到数据库\n", None))
return ans, display_board, status
def continue_fight(detailed_attr_player1, detailed_attr_player2, detailed_skill_player1, detailed_skill_player2,
display_board):
if detailed_attr_player1.strip() == "" or detailed_attr_player2.strip() == "" or detailed_skill_player1.strip() == "" or detailed_skill_player2.strip() == "":
str1 = ""
if detailed_attr_player1.strip() != "":
str1 = get_attr_str_short(json.loads(detailed_attr_player1))
str2 = ""
if detailed_attr_player2.strip() != "":
str2 = get_attr_str_short(json.loads(detailed_attr_player2))
return detailed_attr_player1, detailed_attr_player2, display_board, str1, str2, "请重新检查人物的生成情况"
json1 = json.loads(detailed_attr_player1)
json2 = json.loads(detailed_attr_player2)
name1 = json1["name"]
name2 = json2["name"]
status = f"""{name1} 大战 {name2}"""
if "hp" in json1 and "hp" in json2:
if json1["hp"] <= 0 and json1["hp"] < json2["hp"]:
unique1 = get_unique_from_json(name1, json1)
unique2 = get_unique_from_json(name2, json2)
return detailed_attr_player1, detailed_attr_player2, display_board, "", "", "战斗结束!请清除战斗或者重新生成人物"
if json2["hp"] <= 0 and json2["hp"] < json1["hp"]:
unique1 = get_unique_from_json(name1, json1)
unique2 = get_unique_from_json(name2, json2)
return detailed_attr_player1, detailed_attr_player2, display_board, "", "", "战斗结束!请清除战斗或者重新生成人物"
game_manager = GameManager(json1, json2, detailed_skill_player1, detailed_skill_player2)
msgs, new_player1, new_player2 = game_manager.run()
for msg in msgs:
display_board.append(msg)
if game_manager.flag == "player1_win":
unique1 = get_unique_from_json(name1, json1)
unique2 = get_unique_from_json(name2, json2)
update_elo(unique1, unique2)
elif game_manager.flag == "player2_win":
unique1 = get_unique_from_json(name1, json1)
unique2 = get_unique_from_json(name2, json2)
update_elo(unique2, unique1)
new_player1_str = json.dumps(new_player1, ensure_ascii=False)
new_player2_str = json.dumps(new_player2, ensure_ascii=False)
return new_player1_str, new_player2_str, display_board, get_attr_str_short(new_player1), get_attr_str_short(
new_player2), status
def callback_random_role(display_board):
data = get_random_fighter()
name = data["name"]
json_str = data["attr_str"]
skills_jsonl = data["skills"]
json_answer = json.loads(json_str)
display_board.append(("从数据库选择角色" + name, None))
return name, json_str, skills_jsonl, get_attr_str_short(json_answer), get_skill_str_short(
json_answer), display_board
def callback_role_from_name(display_board, name):
data = searching_fighter_on_elo(name)
json_str = data["attr_str"]
skills_jsonl = data["skills"]
json_answer = json.loads(json_str)
display_board.append(("从数据库选择角色" + name, None))
return name, json_str, skills_jsonl, get_attr_str_short(json_answer), get_skill_str_short(
json_answer), display_board
def callback_clean_fight(display_board, detailed_attr_player1, detailed_attr_player2):
display_board = []
json1 = json.loads(detailed_attr_player1)
json2 = json.loads(detailed_attr_player2)
if "hp" in json1:
del json1["hp"]
if "hp" in json2:
del json2["hp"]
new_detailed_attr_player1 = json.dumps(json1, ensure_ascii=False)
new_detailed_attr_player2 = json.dumps(json2, ensure_ascii=False)
return display_board, new_detailed_attr_player1, new_detailed_attr_player2
def callback_refresh_rank():
return get_rank_str()
with gr.Blocks() as demo:
gr.Markdown(
"""
# LuotuoFighter
## 骆驼大乱斗
Implemented by 李鲁鲁
项目链接 https://github.com/LC1332/Chat-Haruhi-Suzumiya
这个原型是为了百川Hackathon而初步建立
三种角色建立方式: 根据姓名召唤、随机召唤,或者输入姓名和描述后生成新的人物
姓名召唤会召唤同名人物中战力最强的那个
由于Gradio特性,建立角色要花个几十秒,先点击生成功法,再点击生成功法描述
"""
)
with gr.Row():
with gr.Column():
current_status = gr.Textbox(label="当前状态", value="请输入角色1的姓名之后生成功法,或进行召唤")
display_board = gr.Chatbot(height=800)
submit_fight2 = gr.Button("继续战斗!")
clean_fight = gr.Button("清空战斗")
with gr.Column():
with gr.Row():
name_player1 = gr.Textbox(label="角色1姓名", scale=1, interactive=True, value="李鲁鲁")
desc_player1 = gr.Textbox(label="角色1描述", scale=20, value="师从,主修剑法", interactive=True)
with gr.Row():
generate_ma_player1 = gr.Button("生成功法")
generate_madetail_player1 = gr.Button("生成功法描述")
with gr.Row():
random_sel_player1 = gr.Button("随机召唤")
call_player1_with_name1 = gr.Button("根据姓名召唤")
with gr.Row():
name_player2 = gr.Textbox(label="角色2姓名", scale=1, interactive=True, value="冷子昂")
desc_player2 = gr.Textbox(label="角色2描述", scale=20, value="师从,主修剑法", interactive=True)
with gr.Row():
generate_ma_player2 = gr.Button("生成功法")
generate_madetail_player2 = gr.Button("生成功法描述")
with gr.Row():
random_sel_player2 = gr.Button("随机召唤2")
call_player1_with_name2 = gr.Button("根据姓名召唤2")
with gr.Row():
submit_fight = gr.Button("继续战斗!")
with gr.Row():
attr_player1 = gr.TextArea(label="角色1属性")
skill_player1 = gr.TextArea(label="角色1功法描述")
with gr.Row():
attr_player2 = gr.TextArea(label="角色2属性")
skill_player2 = gr.TextArea(label="角色2功法描述")
with gr.Row():
refresh_rank = gr.Button("刷新天梯")
with gr.Row():
rank_showboard = gr.TextArea(label="天梯")
with gr.Row():
with gr.Column():
detailed_description_player1 = gr.TextArea(label="角色1具体描述")
detailed_skill_player1 = gr.TextArea(label="角色1技能json")
detailed_attr_player1 = gr.TextArea(label="角色1属性json")
with gr.Column():
detailed_description_player2 = gr.TextArea(label="角色2具体描述")
detailed_skill_player2 = gr.TextArea(label="角色2技能json")
detailed_attr_player2 = gr.TextArea(label="角色2属性json")
generate_ma_player1.click(fn=generate_ma, inputs=[name_player1, desc_player1, display_board],
outputs=[detailed_description_player1, detailed_attr_player1, attr_player1, skill_player1,
display_board, current_status])
generate_ma_player2.click(fn=generate_ma, inputs=[name_player2, desc_player2, display_board],
outputs=[detailed_description_player2, detailed_attr_player2, attr_player2, skill_player2,
display_board, current_status])
generate_madetail_player1.click(fn=generate_madetail, inputs=[detailed_attr_player1, display_board],
outputs=[detailed_skill_player1, display_board, current_status])
generate_madetail_player2.click(fn=generate_madetail, inputs=[detailed_attr_player2, display_board],
outputs=[detailed_skill_player2, display_board, current_status])
random_sel_player1.click(fn=callback_random_role, inputs=[display_board],
outputs=[name_player1, detailed_attr_player1, detailed_skill_player1, attr_player1,
skill_player1, display_board])
random_sel_player2.click(fn=callback_random_role, inputs=[display_board],
outputs=[name_player2, detailed_attr_player2, detailed_skill_player2, attr_player2,
skill_player2, display_board])
call_player1_with_name1.click(fn=callback_role_from_name, inputs=[display_board, name_player1],
outputs=[name_player1, detailed_attr_player1, detailed_skill_player1, attr_player1,
skill_player1, display_board])
call_player1_with_name2.click(fn=callback_role_from_name, inputs=[display_board, name_player2],
outputs=[name_player2, detailed_attr_player2, detailed_skill_player2, attr_player2,
skill_player2, display_board])
refresh_rank.click(fn=callback_refresh_rank, inputs=[], outputs=[rank_showboard])
clean_fight.click(fn=callback_clean_fight, inputs=[display_board, detailed_attr_player1, detailed_attr_player2],
outputs=[display_board, detailed_attr_player1, detailed_attr_player2])
submit_fight.click(fn=continue_fight,
inputs=[detailed_attr_player1, detailed_attr_player2, detailed_skill_player1,
detailed_skill_player2, display_board],
outputs=[detailed_attr_player1, detailed_attr_player2, display_board, attr_player1, attr_player2,
current_status])
submit_fight2.click(fn=continue_fight,
inputs=[detailed_attr_player1, detailed_attr_player2, detailed_skill_player1,
detailed_skill_player2, display_board],
outputs=[detailed_attr_player1, detailed_attr_player2, display_board, attr_player1,
attr_player2, current_status])
demo.launch(debug=False, share=True)