"""This module defines a base class for players.
"""import asyncio
import random
from abc import ABC, abstractmethod
from asyncio import Condition, Event, Queue, Semaphore
from logging import Logger
from time import perf_counter
from typing import Any, Awaitable, Dict, List, Optional, Union
import orjson
from poke_env.concurrency import create_in_poke_loop, handle_threaded_coroutines
from poke_env.data import GenData, to_id_str
from poke_env.environment.abstract_battle import AbstractBattle
from poke_env.environment.battle import Battle
from poke_env.environment.double_battle import DoubleBattle
from poke_env.environment.move import Move
from poke_env.environment.pokemon import Pokemon
from poke_env.exceptions import ShowdownException
from poke_env.player.battle_order import(
BattleOrder,
DefaultBattleOrder,
DoubleBattleOrder,)from poke_env.ps_client import PSClient
from poke_env.ps_client.account_configuration import(
CONFIGURATION_FROM_PLAYER_COUNTER,
AccountConfiguration,)from poke_env.ps_client.server_configuration import(
LocalhostServerConfiguration,
ServerConfiguration,)from poke_env.teambuilder.constant_teambuilder import ConstantTeambuilder
from poke_env.teambuilder.teambuilder import Teambuilder
classPlayer(ABC):"""
Base class for players.
"""# Set of messages to ignore during battle
MESSAGES_TO_IGNORE ={"","t:","expire","uhtmlchange"}# Chance of using default order after an error due to invalid choice
DEFAULT_CHOICE_CHANCE =1/1000# 初始化函数,设置各种参数的默认值def__init__(
self,
account_configuration: Optional[AccountConfiguration]=None,# 账户配置,默认为None*,
avatar: Optional[int]=None,# 头像,默认为None
battle_format:str="gen9randombattle",# 战斗格式,默认为"gen9randombattle"
log_level: Optional[int]=None,# 日志级别,默认为None
max_concurrent_battles:int=1,# 最大并发战斗数,默认为1
save_replays: Union[bool,str]=False,# 是否保存战斗重放,默认为False
server_configuration: Optional[ServerConfiguration]=None,# 服务器配置,默认为None
start_timer_on_battle_start:bool=False,# 战斗开始时是否启动计时器,默认为False
start_listening:bool=True,# 是否开始监听,默认为True
ping_interval: Optional[float]=20.0,# ping间隔时间,默认为20.0
ping_timeout: Optional[float]=20.0,# ping超时时间,默认为20.0
team: Optional[Union[str, Teambuilder]]=None,# 队伍信息,默认为None# 辅助奖励计算函数defreward_computing_helper(
self,
battle: AbstractBattle,# 战斗对象*,
fainted_value:float=0.0,# 损失生命值的价值,默认为0.0
hp_value:float=0.0,# 生命值的价值,默认为0.0
number_of_pokemons:int=6,# 参与战斗的宝可梦数量,默认为6
starting_value:float=0.0,# 初始价值,默认为0.0
status_value:float=0.0,# 状态价值,默认为0.0
victory_value:float=1.0,# 胜利价值,默认为1.0# 定义一个方法,计算战斗的奖励值)->float:# 如果战斗不在奖励缓冲区中,则将其初始值添加到缓冲区中if battle notin self._reward_buffer:
self._reward_buffer[battle]= starting_value
current_value =0# 遍历我方队伍中的每只精灵for mon in battle.team.values():# 根据当前生命值比例计算当前值
current_value += mon.current_hp_fraction * hp_value
# 如果精灵已经倒下,则减去倒下的值if mon.fainted:
current_value -= fainted_value
# 如果精灵有异常状态,则减去异常状态的值elif mon.status isnotNone:
current_value -= status_value
# 根据我方队伍中精灵数量与总精灵数量的差值计算当前值
current_value +=(number_of_pokemons -len(battle.team))* hp_value
# 遍历对手队伍中的每只精灵for mon in battle.opponent_team.values():# 根据当前生命值比例计算当前值
current_value -= mon.current_hp_fraction * hp_value
# 如果精灵已经倒下,则加上倒下的值if mon.fainted:
current_value += fainted_value
# 如果精灵有异常状态,则加上异常状态的值elif mon.status isnotNone:
current_value += status_value
# 根据对手队伍中精灵数量与总精灵数量的差值计算当前值
current_value -=(number_of_pokemons -len(battle.opponent_team))* hp_value
# 如果战斗胜利,则加上胜利的值;如果战斗失败,则减去胜利的值if battle.won:
current_value += victory_value
elif battle.lost:
current_value -= victory_value
# 计算最终奖励值并更新奖励缓冲区
to_return = current_value - self._reward_buffer[battle]
self._reward_buffer[battle]= current_value
return to_return
# 定义一个方法,创建账户配置def_create_account_configuration(self)-> AccountConfiguration:
key =type(self).__name__
# 更新配置计数器
CONFIGURATION_FROM_PLAYER_COUNTER.update([key])
username ="%s %d"%(key, CONFIGURATION_FROM_PLAYER_COUNTER[key])# 如果用户名长度超过18个字符,则截取前18个字符iflen(username)>18:
username ="%s %d"%(
key[:18-len(username)],
CONFIGURATION_FROM_PLAYER_COUNTER[key],)return AccountConfiguration(username,None)# 定义一个方法,处理战斗结束的回调def_battle_finished_callback(self, battle: AbstractBattle):passdefupdate_team(self, team: Union[Teambuilder,str]):"""Updates the team used by the player.
:param team: The new team to use.
:type team: str or Teambuilder
"""# 如果传入的 team 是 Teambuilder 类型,则直接赋值给 self._teamifisinstance(team, Teambuilder):
self._team = team
else:# 如果传入的 team 不是 Teambuilder 类型,则使用 ConstantTeambuilder 类创建一个新的 team
self._team = ConstantTeambuilder(team)asyncdef_get_battle(self, battle_tag:str)-> AbstractBattle:# 去掉 battle_tag 的第一个字符
battle_tag = battle_tag[1:]whileTrue:# 如果 battle_tag 在 self._battles 中,则返回对应的 AbstractBattle 对象if battle_tag in self._battles:return self._battles[battle_tag]# 否则等待 _battle_start_condition 条件满足asyncwith self._battle_start_condition:await self._battle_start_condition.wait()asyncdef_handle_battle_request(
self,
battle: AbstractBattle,
from_teampreview_request:bool=False,
maybe_default_order:bool=False,):# 如果 maybe_default_order 为 True 且随机数小于 DEFAULT_CHOICE_CHANCE,则选择默认的移动if maybe_default_order and random.random()< self.DEFAULT_CHOICE_CHANCE:
message = self.choose_default_move().message
elif battle.teampreview:# 如果在队伍预览阶段,且不是来自队伍预览请求,则返回ifnot from_teampreview_request:return
message = self.teampreview(battle)else:# 选择移动或者等待选择移动的结果
message = self.choose_move(battle)ifisinstance(message, Awaitable):
message =await message
message = message.message
# 发送消息到 ps_client,指定 battle.battle_tagawait self.ps_client.send_message(message, battle.battle_tag)asyncdef_handle_challenge_request(self, split_message: List[str]):"""Handles an individual challenge."""# 获取挑战者的用户名
challenging_player = split_message[2].strip()# 如果挑战者不是当前用户,并且消息长度大于等于 6,且消息中指定的格式与 self._format 相同,则将挑战者加入到 _challenge_queue 中if challenging_player != self.username:iflen(split_message)>=6:if split_message[5]== self._format:await self._challenge_queue.put(challenging_player)asyncdef_update_challenges(self, split_message: List[str]):"""Update internal challenge state.
Add corresponding challenges to internal queue of challenges, where they will be
processed if relevant.
:param split_message: Recevied message, split.
:type split_message: List[str]
"""# 记录调试信息,更新挑战信息
self.logger.debug("Updating challenges with %s", split_message)# 从消息中解析挑战信息
challenges = orjson.loads(split_message[2]).get("challengesFrom",{})# 遍历挑战信息,将符合条件的挑战加入挑战队列for user, format_ in challenges.items():if format_ == self._format:await self._challenge_queue.put(user)asyncdefaccept_challenges(
self,
opponent: Optional[Union[str, List[str]]],
n_challenges:int,
packed_team: Optional[str]=None,):"""Let the player wait for challenges from opponent, and accept them.
If opponent is None, every challenge will be accepted. If opponent if a string,
all challenges from player with that name will be accepted. If opponent is a
list all challenges originating from players whose name is in the list will be
accepted.
Up to n_challenges challenges will be accepted, after what the function will
wait for these battles to finish, and then return.
:param opponent: Players from which challenges will be accepted.
:type opponent: None, str or list of str
:param n_challenges: Number of challenges that will be accepted
:type n_challenges: int
:packed_team: Team to use. Defaults to generating a team with the agent's teambuilder.
:type packed_team: string, optional.
"""# 如果没有指定队伍,则使用下一个队伍if packed_team isNone:
packed_team = self.next_team
# 导入日志模块,记录警告信息import logging
logging.warning("AAAHHH in accept_challenges")# 处理多线程协程,接受挑战await handle_threaded_coroutines(
self._accept_challenges(opponent, n_challenges, packed_team))# 异步方法,用于接受挑战asyncdef_accept_challenges(
self,
opponent: Optional[Union[str, List[str]]],# 对手的用户名,可以是字符串或字符串列表
n_challenges:int,# 挑战的次数
packed_team: Optional[str],# 打包的队伍信息):import logging # 导入日志模块
logging.warning("AAAHHH in _accept_challenges")# 记录警告日志if opponent:# 如果存在对手ifisinstance(opponent,list):# 如果对手是列表
opponent =[to_id_str(o)for o in opponent]# 将对手列表中的每个元素转换为字符串else:
opponent = to_id_str(opponent)# 将对手转换为字符串await self.ps_client.logged_in.wait()# 等待客户端登录完成
self.logger.debug("Event logged in received in accept_challenge")# 记录调试日志for _ inrange(n_challenges):# 循环处理挑战次数whileTrue:# 无限循环
username = to_id_str(await self._challenge_queue.get())# 从挑战队列中获取用户名并转换为字符串
self.logger.debug("Consumed %s from challenge queue in accept_challenge", username
)# 记录调试日志if((opponent isNone)# 如果对手为空or(opponent == username)# 或对手等于用户名or(isinstance(opponent,list)and(username in opponent))# 或对手是列表且用户名在对手列表中):await self.ps_client.accept_challenge(username, packed_team)# 接受挑战await self._battle_semaphore.acquire()# 获取战斗信号量break# 跳出循环await self._battle_count_queue.join()# 等待所有战斗完成@abstractmethoddefchoose_move(
self, battle: AbstractBattle
)-> Union[BattleOrder, Awaitable[BattleOrder]]:"""Abstract method to choose a move in a battle.
:param battle: The battle.
:type battle: AbstractBattle
:return: The move order.
:rtype: str
"""pass# 选择默认的战斗指令defchoose_default_move(self)-> DefaultBattleOrder:"""Returns showdown's default move order.
This order will result in the first legal order - according to showdown's
ordering - being chosen.
"""return DefaultBattleOrder()# 选择一个随机的单个行动,返回一个 BattleOrder 对象defchoose_random_singles_move(self, battle: Battle)-> BattleOrder:# 创建一个包含可用行动的 BattleOrder 对象列表
available_orders =[BattleOrder(move)for move in battle.available_moves]# 将可用交换的 BattleOrder 对象也添加到列表中
available_orders.extend([BattleOrder(switch)for switch in battle.available_switches])# 如果可以超级进化,将带有 mega 标记的 BattleOrder 对象也添加到列表中if battle.can_mega_evolve:
available_orders.extend([BattleOrder(move, mega=True)for move in battle.available_moves])# 如果可以极巨化,将带有 dynamax 标记的 BattleOrder 对象也添加到列表中if battle.can_dynamax:
available_orders.extend([BattleOrder(move, dynamax=True)for move in battle.available_moves])# 如果可以使用 tera,将带有 terastallize 标记的 BattleOrder 对象也添加到列表中if battle.can_tera:
available_orders.extend([
BattleOrder(move, terastallize=True)for move in battle.available_moves
])# 如果可以使用 Z 招式且有活跃的精灵,将带有 z_move 标记的 BattleOrder 对象也添加到列表中if battle.can_z_move and battle.active_pokemon:
available_z_moves =set(battle.active_pokemon.available_z_moves)
available_orders.extend([
BattleOrder(move, z_move=True)for move in battle.available_moves
if move in available_z_moves
])# 如果有可用的行动,则随机选择一个返回if available_orders:return available_orders[int(random.random()*len(available_orders))]# 如果没有可用的行动,则选择默认行动else:return self.choose_default_move()# 从给定的战斗中选择一个随机的合法移动defchoose_random_move(self, battle: AbstractBattle)-> BattleOrder:"""Returns a random legal move from battle.
:param battle: The battle in which to move.
:type battle: AbstractBattle
:return: Move order
:rtype: str
"""# 如果战斗是单打模式ifisinstance(battle, Battle):# 调用选择单打模式下的随机移动方法return self.choose_random_singles_move(battle)# 如果战斗是双打模式elifisinstance(battle, DoubleBattle):# 调用选择双打模式下的随机移动方法return self.choose_random_doubles_move(battle)else:# 抛出数值错误,提示战斗应该是 Battle 或 DoubleBattle 类型raise ValueError("battle should be Battle or DoubleBattle. Received %d"%(type(battle)))# 在天梯上进行游戏asyncdefladder(self, n_games:int):"""Make the player play games on the ladder.
n_games defines how many battles will be played.
:param n_games: Number of battles that will be played
:type n_games: int
"""# 等待玩家登录完成await handle_threaded_coroutines(self._ladder(n_games))# 在天梯上进行游戏的内部方法asyncdef_ladder(self, n_games:int):# 等待玩家登录完成await self.ps_client.logged_in.wait()# 记录开始时间
start_time = perf_counter()# 循环进行指定次数的游戏for _ inrange(n_games):# 异步等待战斗开始条件asyncwith self._battle_start_condition:# 搜索天梯游戏并等待战斗开始await self.ps_client.search_ladder_game(self._format, self.next_team)await self._battle_start_condition.wait()# 当战斗计数队列已满时while self._battle_count_queue.full():# 异步等待战斗结束条件asyncwith self._battle_end_condition:await self._battle_end_condition.wait()# 获取战斗信号量await self._battle_semaphore.acquire()# 等待战斗计数队列完成await self._battle_count_queue.join()# 记录日志,显示天梯游戏完成所花费的时间
self.logger.info("Laddering (%d battles) finished in %fs",
n_games,
perf_counter()- start_time,)# 异步方法,使玩家与对手进行指定次数的对战asyncdefbattle_against(self, opponent:"Player", n_battles:int=1):"""Make the player play n_battles against opponent.
This function is a wrapper around send_challenges and accept challenges.
:param opponent: The opponent to play against.
:type opponent: Player
:param n_battles: The number of games to play. Defaults to 1.
:type n_battles: int
"""# 调用异步方法处理线程化的协程await handle_threaded_coroutines(self._battle_against(opponent, n_battles))# 异步方法,实际进行玩家与对手的对战asyncdef_battle_against(self, opponent:"Player", n_battles:int):# 并发执行发送挑战和接受挑战的操作await asyncio.gather(
self.send_challenges(
to_id_str(opponent.username),
n_battles,
to_wait=opponent.ps_client.logged_in,),
opponent.accept_challenges(
to_id_str(self.username), n_battles, opponent.next_team
),)# 异步方法,使玩家发送挑战给对手asyncdefsend_challenges(
self, opponent:str, n_challenges:int, to_wait: Optional[Event]=None):"""Make the player send challenges to opponent.
opponent must be a string, corresponding to the name of the player to challenge.
n_challenges defines how many challenges will be sent.
to_wait is an optional event that can be set, in which case it will be waited
before launching challenges.
:param opponent: Player username to challenge.
:type opponent: str
:param n_challenges: Number of battles that will be started
:type n_challenges: int
:param to_wait: Optional event to wait before launching challenges.
:type to_wait: Event, optional.
"""# 调用异步方法处理线程化的协程await handle_threaded_coroutines(
self._send_challenges(opponent, n_challenges, to_wait))# 异步方法,实际发送挑战给对手asyncdef_send_challenges(
self, opponent:str, n_challenges:int, to_wait: Optional[Event]=None):# 等待玩家登录完成await self.ps_client.logged_in.wait()# 在发送挑战时记录事件已登录
self.logger.info("Event logged in received in send challenge")# 如果有需要等待的事件,则等待if to_wait isnotNone:await to_wait.wait()# 记录开始时间
start_time = perf_counter()# 发起指定数量的挑战for _ inrange(n_challenges):await self.ps_client.challenge(opponent, self._format, self.next_team)await self._battle_semaphore.acquire()# 等待所有挑战结束await self._battle_count_queue.join()# 记录挑战完成所用时间
self.logger.info("Challenges (%d battles) finished in %fs",
n_challenges,
perf_counter()- start_time,)# 生成随机的队伍预览顺序defrandom_teampreview(self, battle: AbstractBattle)->str:"""Returns a random valid teampreview order for the given battle.
:param battle: The battle.
:type battle: AbstractBattle
:return: The random teampreview order.
:rtype: str
"""# 生成队伍成员列表
members =list(range(1,len(battle.team)+1))# 随机打乱成员顺序
random.shuffle(members)return"/team "+"".join([str(c)for c in members])# 重置玩家的内部战斗追踪器defreset_battles(self):"""Resets the player's inner battle tracker."""# 遍历所有战斗,如果有未结束的战斗则抛出异常for battle inlist(self._battles.values()):ifnot battle.finished:raise EnvironmentError("Can not reset player's battles while they are still running")# 重置战斗追踪器
self._battles ={}# 定义一个方法,用于生成给定战斗的队伍预览顺序defteampreview(self, battle: AbstractBattle)->str:"""Returns a teampreview order for the given battle.
This order must be of the form /team TEAM, where TEAM is a string defining the
team chosen by the player. Multiple formats are supported, among which '3461'
and '3, 4, 6, 1' are correct and indicate leading with pokemon 3, with pokemons
4, 6 and 1 in the back in single battles or leading with pokemons 3 and 4 with
pokemons 6 and 1 in the back in double battles.
Please refer to Pokemon Showdown's protocol documentation for more information.
:param battle: The battle.
:type battle: AbstractBattle
:return: The teampreview order.
:rtype: str
"""# 返回一个随机的队伍预览顺序return self.random_teampreview(battle)@staticmethoddefcreate_order(
order: Union[Move, Pokemon],
mega:bool=False,
z_move:bool=False,
dynamax:bool=False,
terastallize:bool=False,
move_target:int= DoubleBattle.EMPTY_TARGET_POSITION,)-> BattleOrder:"""Formats an move order corresponding to the provided pokemon or move.
:param order: Move to make or Pokemon to switch to.
:type order: Move or Pokemon
:param mega: Whether to mega evolve the pokemon, if a move is chosen.
:type mega: bool
:param z_move: Whether to make a zmove, if a move is chosen.
:type z_move: bool
:param dynamax: Whether to dynamax, if a move is chosen.
:type dynamax: bool
:param terastallize: Whether to terastallize, if a move is chosen.
:type terastallize: bool
:param move_target: Target Pokemon slot of a given move
:type move_target: int
:return: Formatted move order
:rtype: str
"""# 格式化一个移动顺序,对应于提供的精灵或招式return BattleOrder(
order,
mega=mega,
move_target=move_target,
z_move=z_move,
dynamax=dynamax,
terastallize=terastallize,)@propertydefbattles(self)-> Dict[str, AbstractBattle]:# 返回私有属性 _battlesreturn self._battles
@propertydefformat(self)->str:# 返回私有属性 _formatreturn self._format
@propertydefformat_is_doubles(self)->bool:# 检查比赛格式是否为双打
format_lowercase = self._format.lower()return("vgc"in format_lowercase
or"double"in format_lowercase
or"metronome"in format_lowercase
)@propertydefn_finished_battles(self)->int:# 返回已完成的比赛数量returnlen([Nonefor b in self._battles.values()if b.finished])@propertydefn_lost_battles(self)->int:# 返回已输掉的比赛数量returnlen([Nonefor b in self._battles.values()if b.lost])@propertydefn_tied_battles(self)->int:# 返回打平的比赛数量return self.n_finished_battles - self.n_lost_battles - self.n_won_battles
@propertydefn_won_battles(self)->int:# 返回已赢得的比赛数量returnlen([Nonefor b in self._battles.values()if b.won])@property# 计算胜率,即已赢得的战斗数除以已完成的战斗数defwin_rate(self)->float:return self.n_won_battles / self.n_finished_battles
# 返回日志记录器对象@propertydeflogger(self)-> Logger:return self.ps_client.logger
# 返回用户名@propertydefusername(self)->str:return self.ps_client.username
# 返回下一个队伍的名称,如果存在的话@propertydefnext_team(self)-> Optional[str]:# 如果存在队伍对象,则返回其生成的队伍名称if self._team:return self._team.yield_team()# 否则返回空值returnNone
.\PokeLLMon\poke_env\player\random_player.py
# 定义一个随机玩家基线的模块"""This module defines a random players baseline
"""# 从 poke_env.environment 模块中导入 AbstractBattle 类from poke_env.environment import AbstractBattle
# 从 poke_env.player.battle_order 模块中导入 BattleOrder 类from poke_env.player.battle_order import BattleOrder
# 从 poke_env.player.player 模块中导入 Player 类from poke_env.player.player import Player
# 定义一个 RandomPlayer 类,继承自 Player 类classRandomPlayer(Player):# 定义 choose_move 方法,接受一个 AbstractBattle 对象作为参数,返回一个 BattleOrder 对象defchoose_move(self, battle: AbstractBattle)-> BattleOrder:# 调用 Player 类的 choose_random_move 方法选择一个随机的行动return self.choose_random_move(battle)
.\PokeLLMon\poke_env\player\utils.py
"""This module contains utility functions and objects related to Player classes.
"""# 导入必要的模块和对象import asyncio
import math
from concurrent.futures import Future
from typing import Dict, List, Optional, Tuple
from poke_env.concurrency import POKE_LOOP
from poke_env.data import to_id_str
from poke_env.player.baselines import MaxBasePowerPlayer, SimpleHeuristicsPlayer
from poke_env.player.player import Player
from poke_env.player.random_player import RandomPlayer
# 定义不同玩家类型的评估等级
_EVALUATION_RATINGS ={
RandomPlayer:1,
MaxBasePowerPlayer:7.665994,
SimpleHeuristicsPlayer:128.757145,}# 后台交叉评估函数,返回一个 Future 对象defbackground_cross_evaluate(
players: List[Player], n_challenges:int)->"Future[Dict[str, Dict[str, Optional[float]]]]":return asyncio.run_coroutine_threadsafe(
cross_evaluate(players, n_challenges), POKE_LOOP
)# 异步交叉评估函数asyncdefcross_evaluate(
players: List[Player], n_challenges:int)-> Dict[str, Dict[str, Optional[float]]]:# 初始化结果字典
results: Dict[str, Dict[str, Optional[float]]]={
p_1.username:{p_2.username:Nonefor p_2 in players}for p_1 in players
}# 遍历玩家列表,进行交叉评估for i, p_1 inenumerate(players):for j, p_2 inenumerate(players):if j <= i:continue# 并发发送挑战和接受挑战await asyncio.gather(
p_1.send_challenges(
opponent=to_id_str(p_2.username),
n_challenges=n_challenges,
to_wait=p_2.ps_client.logged_in,),
p_2.accept_challenges(
opponent=to_id_str(p_1.username),
n_challenges=n_challenges,
packed_team=p_2.next_team,),)# 更新结果字典中的胜率信息
results[p_1.username][p_2.username]= p_1.win_rate
results[p_2.username][p_1.username]= p_2.win_rate
# 重置战斗状态
p_1.reset_battles()
p_2.reset_battles()return results
# 从结果中估计实力函数def_estimate_strength_from_results(
number_of_games:int, number_of_wins:int, opponent_rating:float# 估计玩家实力基于游戏结果和对手评分defevaluate_player(
number_of_games:int,# 游戏评估的数量
number_of_wins:int,# 赢得的评估游戏数量
opponent_rating:float,# 对手的评分)-> Tuple[float, Tuple[float,float]]:# 返回估计的玩家实力和95%置信区间的元组
n, p = number_of_games, number_of_wins / number_of_games # 计算游戏数量和胜率
q =1- p # 计算失败率if n * p * q <9:# 如果无法应用二项分布的正态近似raise ValueError("The results obtained in evaluate_player are too extreme to obtain an ""accurate player evaluation. You can try to solve this issue by increasing"" the total number of battles. Obtained results: %d victories out of %d"" games."%(p * n, n))
estimate = opponent_rating * p / q # 估计玩家实力
error =(
math.sqrt(n * p * q)/ n *1.96)# 95%置信区间的正态分布
lower_bound =max(0, p - error)# 下限
lower_bound = opponent_rating * lower_bound /(1- lower_bound)# 下限的评估
higher_bound =min(1, p + error)# 上限if higher_bound ==1:# 如果上限为1
higher_bound = math.inf # 上限为正无穷大else:
higher_bound = opponent_rating * higher_bound /(1- higher_bound)# 上限的评估return estimate,(lower_bound, higher_bound)# 返回估计值和置信区间的元组# 后台评估玩家defbackground_evaluate_player(
player: Player,# 玩家对象
n_battles:int=1000,# 战斗数量默认为1000
n_placement_battles:int=30,# 放置战斗数量默认为30)->"Future[Tuple[float, Tuple[float, float]]]":# 返回Future对象,包含估计值和置信区间的元组return asyncio.run_coroutine_threadsafe(
evaluate_player(player, n_battles, n_placement_battles), POKE_LOOP
)# 在POKE_LOOP中异步运行评估玩家函数# 异步评估玩家asyncdefevaluate_player(
player: Player,# 玩家对象
n_battles:int=1000,# 战斗数量默认为1000
n_placement_battles:int=30,# 放置战斗数量默认为30# 估算玩家实力的函数defestimate_player_strength(player: Player, n_battles:int, n_placement_battles:int)-> Tuple[float, Tuple[float,float]]:"""Estimate player strength.
This functions calculates an estimate of a player's strength, measured as its
expected performance against a random opponent in a gen 8 random battle. The
returned number can be interpreted as follows: a strength of k means that the
probability of the player winning a gen 8 random battle against a random player is k
times higher than the probability of the random player winning.
The function returns a tuple containing the best guess based on the played games
as well as a tuple describing a 95% confidence interval for that estimated strength.
The actual evaluation can be performed against any baseline player for which an
accurate strength estimate is available. This baseline is determined at the start of
the process, by playing a limited number of placement battles and choosing the
opponent closest to the player in terms of performance.
:param player: The player to evaluate.
:type player: Player
:param n_battles: The total number of battle to perform, including placement
battles.
:type n_battles: int
:param n_placement_battles: Number of placement battles to perform per baseline
player.
:type n_placement_battles: int
:raises: ValueError if the results are too extreme to be interpreted.
:raises: AssertionError if the player is not configured to play gen8battles or the
selected number of games to play it too small.
:return: A tuple containing the estimated player strength and a 95% confidence
interval
:rtype: tuple of float and tuple of floats
"""# 检查输入assert player.format=="gen8randombattle",("Player %s can not be evaluated as its current format (%s) is not ""gen8randombattle."%(player, player.format))# 如果放置战斗的数量乘以评估等级的数量大于总战斗数量的一半,则进行警告if n_placement_battles *len(_EVALUATION_RATINGS)> n_battles //2:
player.logger.warning("Number of placement battles reduced from %d to %d due to limited number of"" battles (%d). A more accurate evaluation can be performed by increasing ""the total number of players.",
n_placement_battles,
n_battles //len(_EVALUATION_RATINGS)//2,
n_battles,)# 将放置战斗数量减少到总战斗数量的一半除以评估等级的数量
n_placement_battles = n_battles //len(_EVALUATION_RATINGS)//2# 断言放置战斗数量大于0,否则抛出异常assert(
n_placement_battles >0),"Not enough battles to perform placement battles. Please increase the number of ""battles to perform to evaluate the player."# 初始化放置战斗
baselines =[p(max_concurrent_battles=n_battles)for p in _EVALUATION_RATINGS]# 对每个基准玩家进行放置战斗for p in baselines:await p.battle_against(player, n_placement_battles)# 选择最佳对手进行评估
best_opp =min(
baselines, key=lambda p:(abs(p.win_rate -0.5),-_EVALUATION_RATINGS[type(p)]))# 执行主要评估
remaining_battles = n_battles -len(_EVALUATION_RATINGS)* n_placement_battles
await best_opp.battle_against(player, remaining_battles)# 从结果中估计玩家的实力return _estimate_strength_from_results(
best_opp.n_finished_battles,
best_opp.n_lost_battles,
_EVALUATION_RATINGS[type(best_opp)],)
# 导入所需的模块和类from poke_env.ps_client.account_configuration import AccountConfiguration
from poke_env.ps_client.ps_client import PSClient
from poke_env.ps_client.server_configuration import(
LocalhostServerConfiguration,
ServerConfiguration,
ShowdownServerConfiguration,)# 定义 __all__ 列表,包含需要导出的模块和类
__all__ =["AccountConfiguration","LocalhostServerConfiguration","PSClient","ServerConfiguration","ShowdownServerConfiguration",]
.\PokeLLMon\poke_env\stats.py
# 该模块包含与统计相关的实用函数和对象import math
from typing import List
from poke_env.data import GenData
# 定义将统计名称映射到索引的字典
STATS_TO_IDX ={"hp":0,"atk":1,"def":2,"spa":3,"spd":4,"spe":5,"satk":3,"sdef":4,}# 计算原始统计值的函数def_raw_stat(base:int, ev:int, iv:int, level:int, nature_multiplier:float)->int:"""Converts to raw stat
:param base: the base stat
:param ev: Stat Effort Value (EV)
:param iv: Stat Individual Values (IV)
:param level: pokemon level
:param nature_multiplier: stat multiplier of the nature (either 0.9, 1 or 1.1)
:return: the raw stat
"""
s = math.floor((5+ math.floor((math.floor(ev /4)+ iv +2* base)* level /100))* nature_multiplier
)returnint(s)# 计算原始 HP 值的函数def_raw_hp(base:int, ev:int, iv:int, level:int)->int:"""Converts to raw hp
:param base: the base stat
:param ev: HP Effort Value (EV)
:param iv: HP Individual Value (IV)
:param level: pokemon level
:return: the raw hp
"""
s = math.floor((math.floor(ev /4)+ iv +2* base)* level /100)+ level +10returnint(s)# 计算原始统计值的函数defcompute_raw_stats(
species:str, evs: List[int], ivs: List[int], level:int, nature:str, data: GenData
)-> List[int]:"""Converts to raw stats
:param species: pokemon species
:param evs: list of pokemon's EVs (size 6)
:param ivs: list of pokemon's IVs (size 6)
:param level: pokemon level
:param nature: pokemon nature
:return: the raw stats in order [hp, atk, def, spa, spd, spe]
"""assertlen(evs)==6assertlen(ivs)==6
base_stats =[0]*6# 从数据中获取种类的基础统计值for stat, value in data.pokedex[species]["baseStats"].items():
base_stats[STATS_TO_IDX[stat]]= value
nature_multiplier =[1.0]*6# 从数据中获取自然属性的统计值倍增器for stat, multiplier in data.natures[nature].items():if stat !="num":
nature_multiplier[STATS_TO_IDX[stat]]= multiplier
raw_stats =[0]*6# 如果精灵种类是"shedinja",则将生命值设为1if species =="shedinja":
raw_stats[0]=1# 否则,根据基础状态值、努力值、个体值和等级计算生命值else:
raw_stats[0]= _raw_hp(base_stats[0], evs[0], ivs[0], level)# 遍历除生命值外的其他五项状态值for i inrange(1,6):# 根据基础状态值、努力值、个体值、等级和性格系数计算状态值
raw_stats[i]= _raw_stat(
base_stats[i], evs[i], ivs[i], level, nature_multiplier[i])# 返回计算后的状态值列表return raw_stats
"""This module defines the ConstantTeambuilder class, which is a subclass of
ShowdownTeamBuilder that yields a constant team.
"""# 导入Teambuilder类from poke_env.teambuilder.teambuilder import Teambuilder
# 定义ConstantTeambuilder类,继承自Teambuilder类classConstantTeambuilder(Teambuilder):# 初始化方法,接受一个team字符串作为参数def__init__(self, team:str):# 如果team字符串中包含"|",则直接将其赋值给converted_team属性if"|"in team:
self.converted_team = team
# 如果team字符串中不包含"|",则解析team字符串并将解析后的结果赋值给converted_team属性else:
mons = self.parse_showdown_team(team)
self.converted_team = self.join_team(mons)# 返回converted_team属性的值defyield_team(self)->str:return self.converted_team
.\PokeLLMon\poke_env\teambuilder\teambuilder.py
"""This module defines the Teambuilder abstract class, which represents objects yielding
Pokemon Showdown teams in the context of communicating with Pokemon Showdown.
"""# 导入所需的模块from abc import ABC, abstractmethod
from typing import List
from poke_env.stats import STATS_TO_IDX
from poke_env.teambuilder.teambuilder_pokemon import TeambuilderPokemon
# 定义 Teambuilder 抽象类classTeambuilder(ABC):"""Teambuilder objects allow the generation of teams by Player instances.
They must implement the yield_team method, which must return a valid
packed-formatted showdown team every time it is called.
This format is a custom format described in Pokemon's showdown protocol
documentation:
https://github.com/smogon/pokemon-showdown/blob/master/PROTOCOL.md#team-format
This class also implements a helper function to convert teams from the classical
showdown team text format into the packed-format.
"""@abstractmethoddefyield_team(self)->str:"""Returns a packed-format team."""@staticmethod@staticmethoddefjoin_team(team: List[TeambuilderPokemon])->str:"""Converts a list of TeambuilderPokemon objects into the corresponding packed
showdown team format.
:param team: The list of TeambuilderPokemon objects that form the team.
:type team: list of TeambuilderPokemon
:return: The formatted team string.
:rtype: str"""# 将给定的 TeambuilderPokemon 对象列表转换为对应的打包格式的 showdown 队伍格式return"]".join([mon.formatted for mon in team])