From f42646da8e899abc0a844397b0c5b61cf02ca95f Mon Sep 17 00:00:00 2001 From: DragonAura Date: Thu, 26 Jan 2023 21:05:26 +0800 Subject: [PATCH] feat(PyAPI): :sparkles: complete most parts of PyAPI except logger --- PyAPI/API/API.py | 237 ---------------- PyAPI/API/DebugAPI.py | 240 ----------------- PyAPI/API/logic.py | 164 ----------- PyAPI/API/main.py | 26 -- PyAPI/API/structures.py | 105 -------- PyAPI/{API => PyAPI}/AI.py | 9 +- PyAPI/PyAPI/API.py | 250 +++++++++++++++++ PyAPI/{API => PyAPI}/Communication.py | 40 +-- PyAPI/PyAPI/DebugAPI.py | 250 +++++++++++++++++ PyAPI/{API => PyAPI}/Interface.py | 49 ++-- PyAPI/{API => PyAPI}/State.py | 2 +- PyAPI/PyAPI/logic.py | 374 ++++++++++++++++++++++++++ PyAPI/PyAPI/main.py | 30 +++ PyAPI/PyAPI/structures.py | 159 +++++++++++ PyAPI/{API => PyAPI}/utils.py | 15 +- PyAPI/logs/logic-log.txt | 6 + 16 files changed, 1130 insertions(+), 826 deletions(-) delete mode 100644 PyAPI/API/API.py delete mode 100644 PyAPI/API/DebugAPI.py delete mode 100644 PyAPI/API/logic.py delete mode 100644 PyAPI/API/main.py delete mode 100644 PyAPI/API/structures.py rename PyAPI/{API => PyAPI}/AI.py (84%) create mode 100644 PyAPI/PyAPI/API.py rename PyAPI/{API => PyAPI}/Communication.py (89%) create mode 100644 PyAPI/PyAPI/DebugAPI.py rename PyAPI/{API => PyAPI}/Interface.py (80%) rename PyAPI/{API => PyAPI}/State.py (88%) create mode 100644 PyAPI/PyAPI/logic.py create mode 100644 PyAPI/PyAPI/main.py create mode 100644 PyAPI/PyAPI/structures.py rename PyAPI/{API => PyAPI}/utils.py (98%) create mode 100644 PyAPI/logs/logic-log.txt diff --git a/PyAPI/API/API.py b/PyAPI/API/API.py deleted file mode 100644 index 318e51c..0000000 --- a/PyAPI/API/API.py +++ /dev/null @@ -1,237 +0,0 @@ -from Interface import ILogic, IHumanAPI, IButcherAPI, IGameTimer, IAI -from typing import List, Union -import structures as THUAI6 - - -class HumanAPI(IHumanAPI, IGameTimer): - - # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 - - def __init__(self, logic: ILogic): - self.__logic = logic - - def Move(self, timeInMilliseconds: int, angle: float) -> bool: - pass - - # 向特定方向移动 - - def MoveRight(self, timeInMilliseconds: int) -> bool: - pass - - def MoveLeft(self, timeInMilliseconds: int) -> bool: - pass - - def MoveUp(self, timeInMilliseconds: int) -> bool: - pass - - def MoveDown(self, timeInMilliseconds: int) -> bool: - pass - - # 道具和技能相关 - - def PickProp(self, propType: THUAI6.PropType) -> bool: - pass - - def UseProp(self) -> bool: - pass - - def UseSkill(self) -> bool: - pass - - # 消息相关,接收消息时无消息则返回(-1, '') - - def SendMessage(self, toID: int, message: str) -> bool: - pass - - def HaveMessage(self) -> bool: - pass - - def GetMessage(self) -> tuple[int, str]: - pass - - # 等待下一帧 - - def Wait(self) -> bool: - pass - - # 获取各类游戏中的消息 - - def GetFrameCount(self) -> int: - pass - - def GetPlayerGUIDs(self) -> List[int]: - pass - - def GetButchers(self) -> List[THUAI6.Butcher]: - pass - - def GetHumans(self) -> List[THUAI6.Human]: - pass - - def GetProps(self) -> List[THUAI6.Prop]: - pass - - def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: - pass - - def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: - pass - - def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: - pass - - # 用于DEBUG的输出函数,仅在DEBUG模式下有效 - - def PrintHuman(self) -> None: - pass - - def PrintButcher(self) -> None: - pass - - def PrintProp(self) -> None: - pass - - def PrintSelfInfo(self) -> None: - pass - - # 人类阵营的特殊函数 - - def Escape(self) -> bool: - pass - - def StartFixMachine(self) -> bool: - pass - - def EndFixMachine(self) -> bool: - pass - - def StartSaveHuman(self) -> bool: - pass - - def EndSaveHuman(self) -> bool: - pass - - # Timer用 - - def StartTimer(self) -> None: - pass - - def EndTimer(self) -> None: - pass - - def Play(self, ai: IAI) -> None: - pass - - -class ButcherAPI(IButcherAPI, IGameTimer): - - # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 - - def Move(self, timeInMilliseconds: int, angle: float) -> bool: - pass - - # 向特定方向移动 - - def MoveRight(self, timeInMilliseconds: int) -> bool: - pass - - def MoveLeft(self, timeInMilliseconds: int) -> bool: - pass - - def MoveUp(self, timeInMilliseconds: int) -> bool: - pass - - def MoveDown(self, timeInMilliseconds: int) -> bool: - pass - - # 道具和技能相关 - - def PickProp(self, propType: THUAI6.PropType) -> bool: - pass - - def UseProp(self) -> bool: - pass - - def UseSkill(self) -> bool: - pass - - # 消息相关,接收消息时无消息则返回(-1, '') - - def SendMessage(self, toID: int, message: str) -> bool: - pass - - def HaveMessage(self) -> bool: - pass - - def GetMessage(self) -> tuple[int, str]: - pass - - # 等待下一帧 - - def Wait(self) -> bool: - pass - - # 获取各类游戏中的消息 - - def GetFrameCount(self) -> int: - pass - - def GetPlayerGUIDs(self) -> List[int]: - pass - - def GetButchers(self) -> List[THUAI6.Butcher]: - pass - - def GetHumans(self) -> List[THUAI6.Human]: - pass - - def GetProps(self) -> List[THUAI6.Prop]: - pass - - def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: - pass - - def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: - pass - - def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: - pass - - # 用于DEBUG的输出函数,仅在DEBUG模式下有效 - - def PrintHuman(self) -> None: - pass - - def PrintButcher(self) -> None: - pass - - def PrintProp(self) -> None: - pass - - def PrintSelfInfo(self) -> None: - pass - - # 屠夫阵营的特殊函数 - - def Attack(self, angle: float) -> bool: - pass - - def CarryHuman(self) -> bool: - pass - - def ReleaseHuman(self) -> bool: - pass - - def HangHuman(self) -> bool: - pass - - # Timer用 - - def StartTimer(self) -> None: - pass - - def EndTimer(self) -> None: - pass - - def Play(self, ai: IAI) -> None: - pass diff --git a/PyAPI/API/DebugAPI.py b/PyAPI/API/DebugAPI.py deleted file mode 100644 index 7fe4b74..0000000 --- a/PyAPI/API/DebugAPI.py +++ /dev/null @@ -1,240 +0,0 @@ -from Interface import IHumanAPI, IButcherAPI, IGameTimer, IAI -from logic import ILogic - -from Interface import ILogic, IHumanAPI, IButcherAPI, IGameTimer -from typing import List, Union -import structures as THUAI6 - - -class HumanDebugAPI(IHumanAPI, IGameTimer): - - # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 - - def __init__(self, logic: ILogic): - self.__logic = logic - - def Move(self, timeInMilliseconds: int, angle: float) -> bool: - pass - - # 向特定方向移动 - - def MoveRight(self, timeInMilliseconds: int) -> bool: - pass - - def MoveLeft(self, timeInMilliseconds: int) -> bool: - pass - - def MoveUp(self, timeInMilliseconds: int) -> bool: - pass - - def MoveDown(self, timeInMilliseconds: int) -> bool: - pass - - # 道具和技能相关 - - def PickProp(self, propType: THUAI6.PropType) -> bool: - pass - - def UseProp(self) -> bool: - pass - - def UseSkill(self) -> bool: - pass - - # 消息相关,接收消息时无消息则返回(-1, '') - - def SendMessage(self, toID: int, message: str) -> bool: - pass - - def HaveMessage(self) -> bool: - pass - - def GetMessage(self) -> tuple[int, str]: - pass - - # 等待下一帧 - - def Wait(self) -> bool: - pass - - # 获取各类游戏中的消息 - - def GetFrameCount(self) -> int: - pass - - def GetPlayerGUIDs(self) -> List[int]: - pass - - def GetButchers(self) -> List[THUAI6.Butcher]: - pass - - def GetHumans(self) -> List[THUAI6.Human]: - pass - - def GetProps(self) -> List[THUAI6.Prop]: - pass - - def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: - pass - - def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: - pass - - def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: - pass - - # 用于DEBUG的输出函数,仅在DEBUG模式下有效 - - def PrintHuman(self) -> None: - pass - - def PrintButcher(self) -> None: - pass - - def PrintProp(self) -> None: - pass - - def PrintSelfInfo(self) -> None: - pass - - # 人类阵营的特殊函数 - - def Escape(self) -> bool: - pass - - def StartFixMachine(self) -> bool: - pass - - def EndFixMachine(self) -> bool: - pass - - def StartSaveHuman(self) -> bool: - pass - - def EndSaveHuman(self) -> bool: - pass - - # Timer用 - - def StartTimer(self) -> None: - pass - - def EndTimer(self) -> None: - pass - - def Play(self, ai: IAI) -> None: - pass - - -class ButcherDebugAPI(IButcherAPI, IGameTimer): - - # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 - - def Move(self, timeInMilliseconds: int, angle: float) -> bool: - pass - - # 向特定方向移动 - - def MoveRight(self, timeInMilliseconds: int) -> bool: - pass - - def MoveLeft(self, timeInMilliseconds: int) -> bool: - pass - - def MoveUp(self, timeInMilliseconds: int) -> bool: - pass - - def MoveDown(self, timeInMilliseconds: int) -> bool: - pass - - # 道具和技能相关 - - def PickProp(self, propType: THUAI6.PropType) -> bool: - pass - - def UseProp(self) -> bool: - pass - - def UseSkill(self) -> bool: - pass - - # 消息相关,接收消息时无消息则返回(-1, '') - - def SendMessage(self, toID: int, message: str) -> bool: - pass - - def HaveMessage(self) -> bool: - pass - - def GetMessage(self) -> tuple[int, str]: - pass - - # 等待下一帧 - - def Wait(self) -> bool: - pass - - # 获取各类游戏中的消息 - - def GetFrameCount(self) -> int: - pass - - def GetPlayerGUIDs(self) -> List[int]: - pass - - def GetButchers(self) -> List[THUAI6.Butcher]: - pass - - def GetHumans(self) -> List[THUAI6.Human]: - pass - - def GetProps(self) -> List[THUAI6.Prop]: - pass - - def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: - pass - - def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: - pass - - def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: - pass - - # 用于DEBUG的输出函数,仅在DEBUG模式下有效 - - def PrintHuman(self) -> None: - pass - - def PrintButcher(self) -> None: - pass - - def PrintProp(self) -> None: - pass - - def PrintSelfInfo(self) -> None: - pass - - # 屠夫阵营的特殊函数 - - def Attack(self, angle: float) -> bool: - pass - - def CarryHuman(self) -> bool: - pass - - def ReleaseHuman(self) -> bool: - pass - - def HangHuman(self) -> bool: - pass - - # Timer用 - - def StartTimer(self) -> None: - pass - - def EndTimer(self) -> None: - pass - - def Play(self, ai: IAI) -> None: - pass diff --git a/PyAPI/API/logic.py b/PyAPI/API/logic.py deleted file mode 100644 index 67c053a..0000000 --- a/PyAPI/API/logic.py +++ /dev/null @@ -1,164 +0,0 @@ -from abc import abstractmethod -from typing import List, Union, Callable -import threading - -from Interface import ILogic, IGameTimer -from State import State -import structures as THUAI6 -import proto.Message2Clients_pb2 as Message2Clients -import proto.Message2Server_pb2 as Message2Server -import proto.MessageType_pb2 as MessageType - - -class Logic(ILogic): - def __init__(self, playerType: THUAI6.PlayerType, playerID: int, humanType: THUAI6.HumanType, butcherType: THUAI6.ButcherType) -> None: - - # 类型、ID - self.__playerType: THUAI6.PlayerType = playerType - self.__playerID: int = playerID - self.__humanType: THUAI6.HumanType = humanType - self.__butcherType: THUAI6.ButcherType = butcherType - - # 存储状态 - self.__currentState: State - self.__bufferState: State - - # timer,用于实际运行AI - self.__timer: IGameTimer - - # AI线程 - self.__threadAI: threading.Thread - - # 互斥锁 - self.__mtxAI: threading.Lock - self.__mtxBuffer: threading.Lock - self.__mtxTimer: threading.Lock - - # 条件变量 - self.__cvBuffer: threading.Condition - self.__cvAI: threading.Condition - - # 保存缓冲区数 - self.__counterState: int - self.__counterBuffer: int - - # 记录游戏状态 - self.__gameState: THUAI6.GameState = THUAI6.GameState.NullGameState - - # 是否应该执行player() - self.__AILoop: bool = True - - # buffer是否更新完毕 - self.__bufferUpdated: bool = False - - # 是否应当启动AI - self.__AIStart: bool = False - - # asynchronous为true时控制内容更新的变量 - self.__freshed: bool = False - - # IAPI统一可用的接口 - - def GetButchers(self) -> List[THUAI6.Butcher]: - pass - - def GetHumans(self) -> List[THUAI6.Human]: - pass - - def GetProps(self) -> List[THUAI6.Prop]: - pass - - def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: - pass - - def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: - pass - - def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType: - pass - - def Move(self, time: int, angle: float) -> bool: - pass - - def PickProp(self, propType: THUAI6.PropType) -> bool: - pass - - def UseProp(self) -> bool: - pass - - def UseSkill(self) -> bool: - pass - - def SendMessage(self, toID: int, message: str) -> bool: - pass - - def HaveMessage(self) -> bool: - pass - - def GetMessage(self) -> tuple[int, str]: - pass - - def WaitThread(self) -> bool: - pass - - def GetCounter(self) -> int: - pass - - def GetPlayerGUIDs(self) -> List[int]: - pass - - # IHumanAPI使用的接口 - - def Escape(self) -> bool: - pass - - def StartFixMachine(self) -> bool: - pass - - def EndFixMachine(self) -> bool: - pass - - def StartSaveHuman(self) -> bool: - pass - - def EndSaveHuman(self) -> bool: - pass - - # Butcher使用的接口 - - def Attack(self, angle: float) -> bool: - pass - - def CarryHuman(self) -> bool: - pass - - def ReleaseHuman(self) -> bool: - pass - - def HangHuman(self) -> bool: - pass - - # Logic内部逻辑 - def __TryConnection(self) -> bool: - pass - - def __ProcessMessage(self) -> None: - pass - - def __LoadBuffer(self) -> None: - pass - - def __UnBlockBuffer(self) -> None: - pass - - def __UnBlockAI(self) -> None: - pass - - def __Update(self) -> None: - pass - - def __Wait(self) -> None: - pass - - def Main(self, createAI: Callable, IP: str, port: str, file: bool, print: bool, warnOnly: bool) -> None: - pass diff --git a/PyAPI/API/main.py b/PyAPI/API/main.py deleted file mode 100644 index b73b159..0000000 --- a/PyAPI/API/main.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys -from typing import List, Callable -from logic import Logic -from AI import AI, Setting -from Interface import IAI -import structures as THUAI6 - - -def THUAI6Main(argv: List[str], AIBuilder: Callable) -> None: - pID: int = 114514 - sIP: str = "114.51.41.91" - sPort: str = "9810" - file: bool = False - print: bool = False - warnOnly: bool = False - logic = Logic(Setting.playerType(), pID, - Setting.humanType(), Setting.butcherType()) - logic.Main(AIBuilder, sIP, sPort, file, print, warnOnly) - - -def CreateAI() -> IAI: - return AI() - - -if __name__ == '__main__': - THUAI6Main(sys.argv, CreateAI) diff --git a/PyAPI/API/structures.py b/PyAPI/API/structures.py deleted file mode 100644 index d32ad49..0000000 --- a/PyAPI/API/structures.py +++ /dev/null @@ -1,105 +0,0 @@ -from enum import Enum -from typing import List - - -class GameState(Enum): - NullGameState = 0 - GameStart = 1 - GameRunning = 2 - GameEnd = 3 - - -class PlaceType(Enum): - NullPlaceType = 0 - Land = 1 - Wall = 2 - Grass = 3 - Machine = 4 - Gate = 5 - HiddenGate = 6 - - -class ShapeType(Enum): - NullShapeType = 0 - Square = 1 - Circle = 2 - - -class PlayerType(Enum): - NullPlayerType = 0 - HumanPlayer = 1 - ButcherPlayer = 2 - - -class PropType(Enum): - NullPropType = 0 - PropType1 = 1 - - -class HumanType(Enum): - NullHumanType = 0 - HumanType1 = 1 - - -class ButcherType(Enum): - NullButcherType = 0 - ButcherType1 = 1 - - -class HumanBuffType(Enum): - NullHumanBuffType = 0 - HumanBuffType1 = 1 - - -class ButcherBuffType(Enum): - NullButcherBuffType = 0 - ButcherBuffType1 = 1 - - -class HumanState(Enum): - NullHumanState = 0 - Idle = 1 - Fixing = 2 - Dying = 3 - OnChair = 4 - Dead = 5 - - -class Player: - x: int - y: int - speed: int - viewRange: int - playerID: int - guid: int - radius: int - timeUntilSkillAvailable: float - playerType: PlayerType - prop: PropType - place: PlaceType - - -class Human(Player): - state: HumanState - life: int - hangedTime: int - humanType: HumanType - buff: List[HumanBuffType] - - -class Butcher(Player): - damage: int - movable: bool - butcherType: ButcherType - buff: List[ButcherBuffType] - - -class Prop: - x: int - y: int - size: int - guid: int - type: PropType - place: PlaceType - facingDirection: float - isMoving: bool diff --git a/PyAPI/API/AI.py b/PyAPI/PyAPI/AI.py similarity index 84% rename from PyAPI/API/AI.py rename to PyAPI/PyAPI/AI.py index b79b918..41f5dbc 100644 --- a/PyAPI/API/AI.py +++ b/PyAPI/PyAPI/AI.py @@ -1,22 +1,25 @@ -from abc import abstractmethod +import PyAPI.structures as THUAI6 +from PyAPI.Interface import IHumanAPI, IButcherAPI, IAI from typing import Union -from Interface import IHumanAPI, IButcherAPI, IAI -import structures as THUAI6 class Setting: # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 + @staticmethod def asynchronous() -> bool: return False # 选手必须修改该函数的返回值来选择自己的阵营 + @staticmethod def playerType() -> THUAI6.PlayerType: return THUAI6.PlayerType.HumanPlayer # 选手需要将两个都定义,本份代码中不选择的阵营任意定义即可 + @staticmethod def humanType() -> THUAI6.HumanType: return THUAI6.HumanType.HumanType1 + @staticmethod def butcherType() -> THUAI6.ButcherType: return THUAI6.ButcherType.ButcherType1 diff --git a/PyAPI/PyAPI/API.py b/PyAPI/PyAPI/API.py new file mode 100644 index 0000000..ae7f7a3 --- /dev/null +++ b/PyAPI/PyAPI/API.py @@ -0,0 +1,250 @@ +import PyAPI.structures as THUAI6 +from PyAPI.Interface import ILogic, IHumanAPI, IButcherAPI, IGameTimer, IAI +from math import pi +from concurrent.futures import ProcessPoolExecutor, Future +from typing import List, Union + + +class HumanAPI(IHumanAPI, IGameTimer): + + def __init__(self, logic: ILogic) -> None: + self.__logic = logic + self.__pool = ProcessPoolExecutor(20) + + # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 + + def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: + return self.__pool.submit(self.__logic.Move, timeInMilliseconds, angle) + + # 向特定方向移动 + + def MoveRight(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 0.5) + + def MoveLeft(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 1.5) + + def MoveUp(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, 0) + + def MoveDown(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi) + + # 道具和技能相关 + + def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: + return self.__pool.submit(self.__logic.PickProp, propType) + + def UseProp(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseProp) + + def UseSkill(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseSkill) + + # 消息相关,接收消息时无消息则返回(-1, '') + + def SendMessage(self, toID: int, message: str) -> Future[bool]: + return self.__pool.submit(self.__logic.SendMessage, toID, message) + + def HaveMessage(self) -> Future[bool]: + return self.__pool.submit(self.__logic.HaveMessage) + + def GetMessage(self) -> Future[tuple[int, str]]: + return self.__pool.submit(self.__logic.GetMessage) + + # 等待下一帧 + + def Wait(self) -> Future[bool]: + if self.__logic.GetCounter() == -1: + return self.__pool.submit(lambda: False) + else: + return self.__pool.submit(self.__logic.WaitThread) + + # 获取各类游戏中的消息 + + def GetFrameCount(self) -> int: + return self.__logic.GetCounter() + + def GetPlayerGUIDs(self) -> List[int]: + return self.__logic.GetPlayerGUIDs() + + def GetButchers(self) -> List[THUAI6.Butcher]: + return self.__logic.GetButchers() + + def GetHumans(self) -> List[THUAI6.Human]: + return self.__logic.GetHumans() + + def GetProps(self) -> List[THUAI6.Prop]: + return self.__logic.GetProps() + + def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: + return self.__logic.GetSelfInfo() + + def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: + return self.__logic.GetFullMap() + + def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: + return self.__logic.GetPlaceType(cellX, cellY) + + # 用于DEBUG的输出函数,仅在DEBUG模式下有效 + + def PrintHuman(self) -> None: + pass + + def PrintButcher(self) -> None: + pass + + def PrintProp(self) -> None: + pass + + def PrintSelfInfo(self) -> None: + pass + + # 人类阵营的特殊函数 + + def Escape(self) -> Future[bool]: + return self.__pool.submit(self.__logic.Escape) + + def StartFixMachine(self) -> Future[bool]: + return self.__pool.submit(self.__logic.StartFixMachine) + + def EndFixMachine(self) -> Future[bool]: + return self.__pool.submit(self.__logic.EndFixMachine) + + def StartSaveHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.StartSaveHuman) + + def EndSaveHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.EndSaveHuman) + + # Timer用 + + def StartTimer(self) -> None: + pass + + def EndTimer(self) -> None: + pass + + def Play(self, ai: IAI) -> None: + pass + + +class ButcherAPI(IButcherAPI, IGameTimer): + + def __init__(self, logic: ILogic) -> None: + self.__logic = logic + self.__pool = ProcessPoolExecutor(20) + + # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 + + def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: + return self.__pool.submit(self.__logic.Move, timeInMilliseconds, angle) + + # 向特定方向移动 + + def MoveRight(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 0.5) + + def MoveLeft(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 1.5) + + def MoveUp(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, 0) + + def MoveDown(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi) + + # 道具和技能相关 + + def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: + return self.__pool.submit(self.__logic.PickProp, propType) + + def UseProp(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseProp) + + def UseSkill(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseSkill) + + # 消息相关,接收消息时无消息则返回(-1, '') + + def SendMessage(self, toID: int, message: str) -> Future[bool]: + return self.__pool.submit(self.__logic.SendMessage, toID, message) + + def HaveMessage(self) -> Future[bool]: + return self.__pool.submit(self.__logic.HaveMessage) + + def GetMessage(self) -> Future[tuple[int, str]]: + return self.__pool.submit(self.__logic.GetMessage) + + # 等待下一帧 + + def Wait(self) -> Future[bool]: + if self.__logic.GetCounter() == -1: + return self.__pool.submit(lambda: False) + else: + return self.__pool.submit(self.__logic.WaitThread) + + # 获取各类游戏中的消息 + + def GetFrameCount(self) -> int: + return self.__logic.GetCounter() + + def GetPlayerGUIDs(self) -> List[int]: + return self.__logic.GetPlayerGUIDs() + + def GetButchers(self) -> List[THUAI6.Butcher]: + return self.__logic.GetButchers() + + def GetHumans(self) -> List[THUAI6.Human]: + return self.__logic.GetHumans() + + def GetProps(self) -> List[THUAI6.Prop]: + return self.__logic.GetProps() + + def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: + return self.__logic.GetSelfInfo() + + def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: + return self.__logic.GetFullMap() + + def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: + return self.__logic.GetPlaceType(cellX, cellY) + + # 用于DEBUG的输出函数,仅在DEBUG模式下有效 + + def PrintHuman(self) -> None: + pass + + def PrintButcher(self) -> None: + pass + + def PrintProp(self) -> None: + pass + + def PrintSelfInfo(self) -> None: + pass + + # 屠夫阵营的特殊函数 + + def Attack(self, angle: float) -> Future[bool]: + return self.__pool.submit(self.__logic.Attack, angle) + + def CarryHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.CarryHuman) + + def ReleaseHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.ReleaseHuman) + + def HangHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.HangHuman) + + # Timer用 + + def StartTimer(self) -> None: + pass + + def EndTimer(self) -> None: + pass + + def Play(self, ai: IAI) -> None: + pass diff --git a/PyAPI/API/Communication.py b/PyAPI/PyAPI/Communication.py similarity index 89% rename from PyAPI/API/Communication.py rename to PyAPI/PyAPI/Communication.py index 6d09ce8..5a268ce 100644 --- a/PyAPI/API/Communication.py +++ b/PyAPI/PyAPI/Communication.py @@ -1,14 +1,12 @@ -from queue import Queue -import grpc -import threading -import proto.Message2Clients_pb2 as Message2Clients -import proto.Message2Server_pb2 as Message2Server -import proto.MessageType_pb2 as MessageType +import PyAPI.structures as THUAI6 +from PyAPI.AI import Setting +from PyAPI.utils import THUAI62Proto +from PyAPI.Interface import IErrorHandler import proto.Services_pb2_grpc as Services -from logic import Logic -from Interface import IErrorHandler -from utils import THUAI62Proto -import structures as THUAI6 +import proto.Message2Clients_pb2 as Message2Clients +import threading +import grpc +from queue import Queue # 使用gRPC的异步来减少通信对于选手而言损失的时间,而gRPC的return值有result()方法,故若连接错误时也应当返回一个具有result()方法的对象,使用此处的ErrorHandler类来实现 @@ -24,6 +22,8 @@ class Communication: __haveNewMessage: bool __message2Client: Message2Clients.MessageToClient __messageQueue: Queue # Python的Queue是线程安全的,故无须自己实现Queue + __mtxMessage: threading.Lock + __cvMessage: threading.Condition def __init__(self, sIP: str, sPort: str): aim = sIP + ':' + sPort @@ -31,6 +31,7 @@ class Communication: self.__THUAI6Stub = Services.AvailableServiceStub(channel) self.__haveNewMessage = False self.__messageQueue = Queue() + self.__cvMessage = threading.Condition() def Move(self, time: int, angle: float, playerID: int) -> bool: try: @@ -190,21 +191,22 @@ class Communication: else: return True - def HaveMessage2Client(self) -> bool: - return self.__haveNewMessage - def GetMessage2Client(self) -> Message2Clients.MessageToClient: - self.__haveNewMessage = False - return self.__message2Client + with self.__cvMessage: + self.__cvMessage.wait_for(lambda: self.__haveNewMessage) + self.__haveNewMessage = False + return self.__message2Client - def AddPlayer(self, playerID: int, playerType: THUAI6.PlayerType, humanType: THUAI6.HumanType, butcherType: THUAI6.ButcherType) -> None: + def AddPlayer(self, playerID: int) -> None: def tMessage(): try: playerMsg = THUAI62Proto.THUAI62ProtobufPlayer( - playerID, playerType, humanType, butcherType) + playerID, Setting.playerType(), Setting.humanType(), Setting.butcherType()) for msg in self.__THUAI6Stub.AddPlayer(playerMsg): - self.__haveNewMessage = True - self.__message2Client = msg + with self.__cvMessage: + self.__haveNewMessage = True + self.__message2Client = msg + self.__cvMessage.notify() except grpc.RpcError as e: return diff --git a/PyAPI/PyAPI/DebugAPI.py b/PyAPI/PyAPI/DebugAPI.py new file mode 100644 index 0000000..3c1060e --- /dev/null +++ b/PyAPI/PyAPI/DebugAPI.py @@ -0,0 +1,250 @@ +import PyAPI.structures as THUAI6 +from PyAPI.Interface import ILogic, IHumanAPI, IButcherAPI, IGameTimer, IAI +from math import pi +from concurrent.futures import ProcessPoolExecutor, Future +from typing import List, Union + + +class HumanDebugAPI(IHumanAPI, IGameTimer): + + def __init__(self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int) -> None: + self.__logic = logic + self.__pool = ProcessPoolExecutor(20) + + # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 + + def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: + return self.__pool.submit(self.__logic.Move, timeInMilliseconds, angle) + + # 向特定方向移动 + + def MoveRight(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 0.5) + + def MoveLeft(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 1.5) + + def MoveUp(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, 0) + + def MoveDown(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi) + + # 道具和技能相关 + + def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: + return self.__pool.submit(self.__logic.PickProp, propType) + + def UseProp(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseProp) + + def UseSkill(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseSkill) + + # 消息相关,接收消息时无消息则返回(-1, '') + + def SendMessage(self, toID: int, message: str) -> Future[bool]: + return self.__pool.submit(self.__logic.SendMessage, toID, message) + + def HaveMessage(self) -> Future[bool]: + return self.__pool.submit(self.__logic.HaveMessage) + + def GetMessage(self) -> Future[tuple[int, str]]: + return self.__pool.submit(self.__logic.GetMessage) + + # 等待下一帧 + + def Wait(self) -> Future[bool]: + if self.__logic.GetCounter() == -1: + return self.__pool.submit(lambda: False) + else: + return self.__pool.submit(self.__logic.WaitThread) + + # 获取各类游戏中的消息 + + def GetFrameCount(self) -> int: + return self.__logic.GetCounter() + + def GetPlayerGUIDs(self) -> List[int]: + return self.__logic.GetPlayerGUIDs() + + def GetButchers(self) -> List[THUAI6.Butcher]: + return self.__logic.GetButchers() + + def GetHumans(self) -> List[THUAI6.Human]: + return self.__logic.GetHumans() + + def GetProps(self) -> List[THUAI6.Prop]: + return self.__logic.GetProps() + + def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: + return self.__logic.GetSelfInfo() + + def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: + return self.__logic.GetFullMap() + + def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: + return self.__logic.GetPlaceType(cellX, cellY) + + # 用于DEBUG的输出函数,仅在DEBUG模式下有效 + + def PrintHuman(self) -> None: + pass + + def PrintButcher(self) -> None: + pass + + def PrintProp(self) -> None: + pass + + def PrintSelfInfo(self) -> None: + pass + + # 人类阵营的特殊函数 + + def Escape(self) -> Future[bool]: + return self.__pool.submit(self.__logic.Escape) + + def StartFixMachine(self) -> Future[bool]: + return self.__pool.submit(self.__logic.StartFixMachine) + + def EndFixMachine(self) -> Future[bool]: + return self.__pool.submit(self.__logic.EndFixMachine) + + def StartSaveHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.StartSaveHuman) + + def EndSaveHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.EndSaveHuman) + + # Timer用 + + def StartTimer(self) -> None: + pass + + def EndTimer(self) -> None: + pass + + def Play(self, ai: IAI) -> None: + pass + + +class ButcherDebugAPI(IButcherAPI, IGameTimer): + + def __init__(self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int) -> None: + self.__logic = logic + self.__pool = ProcessPoolExecutor(20) + + # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 + + def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: + return self.__pool.submit(self.__logic.Move, timeInMilliseconds, angle) + + # 向特定方向移动 + + def MoveRight(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 0.5) + + def MoveLeft(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi * 1.5) + + def MoveUp(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, 0) + + def MoveDown(self, timeInMilliseconds: int) -> Future[bool]: + return self.Move(timeInMilliseconds, pi) + + # 道具和技能相关 + + def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: + return self.__pool.submit(self.__logic.PickProp, propType) + + def UseProp(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseProp) + + def UseSkill(self) -> Future[bool]: + return self.__pool.submit(self.__logic.UseSkill) + + # 消息相关,接收消息时无消息则返回(-1, '') + + def SendMessage(self, toID: int, message: str) -> Future[bool]: + return self.__pool.submit(self.__logic.SendMessage, toID, message) + + def HaveMessage(self) -> Future[bool]: + return self.__pool.submit(self.__logic.HaveMessage) + + def GetMessage(self) -> Future[tuple[int, str]]: + return self.__pool.submit(self.__logic.GetMessage) + + # 等待下一帧 + + def Wait(self) -> Future[bool]: + if self.__logic.GetCounter() == -1: + return self.__pool.submit(lambda: False) + else: + return self.__pool.submit(self.__logic.WaitThread) + + # 获取各类游戏中的消息 + + def GetFrameCount(self) -> int: + return self.__logic.GetCounter() + + def GetPlayerGUIDs(self) -> List[int]: + return self.__logic.GetPlayerGUIDs() + + def GetButchers(self) -> List[THUAI6.Butcher]: + return self.__logic.GetButchers() + + def GetHumans(self) -> List[THUAI6.Human]: + return self.__logic.GetHumans() + + def GetProps(self) -> List[THUAI6.Prop]: + return self.__logic.GetProps() + + def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: + return self.__logic.GetSelfInfo() + + def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: + return self.__logic.GetFullMap() + + def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: + return self.__logic.GetPlaceType(cellX, cellY) + + # 用于DEBUG的输出函数,仅在DEBUG模式下有效 + + def PrintHuman(self) -> None: + pass + + def PrintButcher(self) -> None: + pass + + def PrintProp(self) -> None: + pass + + def PrintSelfInfo(self) -> None: + pass + + # 屠夫阵营的特殊函数 + + def Attack(self, angle: float) -> Future[bool]: + return self.__pool.submit(self.__logic.Attack, angle) + + def CarryHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.CarryHuman) + + def ReleaseHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.ReleaseHuman) + + def HangHuman(self) -> Future[bool]: + return self.__pool.submit(self.__logic.HangHuman) + + # Timer用 + + def StartTimer(self) -> None: + pass + + def EndTimer(self) -> None: + pass + + def Play(self, ai: IAI) -> None: + pass diff --git a/PyAPI/API/Interface.py b/PyAPI/PyAPI/Interface.py similarity index 80% rename from PyAPI/API/Interface.py rename to PyAPI/PyAPI/Interface.py index d033091..f216948 100644 --- a/PyAPI/API/Interface.py +++ b/PyAPI/PyAPI/Interface.py @@ -1,6 +1,7 @@ -from abc import abstractmethod, ABCMeta from typing import List, Union -import structures as THUAI6 +from concurrent.futures import Future +from abc import abstractmethod, ABCMeta +import PyAPI.structures as THUAI6 class ILogic(metaclass=ABCMeta): @@ -112,65 +113,65 @@ class ILogic(metaclass=ABCMeta): pass -class IAPI(metacls=ABCMeta): +class IAPI(metaclass=ABCMeta): # 选手可执行的操作 # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 @abstractmethod - def Move(self, timeInMilliseconds: int, angle: float) -> bool: + def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: pass # 向特定方向移动 @abstractmethod - def MoveRight(self, timeInMilliseconds: int) -> bool: + def MoveRight(self, timeInMilliseconds: int) -> Future[bool]: pass @abstractmethod - def MoveLeft(self, timeInMilliseconds: int) -> bool: + def MoveLeft(self, timeInMilliseconds: int) -> Future[bool]: pass @abstractmethod - def MoveUp(self, timeInMilliseconds: int) -> bool: + def MoveUp(self, timeInMilliseconds: int) -> Future[bool]: pass @abstractmethod - def MoveDown(self, timeInMilliseconds: int) -> bool: + def MoveDown(self, timeInMilliseconds: int) -> Future[bool]: pass # 道具和技能相关 @abstractmethod - def PickProp(self, propType: THUAI6.PropType) -> bool: + def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: pass @abstractmethod - def UseProp(self) -> bool: + def UseProp(self) -> Future[bool]: pass @abstractmethod - def UseSkill(self) -> bool: + def UseSkill(self) -> Future[bool]: pass # 消息相关,接收消息时无消息则返回(-1, '') @abstractmethod - def SendMessage(self, toID: int, message: str) -> bool: + def SendMessage(self, toID: int, message: str) -> Future[bool]: pass @abstractmethod - def HaveMessage(self) -> bool: + def HaveMessage(self) -> Future[bool]: pass @abstractmethod - def GetMessage(self) -> tuple[int, str]: + def GetMessage(self) -> Future[tuple[int, str]]: pass # 等待下一帧 @abstractmethod - def Wait(self) -> bool: + def Wait(self) -> Future[bool]: pass # 获取各类游戏中的消息 @@ -231,23 +232,23 @@ class IHumanAPI(IAPI, metaclass=ABCMeta): # 人类阵营的特殊函数 @abstractmethod - def Escape(self) -> bool: + def Escape(self) -> Future[bool]: pass @abstractmethod - def StartFixMachine(self) -> bool: + def StartFixMachine(self) -> Future[bool]: pass @abstractmethod - def EndFixMachine(self) -> bool: + def EndFixMachine(self) -> Future[bool]: pass @abstractmethod - def StartSaveHuman(self) -> bool: + def StartSaveHuman(self) -> Future[bool]: pass @abstractmethod - def EndSaveHuman(self) -> bool: + def EndSaveHuman(self) -> Future[bool]: pass @@ -256,19 +257,19 @@ class IButcherAPI(IAPI, metaclass=ABCMeta): # 屠夫阵营的特殊函数 @abstractmethod - def Attack(self, angle: float) -> bool: + def Attack(self, angle: float) -> Future[bool]: pass @abstractmethod - def CarryHuman(self) -> bool: + def CarryHuman(self) -> Future[bool]: pass @abstractmethod - def ReleaseHuman(self) -> bool: + def ReleaseHuman(self) -> Future[bool]: pass @abstractmethod - def HangHuman(self) -> bool: + def HangHuman(self) -> Future[bool]: pass diff --git a/PyAPI/API/State.py b/PyAPI/PyAPI/State.py similarity index 88% rename from PyAPI/API/State.py rename to PyAPI/PyAPI/State.py index 868d832..c7be3f6 100644 --- a/PyAPI/API/State.py +++ b/PyAPI/PyAPI/State.py @@ -1,5 +1,5 @@ from typing import List, Union -import structures as THUAI6 +import PyAPI.structures as THUAI6 class State: diff --git a/PyAPI/PyAPI/logic.py b/PyAPI/PyAPI/logic.py new file mode 100644 index 0000000..d363121 --- /dev/null +++ b/PyAPI/PyAPI/logic.py @@ -0,0 +1,374 @@ +import os +from abc import abstractmethod +from typing import List, Union, Callable +import threading +import logging +import proto.MessageType_pb2 as MessageType +import proto.Message2Server_pb2 as Message2Server +import proto.Message2Clients_pb2 as Message2Clients +import PyAPI.structures as THUAI6 +from PyAPI.utils import Proto2THUAI6, AssistFunction +from PyAPI.DebugAPI import HumanDebugAPI, ButcherDebugAPI +from PyAPI.API import HumanAPI, ButcherAPI +from PyAPI.AI import Setting +from PyAPI.Communication import Communication +from PyAPI.State import State +from PyAPI.Interface import ILogic, IGameTimer + + +class Logic(ILogic): + def __init__(self, playerID: int) -> None: + + # ID + self.__playerID: int = playerID + self.__playerGUIDs: List[int] = [] + + # 通信 + self.__comm: Communication + + # 存储状态 + self.__currentState: State + self.__bufferState: State + + # timer,用于实际运行AI + self.__timer: IGameTimer + + # AI线程 + self.__threadAI: threading.Thread + + # 互斥锁 + self.__mtxState: threading.Lock = threading.Lock() + + # 条件变量 + self.__cvBuffer: threading.Condition = threading.Condition() + self.__cvAI: threading.Condition = threading.Condition() + + # 保存缓冲区数 + self.__counterState: int + self.__counterBuffer: int + + # 记录游戏状态 + self.__gameState: THUAI6.GameState = THUAI6.GameState.NullGameState + + # 是否应该执行player() + self.__AILoop: bool = True + + # buffer是否更新完毕 + self.__bufferUpdated: bool = False + + # 是否应当启动AI + self.__AIStart: bool = False + + # asynchronous为True时控制内容更新的变量 + self.__freshed: bool = False + + self.__logger: logging.Logger = logging.getLogger("logic") + + # IAPI统一可用的接口 + + def GetButchers(self) -> List[THUAI6.Butcher]: + with self.__mtxState: + return self.__currentState.butchers + + def GetHumans(self) -> List[THUAI6.Human]: + with self.__mtxState: + return self.__currentState.humans + + def GetProps(self) -> List[THUAI6.Prop]: + with self.__mtxState: + return self.__currentState.props + + def GetSelfInfo(self) -> Union[THUAI6.Human, THUAI6.Butcher]: + with self.__mtxState: + return self.__currentState.self + + def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: + with self.__mtxState: + return self.__currentState.map + + def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType: + with self.__mtxState: + return self.__currentState.map[x][y] + + def Move(self, time: int, angle: float) -> bool: + return self.__comm.Move(time, angle, self.__playerID) + + def PickProp(self, propType: THUAI6.PropType) -> bool: + return self.__comm.PickProp(propType, self.__playerID) + + def UseProp(self) -> bool: + return self.__comm.UseProp(self.__playerID) + + def UseSkill(self) -> bool: + return self.__comm.UseSkill(self.__playerID) + + def SendMessage(self, toID: int, message: str) -> bool: + return self.__comm.SendMessage(toID, message, self.__playerID) + + def HaveMessage(self) -> bool: + return self.__comm.HaveMessage() + + def GetMessage(self) -> tuple[int, str]: + return self.__comm.GetMessage() + + def WaitThread(self) -> bool: + self.__Update() + return True + + def GetCounter(self) -> int: + with self.__mtxState: + return self.__counterState + + def GetPlayerGUIDs(self) -> List[int]: + with self.__mtxState: + return self.__playerGUIDs + + # IHumanAPI使用的接口 + + def Escape(self) -> bool: + return self.__comm.Escape(self.__playerID) + + def StartFixMachine(self) -> bool: + return self.__comm.StartFixMachine(self.__playerID) + + def EndFixMachine(self) -> bool: + return self.__comm.EndFixMachine(self.__playerID) + + def StartSaveHuman(self) -> bool: + return self.__comm.StartSaveHuman(self.__playerID) + + def EndSaveHuman(self) -> bool: + return self.__comm.EndSaveHuman(self.__playerID) + + # Butcher使用的接口 + + def Attack(self, angle: float) -> bool: + return self.__comm.Attack(angle, self.__playerID) + + def CarryHuman(self) -> bool: + return self.__comm.CarryHuman(self.__playerID) + + def ReleaseHuman(self) -> bool: + return self.__comm.ReleaseHuman(self.__playerID) + + def HangHuman(self) -> bool: + return self.__comm.HangHuman(self.__playerID) + + # Logic内部逻辑 + def __TryConnection(self) -> bool: + return self.__comm.TryConnection(self.__playerID) + + def __ProcessMessage(self) -> None: + def messageThread(): + self.__comm.AddPlayer(self.__playerID) + self.__comm.ReadMessage(self.__playerID) + + while self.__gameState != THUAI6.GameState.GameEnd: + # 读取消息,无消息时此处阻塞 + clientMsg = self.__comm.GetMessage2Client() + self.__gameState = Proto2THUAI6.gameStateDict[ + clientMsg.game_state] + + if self.__gameState == THUAI6.GameState.GameStart: + # 读取玩家的GUID + self.__playerGUIDs.clear() + for human in clientMsg.human_message: + self.__playerGUIDs.append(human.guid) + for butcher in clientMsg.butcher_message: + self.__playerGUIDs.append(butcher.guid) + self.__currentState.guids = self.__playerGUIDs + self.__bufferState.guids = self.__playerGUIDs + + self.__LoadBuffer(clientMsg) + self.__AILoop = True + self.__UnBlockAI() + + elif self.__gameState == THUAI6.GameState.GameRunning: + # 读取玩家的GUID + self.__playerGUIDs.clear() + for human in clientMsg.human_message: + self.__playerGUIDs.append(human.guid) + for butcher in clientMsg.butcher_message: + self.__playerGUIDs.append(butcher.guid) + self.__currentState.guids = self.__playerGUIDs + self.__bufferState.guids = self.__playerGUIDs + self.__LoadBuffer(clientMsg) + else: + continue + self.__AILoop = False + with self.__cvBuffer: + self.__bufferUpdated = True + self.__counterBuffer = -1 + self.__cvBuffer.notify() + + threading.Thread(target=messageThread).start() + + def __LoadBuffer(self, message: Message2Clients.MessageToClient) -> None: + with self.__cvBuffer: + self.__bufferState.humans.clear() + self.__bufferState.butchers.clear() + self.__bufferState.props.clear() + self.__bufferState.map = Proto2THUAI6.Protobuf2THUAI6Map( + message.map_message) + if Setting.playerType() == THUAI6.PlayerType.HumanPlayer: + for human in message.human_message: + if human.player_id == self.__playerID: + self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Human( + human) + self.__bufferState.humans.append( + Proto2THUAI6.Protobuf2THUAI6Human(human)) + for butcher in message.butcher_message: + viewRange: int = self.__bufferState.self.viewRange + deltaX: int = butcher.x - self.__bufferState.self.x + deltaY: int = butcher.y - self.__bufferState.self.y + if deltaX * deltaX + deltaY * deltaY <= viewRange * viewRange: + divide: int = max(abs(deltaX), abs(deltaY)) // 100 + dx: float = deltaX / divide + dy: float = deltaY / divide + selfX: float = self.__bufferState.self.x + selfY: float = self.__bufferState.self.y + for i in range(divide): + selfX += dx + selfY += dy + if self.__bufferState.map[AssistFunction.GridToCell(int(selfX))][AssistFunction.GridToCell(int(selfY))]: + break + else: + self.__bufferState.butchers.append( + Proto2THUAI6.Protobuf2THUAI6Butcher(butcher)) + else: + for butcher in message.butcher_message: + if butcher.player_id == self.__playerID: + self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Butcher( + butcher) + self.__bufferState.butchers.append( + Proto2THUAI6.Protobuf2THUAI6Butcher(butcher)) + for human in message.human_message: + viewRange: int = self.__bufferState.self.viewRange + deltaX: int = human.x - self.__bufferState.self.x + deltaY: int = human.y - self.__bufferState.self.y + if deltaX * deltaX + deltaY * deltaY <= viewRange * viewRange: + divide: int = max(abs(deltaX), abs(deltaY)) // 100 + dx: float = deltaX / divide + dy: float = deltaY / divide + selfX: float = self.__bufferState.self.x + selfY: float = self.__bufferState.self.y + for i in range(divide): + selfX += dx + selfY += dy + if self.__bufferState.map[AssistFunction.GridToCell(int(selfX))][AssistFunction.GridToCell(int(selfY))]: + break + else: + self.__bufferState.humans.append( + Proto2THUAI6.Protobuf2THUAI6Human(human)) + for prop in message.prop_message: + self.__bufferState.props.append( + Proto2THUAI6.Protobuf2THUAI6Prop(prop)) + if Setting.asynchronous(): + with self.__mtxState: + self.__currentState, self.__bufferState = self.__bufferState, self.__currentState + self.__freshed = True + else: + self.__bufferUpdated = True + self.__counterBuffer += 1 + self.__cvBuffer.notify() + + def __UnBlockBuffer(self) -> None: + with self.__cvBuffer: + self.__bufferUpdated = True + self.__cvBuffer.notify() + + def __UnBlockAI(self) -> None: + with self.__cvAI: + self.__AIStart = True + self.__cvAI.notify() + + def __Update(self) -> None: + if not Setting.asynchronous(): + with self.__cvBuffer: + self.__cvBuffer.wait_for(lambda: self.__bufferUpdated) + self.__bufferState, self.__currentState = self.__currentState, self.__bufferState + self.__bufferUpdated = False + self.__counterState = self.__counterBuffer + + def __Wait(self) -> None: + self.__freshed = False + with self.__cvBuffer: + self.__cvBuffer.wait_for(lambda: self.__freshed) + + def Main(self, createAI: Callable, IP: str, port: str, file: bool, screen: bool, warnOnly: bool) -> None: + + # 建立日志组件 + self.__logger.setLevel(logging.DEBUG) + formatter = logging.Formatter( + "[%(asctime)s][%(name)s][%(levelname)s] %(message)s", "%H:%M:%S.%e") + # 确保文件存在 + if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"): + os.makedirs(os.path.dirname(os.path.dirname( + os.path.realpath(__file__))) + "/logs") + + fileHandler = logging.FileHandler(os.path.dirname( + os.path.dirname(os.path.realpath(__file__))) + "/logs/logic-log.txt") + screenHandler = logging.StreamHandler() + if file: + fileHandler.setLevel(logging.DEBUG) + fileHandler.setFormatter(formatter) + self.__logger.addHandler(fileHandler) + if screen: + if warnOnly: + screenHandler.setLevel(logging.WARNING) + else: + screenHandler.setLevel(logging.INFO) + screenHandler.setFormatter(formatter) + self.__logger.addHandler(screenHandler) + + self.__logger.info("*********Basic Info*********") + self.__logger.info("asynchronous: %s", Setting.asynchronous()) + self.__logger.info("server: %s:%s", IP, port) + self.__logger.info("playerID: %s", self.__playerID) + self.__logger.info("player type: %s", + THUAI6.playerTypeDict[Setting.playerType()]) + self.__logger.info("****************************") + + # 建立通信组件 + self.__comm = Communication(IP, port) + + # 构造timer + if Setting.playerType() == THUAI6.PlayerType.HumanPlayer: + if not file and not screen: + self.__timer = HumanAPI(self) + else: + self.__timer = HumanDebugAPI( + self, file, screen, warnOnly, self.__playerID) + elif Setting.playerType() == THUAI6.PlayerType.ButcherPlayer: + if not file and not screen: + self.__timer = ButcherAPI(self) + else: + self.__timer = ButcherDebugAPI( + self, file, screen, warnOnly, self.__playerID) + + # 构建AI线程 + def AIThread(): + with self.__cvAI: + self.__cvAI.wait_for(lambda: self.__AIStart) + + ai = createAI() + while self.__AILoop: + if Setting.asynchronous(): + self.__Wait() + self.__timer.StartTimer() + self.__timer.Play(ai) + self.__timer.EndTimer() + else: + self.__Update() + self.__timer.StartTimer() + self.__timer.Play(ai) + self.__timer.EndTimer() + + if self.__TryConnection(): + self.__threadAI = threading.Thread(target=AIThread) + self.__threadAI.start() + self.__ProcessMessage() + self.__threadAI.join() + else: + self.__AILoop = False + return diff --git a/PyAPI/PyAPI/main.py b/PyAPI/PyAPI/main.py new file mode 100644 index 0000000..46e8ef9 --- /dev/null +++ b/PyAPI/PyAPI/main.py @@ -0,0 +1,30 @@ +import os +import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) +sys.path.append(os.path.dirname(os.path.dirname( + os.path.realpath(__file__))) + '/proto') + +from PyAPI.Interface import IAI +from PyAPI.AI import AI +from PyAPI.logic import Logic +from typing import List, Callable + + +def THUAI6Main(argv: List[str], AIBuilder: Callable) -> None: + pID: int = 114514 + sIP: str = "114.51.41.91" + sPort: str = "9810" + file: bool = True + screen: bool = True + warnOnly: bool = False + logic = Logic(pID) + print("Welcome to THUAI6") + logic.Main(AIBuilder, sIP, sPort, file, screen, warnOnly) + + +def CreateAI() -> IAI: + return AI() + + +if __name__ == '__main__': + THUAI6Main(sys.argv, CreateAI) diff --git a/PyAPI/PyAPI/structures.py b/PyAPI/PyAPI/structures.py new file mode 100644 index 0000000..2decf50 --- /dev/null +++ b/PyAPI/PyAPI/structures.py @@ -0,0 +1,159 @@ +from enum import Enum +from typing import List, Dict + + +class GameState(Enum): + NullGameState = 0 + GameStart = 1 + GameRunning = 2 + GameEnd = 3 + + +class PlaceType(Enum): + NullPlaceType = 0 + Land = 1 + Wall = 2 + Grass = 3 + Machine = 4 + Gate = 5 + HiddenGate = 6 + + +class ShapeType(Enum): + NullShapeType = 0 + Square = 1 + Circle = 2 + + +class PlayerType(Enum): + NullPlayerType = 0 + HumanPlayer = 1 + ButcherPlayer = 2 + + +class PropType(Enum): + NullPropType = 0 + PropType1 = 1 + + +class HumanType(Enum): + NullHumanType = 0 + HumanType1 = 1 + + +class ButcherType(Enum): + NullButcherType = 0 + ButcherType1 = 1 + + +class HumanBuffType(Enum): + NullHumanBuffType = 0 + HumanBuffType1 = 1 + + +class ButcherBuffType(Enum): + NullButcherBuffType = 0 + ButcherBuffType1 = 1 + + +class HumanState(Enum): + NullHumanState = 0 + Idle = 1 + Fixing = 2 + Dying = 3 + OnChair = 4 + Dead = 5 + + +class Player: + x: int + y: int + speed: int + viewRange: int + playerID: int + guid: int + radius: int + timeUntilSkillAvailable: float + playerType: PlayerType + prop: PropType + place: PlaceType + + +class Human(Player): + state: HumanState + life: int + hangedTime: int + humanType: HumanType + buff: List[HumanBuffType] + + +class Butcher(Player): + damage: int + movable: bool + butcherType: ButcherType + buff: List[ButcherBuffType] + + +class Prop: + x: int + y: int + size: int + guid: int + type: PropType + place: PlaceType + facingDirection: float + isMoving: bool + + +gameStateDict: Dict[GameState, str] = { + GameState.NullGameState: "NullGameState", + GameState.GameStart: "GameStart", + GameState.GameRunning: "GameRunning", + GameState.GameEnd: "GameEnd"} + +humanStateDict: Dict[HumanState, str] = { + HumanState.NullHumanState: "NullHumanState", + HumanState.Idle: "Idle", + HumanState.Fixing: "Fixing", + HumanState.Dying: "Dying", + HumanState.OnChair: "OnChair", + HumanState.Dead: "Dead"} + +playerTypeDict: Dict[PlayerType, str] = { + PlayerType.NullPlayerType: "NullPlayerType", + PlayerType.HumanPlayer: "HumanPlayer", + PlayerType.ButcherPlayer: "ButcherPlayer"} + +propTypeDict: Dict[PropType, str] = { + PropType.NullPropType: "NullPropType", + PropType.PropType1: "PropType1"} + +humanTypeDict: Dict[HumanType, str] = { + HumanType.NullHumanType: "NullHumanType", + HumanType.HumanType1: "HumanType1"} + +butcherTypeDict: Dict[ButcherType, str] = { + ButcherType.NullButcherType: "NullButcherType", + ButcherType.ButcherType1: "ButcherType1"} + +humanBuffTypeDict: Dict[HumanBuffType, str] = { + HumanBuffType.NullHumanBuffType: "NullHumanBuffType", + HumanBuffType.HumanBuffType1: "HumanBuffType1"} + +butcherBuffTypeDict: Dict[ButcherBuffType, str] = { + ButcherBuffType.NullButcherBuffType: "NullButcherBuffType", + ButcherBuffType.ButcherBuffType1: "ButcherBuffType1"} + +placeTypeDict: Dict[PlaceType, str] = { + PlaceType.NullPlaceType: "NullPlaceType", + PlaceType.Land: "Land", + PlaceType.Wall: "Wall", + PlaceType.Grass: "Grass", + PlaceType.Machine: "Machine", + PlaceType.Gate: "Gate", + PlaceType.HiddenGate: "HiddenGate"} + +shapeTypeDict: Dict[ShapeType, str] = { + ShapeType.NullShapeType: "NullShapeType", + ShapeType.Square: "Square", + ShapeType.Circle: "Circle"} diff --git a/PyAPI/API/utils.py b/PyAPI/PyAPI/utils.py similarity index 98% rename from PyAPI/API/utils.py rename to PyAPI/PyAPI/utils.py index 0fff54a..fd614a5 100644 --- a/PyAPI/API/utils.py +++ b/PyAPI/PyAPI/utils.py @@ -1,15 +1,16 @@ -import structures as THUAI6 -from typing import Final, List -import proto.Message2Clients_pb2 as Message2Clients -import proto.Message2Server_pb2 as Message2Server import proto.MessageType_pb2 as MessageType +import proto.Message2Server_pb2 as Message2Server +import proto.Message2Clients_pb2 as Message2Clients +import PyAPI.structures as THUAI6 +from typing import Final, List + numOfGridPerCell: Final[int] = 1000 # 起到NameSpace的作用 class NoInstance: - def __call__(self, *args, **kwargs): + def __call__(self): raise TypeError("This class cannot be instantiated.") @@ -17,11 +18,11 @@ class AssistFunction(NoInstance): # 辅助函数 @staticmethod def CellToGrid(cell: int) -> int: - return cell*numOfGridPerCell+numOfGridPerCell//2 + return cell * numOfGridPerCell + numOfGridPerCell // 2 @staticmethod def GridToCell(grid: int) -> int: - return grid//numOfGridPerCell + return grid // numOfGridPerCell class Proto2THUAI6(NoInstance): diff --git a/PyAPI/logs/logic-log.txt b/PyAPI/logs/logic-log.txt new file mode 100644 index 0000000..fae3f28 --- /dev/null +++ b/PyAPI/logs/logic-log.txt @@ -0,0 +1,6 @@ +[21:02:47.26][logic][INFO] *********Basic Info********* +[21:02:47.26][logic][INFO] asynchronous: False +[21:02:47.26][logic][INFO] server: 114.51.41.91:9810 +[21:02:47.26][logic][INFO] playerID: 114514 +[21:02:47.26][logic][INFO] player type: HumanPlayer +[21:02:47.26][logic][INFO] ****************************