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)