Browse Source

Merge branch 'dev' of github.com:eesast/THUAI6 into dev

tags/0.1.0
gsy1519 2 years ago
parent
commit
331d396f05
62 changed files with 957 additions and 331 deletions
  1. +29
    -0
      .github/workflows/docker.yml
  2. +3
    -2
      .github/workflows/upload_COS.yml
  3. +12
    -0
      CAPI/cpp/API/include/API.h
  4. +2
    -0
      CAPI/cpp/API/include/logic.h
  5. +1
    -1
      CAPI/cpp/API/src/AI.cpp
  6. +12
    -0
      CAPI/cpp/API/src/API.cpp
  7. +12
    -0
      CAPI/cpp/API/src/DebugAPI.cpp
  8. +8
    -4
      CAPI/cpp/API/src/logic.cpp
  9. +1
    -1
      CAPI/python/PyAPI/AI.py
  10. +6
    -0
      CAPI/python/PyAPI/API.py
  11. +1
    -7
      CAPI/python/PyAPI/Communication.py
  12. +6
    -0
      CAPI/python/PyAPI/DebugAPI.py
  13. +8
    -0
      CAPI/python/PyAPI/Interface.py
  14. +21
    -9
      CAPI/python/PyAPI/State.py
  15. +4
    -0
      CAPI/python/PyAPI/logic.py
  16. +71
    -61
      CAPI/python/PyAPI/structures.py
  17. +0
    -6
      CAPI/python/PyAPI/utils.py
  18. +5
    -5
      CAPI/python/run.sh
  19. +1
    -1
      CAPI/shell/GeneratePythonProto.sh
  20. +15
    -0
      dependency/Dockerfile/Dockerfile_base
  21. +7
    -38
      dependency/Dockerfile/Dockerfile_cpp
  22. +16
    -0
      dependency/Dockerfile/Dockerfile_run
  23. +5
    -0
      dependency/Dockerfile/README.md
  24. +332
    -0
      dependency/algorithm/README.md
  25. +2
    -2
      dependency/proto/Protos.csproj
  26. +9
    -0
      dependency/shell/README.md
  27. +13
    -10
      dependency/shell/compile.sh
  28. +8
    -0
      dependency/shell/cpp_output.sh
  29. +22
    -0
      dependency/shell/docker.sh
  30. +10
    -0
      dependency/shell/generate_proto.sh
  31. +50
    -0
      dependency/shell/run.sh
  32. +10
    -4
      docs/CAPI接口(cpp).md
  33. +25
    -16
      docs/CAPI接口(python).md
  34. +14
    -17
      docs/GameRules.md
  35. +8
    -11
      docs/QandA.md
  36. +2
    -1
      docs/Tool_tutorial.md
  37. +5
    -0
      docs/使用文档.md
  38. +1
    -0
      installer/Installer/Model.cs
  39. +1
    -1
      logic/Client/Client.csproj
  40. +33
    -6
      logic/Client/MainWindow.xaml.cs
  41. +1
    -1
      logic/ClientTest/ClientTest.csproj
  42. +2
    -2
      logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs
  43. +1
    -0
      logic/GameClass/GameObj/Bullet/Bullet.cs
  44. +1
    -1
      logic/GameClass/GameObj/Character/Character.cs
  45. +1
    -1
      logic/GameClass/GameObj/Map/Generator.cs
  46. +5
    -4
      logic/GameClass/GameObj/Map/Map.cs
  47. +3
    -3
      logic/GameEngine/CollisionChecker.cs
  48. +108
    -83
      logic/GameEngine/MoveEngine.cs
  49. +4
    -6
      logic/Gaming/ActionManager.cs
  50. +6
    -3
      logic/Gaming/AttackManager.cs
  51. +10
    -13
      logic/Gaming/CharacterManager .cs
  52. +2
    -2
      logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs
  53. +1
    -1
      logic/Preparation/Interface/IMoveable.cs
  54. +1
    -1
      logic/Preparation/Preparation.csproj
  55. +2
    -1
      logic/Preparation/Utility/GameData.cs
  56. +12
    -2
      logic/Preparation/Utility/XY.cs
  57. +3
    -1
      logic/README.md
  58. +1
    -1
      logic/Server/Server.csproj
  59. +1
    -1
      logic/cmd/playback.cmd
  60. +1
    -1
      playback/Playback/Playback.csproj
  61. BIN
      resource/AIcpp.png
  62. BIN
      resource/AIpy.png

+ 29
- 0
.github/workflows/docker.yml View File

@@ -0,0 +1,29 @@
name: "docker"
on:
push:
branches: [main]

jobs:
upload_docker_images:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Log in to DockerHub
run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}

#- name: Build base docker image
# run: docker build -t ${{ secrets.DOCKER_USERNAME }}/thuai6_base:base -f ./dependency/Dockerfile/Dockerfile_base .
#- name: Push base image to DockerHub
# run: docker push ${{ secrets.DOCKER_USERNAME }}/thuai6_base:base


- name: Build cpp_compile docker image
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/thuai6_cpp:latest -f ./dependency/Dockerfile/Dockerfile_cpp .
- name: Push cpp_compile image to DockerHub
run: docker push ${{ secrets.DOCKER_USERNAME }}/thuai6_cpp:latest

- name: Build run docker image
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/thuai6_run:latest -f ./dependency/Dockerfile/Dockerfile_run .
- name: Push run image to DockerHub
run: docker push ${{ secrets.DOCKER_USERNAME }}/thuai6_run:latest

+ 3
- 2
.github/workflows/upload_COS.yml View File

@@ -113,6 +113,9 @@ jobs:
name: my-artifact
path: ./THUAI6

- name: Remove ReadMe.md
run: rm ./docs/README.md

- name: Markdown to PDF and HTML
uses: BaileyJM02/markdown-to-pdf@v1.2.0
with:
@@ -145,7 +148,6 @@ jobs:
rm ./THUAI6/win/win64/WindowsBase.dll
rm ./THUAI6/win/win64/Debug/grpc_csharp_ext.x64.dll
rm ./THUAI6/win/win64/grpc_csharp_ext.x64.dll

rm -r ./THUAI6/win/CAPI/cpp/grpc
rm -r ./THUAI6/win/CAPI/cpp/spdlog
rm -r ./THUAI6/win/CAPI/cpp/tclap
@@ -162,7 +164,6 @@ jobs:
rm ./THUAI6/osx/osx64/Debug/System.*.dll
rm ./THUAI6/win/win64/System.*.dll
rm ./THUAI6/win/win64/Debug/System.*.dll

rm ./THUAI6/linux/linux64/*.so
rm ./THUAI6/linux/linux64/Debug/*.so


+ 12
- 0
CAPI/cpp/API/include/API.h View File

@@ -83,6 +83,8 @@ public:
virtual bool Attack(double angle) = 0;

virtual std::vector<int64_t> GetPlayerGUIDs() const = 0;

[[nodiscard]] virtual bool HaveView(int gridX, int gridY, int selfX, int selfY, int viewRange) const = 0;
};

class IAPI
@@ -162,6 +164,8 @@ public:
return grid / numOfGridPerCell;
}

[[nodiscard]] virtual bool HaveView(int gridX, int gridY) const = 0;

// 用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用

virtual void Print(std::string str) const = 0;
@@ -271,6 +275,8 @@ public:
std::future<bool> Graduate() override;
[[nodiscard]] std::shared_ptr<const THUAI6::Student> GetSelfInfo() const override;

[[nodiscard]] bool HaveView(int gridX, int gridY) const override;

void Print(std::string str) const override
{
}
@@ -356,6 +362,8 @@ public:
std::future<bool> Attack(double angleInRadian) override;
[[nodiscard]] std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const override;

[[nodiscard]] bool HaveView(int gridX, int gridY) const override;

void Print(std::string str) const override
{
}
@@ -439,6 +447,8 @@ public:
std::future<bool> Graduate() override;
[[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> GetSelfInfo() const override;

[[nodiscard]] bool HaveView(int gridX, int gridY) const override;

void Print(std::string str) const override;
void PrintStudent() const override;
void PrintTricker() const override;
@@ -509,6 +519,8 @@ public:
std::future<bool> Attack(double angleInRadian) override;
[[nodiscard]] std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const override;

[[nodiscard]] bool HaveView(int gridX, int gridY) const override;

void Print(std::string str) const override;
void PrintStudent() const override;
void PrintTricker() const override;


+ 2
- 0
CAPI/cpp/API/include/logic.h View File

@@ -161,6 +161,8 @@ private:
// 等待
void Wait() noexcept;

[[nodiscard]] bool HaveView(int gridX, int gridY, int selfX, int selfY, int viewRange) const override;

public:
// 构造函数还需要传更多参数,有待补充
Logic(THUAI6::PlayerType type, int64_t ID, THUAI6::TrickerType tricker, THUAI6::StudentType student);


+ 1
- 1
CAPI/cpp/API/src/AI.cpp View File

@@ -3,7 +3,7 @@
#include <array>
#include "AI.h"
#include "constants.h"
//注意不要使用conio.h,Windows.h等非标准库
// 注意不要使用conio.h,Windows.h等非标准库

// 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新
extern const bool asynchronous = false;


+ 12
- 0
CAPI/cpp/API/src/API.cpp View File

@@ -419,6 +419,18 @@ std::shared_ptr<const THUAI6::Tricker> TrickerAPI::GetSelfInfo() const
return logic.TrickerGetSelfInfo();
}

bool StudentAPI::HaveView(int gridX, int gridY) const
{
auto selfInfo = GetSelfInfo();
return logic.HaveView(gridX, gridY, selfInfo->x, selfInfo->y, selfInfo->viewRange);
}

bool TrickerAPI::HaveView(int gridX, int gridY) const
{
auto selfInfo = GetSelfInfo();
return logic.HaveView(gridX, gridY, selfInfo->x, selfInfo->y, selfInfo->viewRange);
}

void StudentAPI::Play(IAI& ai)
{
ai.play(*this);


+ 12
- 0
CAPI/cpp/API/src/DebugAPI.cpp View File

@@ -631,6 +631,18 @@ std::shared_ptr<const THUAI6::Tricker> TrickerDebugAPI::GetSelfInfo() const
return logic.TrickerGetSelfInfo();
}

bool StudentDebugAPI::HaveView(int gridX, int gridY) const
{
auto selfInfo = GetSelfInfo();
return logic.HaveView(gridX, gridY, selfInfo->x, selfInfo->y, selfInfo->viewRange);
}

bool TrickerDebugAPI::HaveView(int gridX, int gridY) const
{
auto selfInfo = GetSelfInfo();
return logic.HaveView(gridX, gridY, selfInfo->x, selfInfo->y, selfInfo->viewRange);
}

void StudentDebugAPI::Print(std::string str) const
{
logger->info(str);


+ 8
- 4
CAPI/cpp/API/src/logic.cpp View File

@@ -564,7 +564,7 @@ void Logic::LoadBufferCase(const protobuf::MessageOfObj& item)
}
case THUAI6::MessageOfObj::GateMessage:
{
if (!AssistFunction::HaveView(viewRange, x, y, item.gate_message().x(), item.gate_message().y(), bufferState->gameMap))
if (AssistFunction::HaveView(viewRange, x, y, item.gate_message().x(), item.gate_message().y(), bufferState->gameMap))
{
auto pos = std::make_pair(AssistFunction::GridToCell(item.gate_message().x()), AssistFunction::GridToCell(item.gate_message().y()));
if (bufferState->mapInfo->gateState.count(pos) == 0)
@@ -618,6 +618,7 @@ void Logic::LoadBuffer(const protobuf::MessageToClient& message)
{
std::lock_guard<std::mutex> lock(mtxState);
std::swap(currentState, bufferState);
counterState = counterBuffer;
logger->info("Update State!");
}
freshed = true;
@@ -686,6 +687,11 @@ bool Logic::TryConnection()
return pComm->TryConnection(playerID);
}

bool Logic::HaveView(int gridX, int gridY, int selfX, int selfY, int viewRange) const
{
return AssistFunction::HaveView(viewRange, selfX, selfY, gridX, gridY, currentState->gameMap);
}

void Logic::Main(CreateAIFunc createAI, std::string IP, std::string port, bool file, bool print, bool warnOnly)
{
// 建立日志组件
@@ -695,9 +701,7 @@ void Logic::Main(CreateAIFunc createAI, std::string IP, std::string port, bool f
fileLogger->set_pattern(pattern);
printLogger->set_pattern(pattern);
if (file)
{
fileLogger->set_level(spdlog::level::trace);
}
fileLogger->set_level(spdlog::level::debug);
else
fileLogger->set_level(spdlog::level::off);
if (print)


+ 1
- 1
CAPI/python/PyAPI/AI.py View File

@@ -11,7 +11,7 @@ class Setting:
# 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新
@staticmethod
def asynchronous() -> bool:
return True
return False

# 选手需要依次将player0到player4的职业都定义
@staticmethod


+ 6
- 0
CAPI/python/PyAPI/API.py View File

@@ -132,6 +132,9 @@ class StudentAPI(IStudentAPI, IGameTimer):
def GetGameInfo(self) -> THUAI6.GameInfo:
return self.__logic.GetGameInfo()

def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)

# 用于DEBUG的输出函数,仅在DEBUG模式下有效

def Print(self, cont: str) -> None:
@@ -305,6 +308,9 @@ class TrickerAPI(ITrickerAPI, IGameTimer):
def GetGameInfo(self) -> THUAI6.GameInfo:
return self.__logic.GetGameInfo()

def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)

# 用于DEBUG的输出函数,仅在DEBUG模式下有效

def Print(self, cont: str) -> None:


+ 1
- 7
CAPI/python/PyAPI/Communication.py View File

@@ -16,19 +16,13 @@ class BoolErrorHandler(IErrorHandler):


class Communication:

__THUAI6Stub: Services.AvailableServiceStub
__haveNewMessage: bool
__message2Client: Message2Clients.MessageToClient
__mtxMessage: threading.Lock
__cvMessage: threading.Condition

def __init__(self, sIP: str, sPort: str):
aim = sIP + ':' + sPort
channel = grpc.insecure_channel(aim)
self.__THUAI6Stub = Services.AvailableServiceStub(channel)
self.__haveNewMessage = False
self.__cvMessage = threading.Condition()
self.__message2Client: Message2Clients.MessageToClient

def Move(self, time: int, angle: float, playerID: int) -> bool:
try:


+ 6
- 0
CAPI/python/PyAPI/DebugAPI.py View File

@@ -304,6 +304,9 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
def GetGameInfo(self) -> THUAI6.GameInfo:
return self.__logic.GetGameInfo()

def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)

# 用于DEBUG的输出函数,仅在DEBUG模式下有效

def Print(self, cont: str) -> None:
@@ -756,6 +759,9 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
def GetGameInfo(self) -> THUAI6.GameInfo:
return self.__logic.GetGameInfo()

def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)

# 用于DEBUG的输出函数,仅在DEBUG模式下有效

def Print(self, cont: str) -> None:


+ 8
- 0
CAPI/python/PyAPI/Interface.py View File

@@ -154,6 +154,10 @@ class ILogic(metaclass=ABCMeta):
def StartRouseMate(self, mateID: int) -> bool:
pass

@abstractmethod
def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool:
pass


class IAPI(metaclass=ABCMeta):

@@ -314,6 +318,10 @@ class IAPI(metaclass=ABCMeta):
def GetGameInfo(self) -> THUAI6.GameInfo:
pass

@abstractmethod
def HaveView(self, gridX: int, gridY: int) -> bool:
pass

# 用于DEBUG的输出函数,仅在DEBUG模式下有效

@abstractmethod


+ 21
- 9
CAPI/python/PyAPI/State.py View File

@@ -3,21 +3,33 @@ import PyAPI.structures as THUAI6


class State:
def __init__(self, **kwargs) -> None:
self.teamScore = 0
self.self = THUAI6.Student()
self.students = []
self.trickers = []
self.props = []
self.gameMap = []
self.bullets = []
self.bombedBullets = []
self.mapInfo = THUAI6.GameMap()
self.gameInfo = THUAI6.GameInfo()
self.guids = []
teamScore: int
self: Union[THUAI6.Student, THUAI6.Tricker]

students: List[THUAI6.Student] = []
trickers: List[THUAI6.Tricker] = []
students: List[THUAI6.Student]
trickers: List[THUAI6.Tricker]

props: List[THUAI6.Prop] = []
props: List[THUAI6.Prop]

gameMap: List[List[THUAI6.PlaceType]] = []
gameMap: List[List[THUAI6.PlaceType]]

bullets: List[THUAI6.Bullet] = []
bombedBullets: List[THUAI6.BombedBullet] = []
bullets: List[THUAI6.Bullet]
bombedBullets: List[THUAI6.BombedBullet]

mapInfo: THUAI6.GameMap = THUAI6.GameMap()
mapInfo: THUAI6.GameMap

gameInfo: THUAI6.GameInfo = THUAI6.GameInfo()
gameInfo: THUAI6.GameInfo

guids: List[int] = []
guids: List[int]

+ 4
- 0
CAPI/python/PyAPI/logic.py View File

@@ -262,6 +262,9 @@ class Logic(ILogic):
self.__logger.debug("Called EndAllAction")
return self.__comm.EndAllAction(self.__playerID)

def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool:
return AssistFunction.HaveView(viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap)

# Logic内部逻辑
def __TryConnection(self) -> bool:
self.__logger.info("Try to connect to server...")
@@ -473,6 +476,7 @@ class Logic(ILogic):
if Setting.asynchronous():
with self.__mtxState:
self.__currentState, self.__bufferState = self.__bufferState, self.__currentState
self.__counterState = self.__counterBuffer
self.__logger.info("Update state!")
self.__freshed = True
else:


+ 71
- 61
CAPI/python/PyAPI/structures.py View File

@@ -143,85 +143,95 @@ class HiddenGateState(Enum):


class Player:
x: int
y: int
speed: int
viewRange: int
playerID: int
guid: int
radius: int
score: int
facingDirection: float
timeUntilSkillAvailable: List[float] = []
playerType: PlayerType
prop: List[PropType] = []
place: PlaceType
bulletType: BulletType
playerState: PlayerState
def __init__(self, **kwargs) -> None:
self.x: int = 0
self.y: int = 0
self.speed: int = 0
self.viewRange: int = 0
self.playerID: int = 0
self.guid: int = 0
self.radius: int = 0
self.score: int = 0
self.facingDirection: float = 0.0
self.timeUntilSkillAvailable: List[float] = []
self.playerType: PlayerType = PlayerType.NullPlayerType
self.prop: List[PropType] = []
self.place: PlaceType = PlaceType.NullPlaceType
self.bulletType: BulletType = BulletType.NullBulletType
self.playerState: PlayerState = PlayerState.NullState


class Student(Player):
studentType: StudentType
determination: int
addiction: int
encourageProgress: int
rouseProgress: int
learningSpeed: int
encourageSpeed: int
dangerAlert: float
buff: List[StudentBuffType] = []
def __init__(self, **kwargs) -> None:
super().__init__()
self.studentType: StudentType = StudentType.NullStudentType
self.determination: int = 0
self.addiction: int = 0
self.encourageProgress: int = 0
self.rouseProgress: int = 0
self.learningSpeed: int = 0
self.encourageSpeed: int = 0
self.dangerAlert: float = 0.0
self.buff: List[StudentBuffType] = []


class Tricker(Player):
trickerType: TrickerType
trickDesire: float
classVolume: float
buff: List[TrickerBuffType] = []
def __init__(self, **kwargs) -> None:
super().__init__()
self.trickerType: TrickerType = TrickerType.NullTrickerType
self.trickDesire: float = 0.0
self.classVolume: float = 0.0
self.buff: List[TrickerBuffType] = []


class Prop:
x: int
y: int
guid: int
type: PropType
place: PlaceType
facingDirection: float
def __init__(self, **kwargs) -> None:
self.x: int = 0
self.y: int = 0
self.guid: int = 0
self.type: PropType = PropType.NullPropType
self.place: PlaceType = PlaceType.NullPlaceType
self.facingDirection: float = 0.0


class Bullet:
bulletType: BulletType
x: int
y: int
facingDirection: float
guid: int
team: PlayerType
place: PlaceType
bombRange: float
speed: int
def __init__(self, **kwargs) -> None:
self.bulletType: BulletType = BulletType.NullBulletType
self.x: int = 0
self.y: int = 0
self.facingDirection: float = 0.0
self.guid: int = 0
self.team: PlayerType = PlayerType.NullPlayerType
self.place: PlaceType = PlaceType.NullPlaceType
self.bombRange: float = 0.0
self.speed: int = 0


class BombedBullet:
bulletType: BulletType
x: int
y: int
facingDirection: float
mappingID: int
bombRange: float
def __init__(self, **kwargs) -> None:
self.bulletType: BulletType = BulletType.NullBulletType
self.x: int = 0
self.y: int = 0
self.facingDirection: float = 0.0
self.mappingID: int = 0
self.bombRange: float = 0.0


class GameMap:
classroomState: Dict[Tuple[int, int], int] = {}
gateState: Dict[Tuple[int, int], int] = {}
chestState: Dict[Tuple[int, int], int] = {}
doorState: Dict[Tuple[int, int], bool] = {}
doorProgress: Dict[Tuple[int, int], int] = {}
hiddenGateState: Dict[Tuple[int, int], HiddenGateState] = {}
def __init__(self, **kwargs) -> None:
self.classroomState: Dict[Tuple[int, int], int] = {}
self.gateState: Dict[Tuple[int, int], int] = {}
self.chestState: Dict[Tuple[int, int], int] = {}
self.doorState: Dict[Tuple[int, int], bool] = {}
self.doorProgress: Dict[Tuple[int, int], int] = {}
self.hiddenGateState: Dict[Tuple[int, int], HiddenGateState] = {}


class GameInfo:
gameTime: int
subjectFinished: int
studentGraduated: int
studentQuited: int
studentScore: int
trickerScore: int
def __init__(self, **kwargs) -> None:
self.gameTime: int = 0
self.subjectFinished: int = 0
self.studentGraduated: int = 0
self.studentQuited: int = 0
self.studentScore: int = 0
self.trickerScore: int = 0

+ 0
- 6
CAPI/python/PyAPI/utils.py View File

@@ -177,12 +177,10 @@ class Proto2THUAI6(NoInstance):
tricker.trickDesire = trickerMsg.trick_desire
tricker.classVolume = trickerMsg.class_volume
tricker.bulletType = Proto2THUAI6.bulletTypeDict[trickerMsg.bullet_type]
tricker.timeUntilSkillAvailable.clear()
for time in trickerMsg.time_until_skill_available:
tricker.timeUntilSkillAvailable.append(time)
tricker.place = Proto2THUAI6.placeTypeDict[trickerMsg.place]
tricker.playerState = Proto2THUAI6.playerStateDict[trickerMsg.player_state]
tricker.prop.clear()
for item in trickerMsg.prop:
tricker.prop.append(Proto2THUAI6.propTypeDict[item])
tricker.trickerType = Proto2THUAI6.trickerTypeDict[trickerMsg.tricker_type]
@@ -190,7 +188,6 @@ class Proto2THUAI6(NoInstance):
tricker.playerID = trickerMsg.player_id
tricker.viewRange = trickerMsg.view_range
tricker.radius = trickerMsg.radius
tricker.buff.clear()
for buff in trickerMsg.buff:
tricker.buff.append(Proto2THUAI6.trickerBuffTypeDict[buff])
tricker.playerType = THUAI6.PlayerType.TrickerPlayer
@@ -212,11 +209,9 @@ class Proto2THUAI6(NoInstance):
student.encourageProgress = studentMsg.treat_progress
student.rouseProgress = studentMsg.rescue_progress
student.dangerAlert = studentMsg.danger_alert
student.timeUntilSkillAvailable.clear()
for time in studentMsg.time_until_skill_available:
student.timeUntilSkillAvailable.append(time)
student.place = Proto2THUAI6.placeTypeDict[studentMsg.place]
student.prop.clear()
for item in studentMsg.prop:
student.prop.append(Proto2THUAI6.propTypeDict[item])
student.studentType = Proto2THUAI6.studentTypeDict[studentMsg.student_type]
@@ -225,7 +220,6 @@ class Proto2THUAI6(NoInstance):
student.playerID = studentMsg.player_id
student.viewRange = studentMsg.view_range
student.radius = studentMsg.radius
student.buff.clear()
for buff in studentMsg.buff:
student.buff.append(Proto2THUAI6.studentBuffTypeDict[buff])
student.playerType = THUAI6.PlayerType.StudentPlayer


+ 5
- 5
CAPI/python/run.sh View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash

python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o &
python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -d &
python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 2 -d &
python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 3 -d &
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 4 -d &
python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o&
python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -o&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 2&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 3&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 4&

+ 1
- 1
CAPI/shell/GeneratePythonProto.sh View File

@@ -2,7 +2,7 @@

python -m pip install -r ./CAPI/python/requirements.txt

mkdir -p proto
mkdir -p ./CAPI/python/proto

python -m grpc_tools.protoc -I./CAPI/proto/ --python_out=./CAPI/python/proto --pyi_out=./CAPI/python/proto MessageType.proto
python -m grpc_tools.protoc -I./CAPI/proto/ --python_out=./CAPI/python/proto --pyi_out=./CAPI/python/proto Message2Clients.proto


+ 15
- 0
dependency/Dockerfile/Dockerfile_base View File

@@ -0,0 +1,15 @@
FROM python:3.9.16-bullseye
MAINTAINER eesast
WORKDIR /usr/local
RUN apt-get update && apt-get install --no-install-recommends -y gcc g++ make wget ca-certificates cmake autoconf automake libtool curl unzip git
RUN git clone -b v1.46.3 --depth 1 --shallow-submodules https://gitee.com/mirrors/grpc.git && wget -P . https://cloud.tsinghua.edu.cn/f/1f2713efd9e44255abd6/?dl=1 && mv 'index.html?dl=1' third_party.tar.gz
WORKDIR /usr/local/grpc
RUN rm -rf third_party && mv ../third_party.tar.gz . && tar -zxvf third_party.tar.gz && mkdir -p cmake/build
WORKDIR /usr/local/grpc/cmake/build
RUN cmake -DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
../.. && make -j$(nproc) && make install
WORKDIR /usr/local
RUN git clone https://gitee.com/mirrors/protobuf_source.git ./protobuf
WORKDIR /usr/local/protobuf
RUN git checkout 3.20.x && ./autogen.sh && ./configure && make -j$(nproc) && make install && ldconfig

+ 7
- 38
dependency/Dockerfile/Dockerfile_cpp View File

@@ -1,44 +1,13 @@
FROM ubuntu:22.04
MAINTAINER eesast.com
#FROM ubuntu:18.04
FROM eesast/thuai6_base:base
MAINTAINER eesast
WORKDIR /usr/local
RUN mkdir /usr/local/PlayerCode
#安装主要工具
RUN apt-get update && apt-get install --no-install-recommends -y gcc g++ make wget ca-certificates cmake autoconf automake libtool curl unzip git
#安装grpc
RUN git clone -b v1.46.3 --depth 1 --shallow-submodules https://gitee.com/mirrors/grpc.git
RUN wget -P . https://cloud.tsinghua.edu.cn/f/1f2713efd9e44255abd6/?dl=1
RUN mv 'index.html?dl=1' third_party.tar.gz
WORKDIR /usr/local/grpc
RUN rm -rf third_party
RUN mv ../third_party.tar.gz .
RUN tar -zxvf third_party.tar.gz
RUN mkdir -p cmake/build
WORKDIR /usr/local/grpc/cmake/build
RUN cmake -DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
../..
RUN make -j$(nproc)
RUN make install

#安装protobuf
WORKDIR /usr/local
RUN git clone https://gitee.com/mirrors/protobuf_source.git ./protobuf
WORKDIR /usr/local/protobuf
RUN git checkout 3.20.x
RUN ./autogen.sh
RUN ./configure
RUN make -j$(nproc)
RUN make install
RUN ldconfig
#RUN git submodule update --init --recursive
#RUN cmake .
#RUN cmake --build . --parallel 10
#RUN make install

COPY ./CAPI /usr/local/PlayerCode/CAPI
COPY ./dependency /usr/local/PlayerCode/dependency
COPY ./CAPI/cpp /usr/local/PlayerCode/CAPI/cpp
COPY ./dependency/proto /usr/local/PlayerCode/dependency/proto
COPY ./dependency/shell /usr/local/PlayerCode/dependency/shell
WORKDIR /usr/local/PlayerCode/dependency/proto
RUN ./cpp_output.sh
RUN bash ../shell/cpp_output.sh
WORKDIR /usr/local/PlayerCode/CAPI/cpp
COPY ./dependency/shell/compile.sh .
ENTRYPOINT ["bash","./compile.sh"]


+ 16
- 0
dependency/Dockerfile/Dockerfile_run View File

@@ -0,0 +1,16 @@
FROM eesast/thuai6_base:base
MAINTAINER eesast
WORKDIR /usr/local
RUN mkdir /usr/local/team1 && mkdir /usr/local/team2 && mkdir /usr/local/playback
COPY ./dependency/shell/run.sh .
COPY ./CAPI/python /usr/local/PlayerCode/CAPI/python
COPY ./dependency/proto /usr/local/PlayerCode/dependency/proto
COPY ./dependency/shell /usr/local/PlayerCode/dependency/shell
WORKDIR /usr/local/PlayerCode/CAPI/python
RUN bash ../../dependency/shell/generate_proto.sh

WORKDIR /usr/local
RUN wget -P . https://cloud.tsinghua.edu.cn/f/e48940314a6d4cdb8bd0/?dl=1
RUN mv 'index.html?dl=1' Server.tar.gz
RUN tar -zxvf Server.tar.gz
ENTRYPOINT [ "bash","./run.sh" ]

+ 5
- 0
dependency/Dockerfile/README.md View File

@@ -2,3 +2,8 @@

用于存放Docker配置文件Dockerfile

- 调用runner镜像的方法:
```shell
docker run 镜像名 #编译C++代码
docker run 镜像名 #运行程序(支持一个队既提交python又提交cpp)
```

+ 332
- 0
dependency/algorithm/README.md View File

@@ -0,0 +1,332 @@
# Algorithm

---

天梯分数计算算法

原始记录在:<https://github.com/eesast/THUAI5/discussions/86>

内容如下:

## THUAI4

关于根据队式每场比赛的分数映射到天梯分数的问题:
队式比赛为两队对战,每队得分的区间均为 [0, 2500]。
以 tanh 函数为基础进行设计。
设计原则如下:

1. 输的扣少量天梯分,赢的得大量天梯分
2. 本就有极高天梯分数的虐本就天梯分数低的,这种降维打击现象,天梯分数涨幅极小甚至不涨天梯分
3. 如果在某场比赛中,两者表现差不多,即赢的比输的得分高得不多的话,那么天梯分数涨幅也不是很高
4. 如果本来天梯分数很低的,赢了天梯分数很高的,那么他得到的天梯分会较高,而另一个人,天梯分数降分稍多一些
5. 如果天梯分数低的赢了天梯分数高的,但是这场比赛赢得不多的话,会把两人的分数向中间靠拢
6. 总体上,赢的队伍不会降天梯分;输的队伍不会加天梯分
7. 其他条件相同的情况下,在本场游戏中得分越多,加的天梯分数也越高

上述原则可以保证以下两个目的的达成:

1. 总体来看,进行的游戏场次越多,所有队伍的平均天梯分数就越高
2. 经过足够多次的游戏场次,实力有一定差距的队伍的天体分数差距逐渐拉开,实力相近的队伍的天梯分数不会差别过大,各支队伍的排名趋近于收敛

用 cpp 代码编写算法代码如下(`cal` 函数):

```cpp
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

template <typename T>
using mypair = pair<T, T>;

// orgScore 是天梯中两队的分数;competitionScore 是这次游戏两队的得分

mypair<int> cal(mypair<int> orgScore, mypair<int> competitionScore)
{

// 调整顺序,让第一个元素成为获胜者,便于计算

bool reverse = false; // 记录是否需要调整

if (competitionScore.first < competitionScore.second)
{
reverse = true;
}
else if (competitionScore.first == competitionScore.second)
{
if (orgScore.first == orgScore.second) // 完全平局,不改变天梯分数
{
return orgScore;
}

if (orgScore.first > orgScore.second) // 本次游戏平局,但一方天梯分数高,另一方天梯分数低,需要将两者向中间略微靠拢,因此天梯分数低的定为获胜者
{
reverse = true;
}
else
{
reverse = false;
}
}
if (reverse) // 如果需要换,换两者的顺序
{
swap(competitionScore.first, competitionScore.second);
swap(orgScore.first, orgScore.second);
}


// 转成浮点数
mypair<double> orgScoreLf;
mypair<double> competitionScoreLf;
orgScoreLf.first = orgScore.first;
orgScoreLf.second = orgScore.second;
competitionScoreLf.first = competitionScore.first;
competitionScoreLf.second = competitionScore.second;
mypair<int> resScore;

const double deltaWeight = 80.0; // 差距悬殊判断参数,比赛分差超过此值就可以认定为非常悬殊了,天梯分数增量很小,防止大佬虐菜鸡的现象造成两极分化

double delta = (orgScoreLf.first - orgScoreLf.second) / deltaWeight;
cout << "Tanh delta: " << tanh(delta) << endl;
{

const double firstnerGet = 8e-5; // 胜利者天梯得分权值
const double secondrGet = 5e-6; // 失败者天梯得分权值

double deltaScore = 100.0; // 两队竞争分差超过多少时就认为非常大
double correctRate = (orgScoreLf.first - orgScoreLf.second) / 100.0; // 订正的幅度,该值越小,则在势均力敌时天梯分数改变越大
double correct = 0.5 * (tanh((competitionScoreLf.first - competitionScoreLf.second - deltaScore) / deltaScore - correctRate) + 1.0); // 一场比赛中,在双方势均力敌时,减小天梯分数的改变量

resScore.first = orgScore.first + round(competitionScoreLf.first * competitionScoreLf.first * firstnerGet * (1 - tanh(delta)) * correct); // 胜者所加天梯分
resScore.second = orgScore.second - round((2500.0 - competitionScoreLf.second) * (2500.0 - competitionScoreLf.second) * secondrGet * (1 - tanh(delta)) * correct); // 败者所扣天梯分,2500 为得分的最大值(THUAI4 每场得分介于 0~2500 之间)
}

// 如果换过,再换回来
if (reverse)
{
swap(resScore.first, resScore.second);
}

return resScore;
}
```

**特别注意**:此算法是在 THUAI4 的比赛直接得分封顶为 2500 分、最低不低于 0 分的前提下设计的,因此并不一定适用于 THUAI5 的情形。

## THUAI5

今年把得分上限这个东西去掉了。理论上今年可以得很高很高分,但是我估计大部分比赛得分在400-600左右,最高估计1000左右。算法 借 鉴 了THUAI4,算法,换了个激活函数(正态CDF),感觉分数变化相对更好了一些?
代码如下:

```cpp
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

template <typename T>
using mypair = pair<T, T>;

double PHI(double x) // THUAI3: Sigmoid; THUAI4: Tanh; THUAI5: Normal Distribution CDF
{
//double a1 = 0.2548292592;
//double a2 = -0.284496736;
//double a3 = 1.421413741;
//double a4 = -1.453152027;
//double a5 = 1.061405429;
//double p = 0.3275911;
//int sign = 1;
//if (x < 0)
// sign = -1;
//x = fabs(x) / sqrt(2.0);
//double t = 1.0 / (1.0 + p * x);
//double y = 1.0 - ((((((a5 * t + a4) * t + a3) * t) + a2) * t) + a1) * t * exp(-x * x);
//double cdf = 0.5 * (1.0 + sign * y);
//return (cdf - 0.5) * 2.0; // 化到[-1,1]之间

return erf(x / sqrt(2));
}

// orgScore 是天梯中两队的分数;competitionScore 是这次游戏两队的得分
mypair<int> cal(mypair<int> orgScore, mypair<int> competitionScore)
{

// 调整顺序,让第一个元素成为获胜者,便于计算

bool reverse = false; // 记录是否需要调整

if (competitionScore.first < competitionScore.second)
{
reverse = true;
}
else if (competitionScore.first == competitionScore.second)
{
if (orgScore.first == orgScore.second) // 完全平局,不改变天梯分数
{
return orgScore;
}

if (orgScore.first > orgScore.second) // 本次游戏平局,但一方天梯分数高,另一方天梯分数低,需要将两者向中间略微靠拢,因此天梯分数低的定为获胜者
{
reverse = true;
}
else
{
reverse = false;
}
}

if (reverse) // 如果需要换,换两者的顺序
{
swap(competitionScore.first, competitionScore.second);
swap(orgScore.first, orgScore.second);
}


// 转成浮点数
mypair<double> orgScoreLf;
mypair<double> competitionScoreLf;
orgScoreLf.first = orgScore.first;
orgScoreLf.second = orgScore.second;
competitionScoreLf.first = competitionScore.first;
competitionScoreLf.second = competitionScore.second;
mypair<int> resScore;

const double deltaWeight = 90.0; // 差距悬殊判断参数,比赛分差超过此值就可以认定为非常悬殊了,天梯分数增量很小,防止大佬虐菜鸡的现象造成两极分化

double delta = (orgScoreLf.first - orgScoreLf.second) / deltaWeight;
cout << "Normal CDF delta: " << PHI(delta) << endl;
{

const double firstnerGet = 3e-4; // 胜利者天梯得分权值
const double secondrGet = 1e-4; // 失败者天梯得分权值

double deltaScore = 100.0; // 两队竞争分差超过多少时就认为非常大
double correctRate = (orgScoreLf.first - orgScoreLf.second) / 100.0; // 订正的幅度,该值越小,则在势均力敌时天梯分数改变越大
double correct = 0.5 * (PHI((competitionScoreLf.first - competitionScoreLf.second - deltaScore) / deltaScore - correctRate) + 1.0); // 一场比赛中,在双方势均力敌时,减小天梯分数的改变量

resScore.first = orgScore.first + round(competitionScoreLf.first * competitionScoreLf.first * firstnerGet * (1 - PHI(delta)) * correct); // 胜者所加天梯分
if (competitionScoreLf.second < 1000)
resScore.second = orgScore.second - round((1000.0 - competitionScoreLf.second) * (1000.0 - competitionScoreLf.second) * secondrGet * (1 - PHI(delta)) * correct); // 败者所扣天梯分
else
resScore.second = orgScore.second; // 败者拿1000分,已经很强了,不扣分
}

// 如果换过,再换回来
if (reverse)
{
swap(resScore.first, resScore.second);
}

return resScore;
}

void Print(mypair<int> score)
{
std::cout << " team1: " << score.first << std::endl
<< "team2: " << score.second << std::endl;
}

int main()
{
int x, y;
std::cout << "origin score of team 1 and 2: " << std::endl;
std::cin >> x >> y;
auto ori = mypair<int>(x, y);
std::cout << "game score of team 1 and 2: " << std::endl;
std::cin >> x >> y;
auto sco = mypair<int>(x, y);
Print(cal(ori, sco));
}
```

`1000 - score`(x
`ReLU(1000 - score)`(√
防止真的超过了 1000)

## THUAI6

因为今年的对局得分是两局得分之和,所以会出现一定程度的“数值膨胀”,在这里调低了胜者得分权值,同时提高了比赛分差距悬殊阈值和天梯分差距悬殊阈值。同时由于今年得分的上限不好确定,所以负者失分的基础值变为与胜者的得分之差。

```c++
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

template <typename T>
using mypair = pair<T, T>;

// orgScore 是天梯中两队的分数;competitionScore 是这次游戏两队的得分

mypair<int> cal(mypair<int> orgScore, mypair<int> competitionScore)
{

// 调整顺序,让第一个元素成为获胜者,便于计算

bool reverse = false; // 记录是否需要调整

if (competitionScore.first < competitionScore.second)
{
reverse = true;
}
else if (competitionScore.first == competitionScore.second)
{
if (orgScore.first == orgScore.second) // 完全平局,不改变天梯分数
{
return orgScore;
}

if (orgScore.first > orgScore.second) // 本次游戏平局,但一方天梯分数高,另一方天梯分数低,需要将两者向中间略微靠拢,因此天梯分数低的定为获胜者
{
reverse = true;
}
else
{
reverse = false;
}
}

if (reverse) // 如果需要换,换两者的顺序
{
swap(competitionScore.first, competitionScore.second);
swap(orgScore.first, orgScore.second);
}

// 转成浮点数
mypair<double> orgScoreLf;
mypair<double> competitionScoreLf;
orgScoreLf.first = orgScore.first;
orgScoreLf.second = orgScore.second;
competitionScoreLf.first = competitionScore.first;
competitionScoreLf.second = competitionScore.second;
mypair<int> resScore;

const double deltaWeight = 1000.0; // 差距悬殊判断参数,比赛分差超过此值就可以认定为非常悬殊了,天梯分数增量很小,防止大佬虐菜鸡的现象造成两极分化

double delta = (orgScoreLf.first - orgScoreLf.second) / deltaWeight;
cout << "Tanh delta: " << tanh(delta) << endl;
{

const double firstnerGet = 9e-6; // 胜利者天梯得分权值
const double secondrGet = 5e-6; // 失败者天梯得分权值

double deltaScore = 2100.0; // 两队竞争分差超过多少时就认为非常大
double correctRate = (orgScoreLf.first - orgScoreLf.second) / (deltaWeight * 1.2); // 订正的幅度,该值越小,则在势均力敌时天梯分数改变越大
double correct = 0.5 * (tanh((competitionScoreLf.first - competitionScoreLf.second - deltaScore) / deltaScore - correctRate) + 1.0); // 一场比赛中,在双方势均力敌时,减小天梯分数的改变量
cout << "correct: " << correct << endl;
resScore.first = orgScore.first + round(competitionScoreLf.first * competitionScoreLf.first * firstnerGet * (1 - tanh(delta)) * correct); // 胜者所加天梯分
resScore.second = orgScore.second - round((competitionScoreLf.first - competitionScoreLf.second) * (competitionScoreLf.first - competitionScoreLf.second) * secondrGet * (1 - tanh(delta)) * correct); // 败者所扣天梯分
}

// 如果换过,再换回来
if (reverse)
{
swap(resScore.first, resScore.second);
}

return resScore;
}
```


+ 2
- 2
dependency/proto/Protos.csproj View File

@@ -14,8 +14,8 @@
</ItemGroup>-->

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.1" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.3" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
<PackageReference Include="Grpc.Tools" Version="2.53.0">


+ 9
- 0
dependency/shell/README.md View File

@@ -2,3 +2,12 @@

本目录用于存放程序所需的shell脚本

## run.sh

注意:

1. server 和 client 程序要在后台进行

2. 忙等待到 server 结束

3. 结束后生成标志结束的文件

+ 13
- 10
dependency/shell/compile.sh View File

@@ -3,21 +3,24 @@
i=1
flag=1
bind=/usr/local/mnt
while (( $i <= 4 ))
while (( $i <= 5 ))
do
cp -f $bind/player$i.cpp ./API/src
mv ./API/src/player$i.cpp ./API/src/AI.cpp
cmake ./CMakeLists.txt && make >compile_log$i.txt 2>&1
mv ./capi $bind/capi$i # executable file
if [ $? -ne 0 ]; then
if [ -f "${bind}/player${i}.cpp" ]; then
cp -f $bind/player$i.cpp ./API/src/AI.cpp
cmake ./CMakeLists.txt && make >compile_log$i.txt 2>&1
mv ./capi $bind/capi$i # executable file
if [ $? -ne 0 ]; then
flag=0
fi
mv ./compile_log$i.txt $bind/compile_log$i.txt
elif [ ! -f "${bind}/player${i}.py" ]; then
flag=0
fi
mv ./compile_log$i.txt $bind/compile_log$i.txt
let "i++"
done
# HTML request to update status.
if [ $flag -eq 1 ]; then
curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"compiled"}' > ../mnt/curl_log.txt
curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"compiled"}' > $bind/curl_log.txt
else
curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"failed"}' > ../mnt/curl_log.txt
fi
curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"failed"}' > $bind/curl_log.txt
fi

+ 8
- 0
dependency/shell/cpp_output.sh View File

@@ -0,0 +1,8 @@
protoc Message2Clients.proto --cpp_out=.
protoc MessageType.proto --cpp_out=.
protoc Message2Server.proto --cpp_out=.
protoc Services.proto --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin`
protoc Services.proto --cpp_out=.
chmod -R 755 ./
mv -f ./*.h ../../CAPI/cpp/proto
mv -f ./*.cc ../../CAPI/cpp/proto

+ 22
- 0
dependency/shell/docker.sh View File

@@ -0,0 +1,22 @@

while getopts ':cr' 'OPT'; do
case ${OPT} in
'c') #传入cpp文件并编译
#echo v"$VERSION"
cd /usr/local/PlayerCode/CAPI/cpp
./compile.sh
exit
;;
'r') #运行
#NO_PROMPT='true'
cd /usr/local
./run.sh
;;
'l')

;;
'i')
INSTALL_VERSION="${OPTARG}"
;;
esac
done

+ 10
- 0
dependency/shell/generate_proto.sh View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash

python -m pip install -r requirements.txt

mkdir -p proto

python -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto MessageType.proto
python -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto Message2Clients.proto
python -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto Message2Server.proto
python -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto --grpc_python_out=./proto Services.proto

+ 50
- 0
dependency/shell/run.sh View File

@@ -0,0 +1,50 @@
#!/usr/local

python_dir=/usr/local/PlayerCode/CAPI/python/PyAPI
playback_dir=/usr/local/playback

nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --url $URL --token $TOKEN --fileName $playback_dir/video > $playback_dir/server.log &
server_pid=$!
sleep 5
for k in {1..2}
do
pushd /usr/local/team$k
if [ $k -eq 1 ]; then
for i in {1..4}
do
j=$((i - 1))
if [ -f "./player$i.py" ]; then
cp -f ./player$i.py $python_dir/AI.py
nice -0 python3 $python_dir/main.py -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log &
elif [ -f "./capi$i" ]; then
nice -0 ./capi$i -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log &
else
echo "ERROR. $i is not found."
fi
done
else
for i in {5..5}
do
j=$((i - 1))
if [ -f "./player$i.py" ]; then
cp -f ./player$i.py $python_dir/AI.py
nice -0 python3 $python_dir/main.py -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log &
elif [ -f "./capi$i" ]; then
nice -0 ./capi$i -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log &
else
echo "ERROR. $i is not found."
fi
done
fi
popd
done

ps -p $server_pid
while [ $? -eq 0 ]
do
sleep 1
ps -p $server_pid
done

touch $playback_dir/finish.lock
echo "Finish"

+ 10
- 4
docs/CAPI接口(cpp).md View File

@@ -13,6 +13,9 @@

#### 人物
- `std::future<bool> EndAllAction()`:可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的
- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次

#### 攻击
- `std::future<bool> Attack(double angleInRadian)`:`angleInRadian`为攻击方向
@@ -49,6 +52,7 @@
- `std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const` :返回所有可视捣蛋鬼的信息。
- `std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const` :返回所有可视道具的信息。
- `std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const` :返回所有可视子弹(攻击)的信息。
- `bool HaveView(int gridX, int gridY) const`:判断坐标是否可见

#### 查询特定位置物体的信息

@@ -56,7 +60,7 @@

- `THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY)` :返回某一位置场地种类信息。场地种类详见 structure.h 。
- `bool IsDoorOpen(int32_t cellX, int32_t cellY) const`:查询特定位置门是否开启,没有门也返回false
- 以下指令特定位置没有对应物品返回-1
- 以下指令,若查询物品当前在视野内,则返回最新进度;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。
- `int32_t GetChestProgress(int32_t cellX, int32_t cellY) const`:查询特定位置箱子开启进度
- `int32_t GetGateProgress(int32_t cellX, int32_t cellY) const`:查询特定位置校门开启进度
- `int32_t GetClassroomProgress(int32_t cellX, int32_t cellY) const`:查询特定位置教室作业完成进度
@@ -71,9 +75,9 @@
- `std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const`:返回整张地图的地形信息。可以写成类似`api.GetFullMap()[x][y]`,其中x为地图自上到下第几行,y为自左向右第几列,注意从0开始

### 辅助函数
`static inline int CellToGrid(int cell) noexcept`:将地图格数 cell 转换为绝对坐标grid。
`static inline int CellToGrid(int cell) noexcept`:将地图格数 cell 转换为绝对坐标 grid。

`static inline int GridToCell(int grid) noexcept`:将绝对坐标 grid 转换为地图格数cell。
`static inline int GridToCell(int grid) noexcept`:将绝对坐标 grid 转换为地图格数 cell。

下面为用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用
~~~c++
@@ -123,7 +127,7 @@
// 获取视野内可见的道具信息
[[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const = 0;

// 获取地图信息,视野外的地图统一为Land
// 获取地图信息
[[nodiscard]] virtual std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const = 0;
[[nodiscard]] virtual THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const = 0;

@@ -155,6 +159,8 @@
{
return grid / numOfGridPerCell;
}
[[nodiscard]] virtual bool HaveView(int gridX, int gridY) const = 0;

// 用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用



+ 25
- 16
docs/CAPI接口(python).md View File

@@ -8,7 +8,8 @@

#### 移动

- `def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为x轴,水平向右方向为y轴**因为移动过程中你会受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。
- `def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为 x 轴,水平向右方向为 y 轴**因为移动过程中你会受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。
- 5ms以内的移动指令会被禁止,你不应当使用过小的移动指令
- `def MoveRight(self, timeInMilliseconds: int) -> Future[bool]`即向右移动,`MoveLeft`、`MoveDown`、`MoveUp`同理

#### 使用技能
@@ -18,6 +19,9 @@
#### 人物

- `def EndAllAction(self) -> Future[bool]`:可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的
- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次

#### 攻击

@@ -31,8 +35,8 @@

#### 勉励与唤醒

- `def StartEncourageMate(self, mateID: int) -> Future[bool]`:勉励对应玩家ID的学生。
- `def StartRouseMate(self, mateID: int) -> Future[bool]`:唤醒对应玩家ID的沉迷的学生。
- `def StartEncourageMate(self, mateID: int) -> Future[bool]`:勉励对应玩家 ID 的学生。
- `def StartRouseMate(self, mateID: int) -> Future[bool]`:唤醒对应玩家 ID 的沉迷的学生。

#### 地图互动

@@ -51,16 +55,17 @@

#### 队内信息

- `std::future<bool> SendMessage(int64_t, std::string)`:给同队的队友发送消息。第一个参数指定发送的对象,第二个参数指定发送的内容,不得超过256字节。
- `bool HaveMessage()`:是否有队友发来的尚未接收的信息。
- `std::pair<int64_t, std::string> GetMessage()`:按照消息发送顺序获取来自队友的信息,第一个参数为发送该消息的PlayerID。
- `def GetMessage(self) -> Tuple[int, str]`:给同队的队友发送消息。第一个参数指定发送的对象,第二个参数指定发送的内容,不得超过256字节。
- `def HaveMessage(self) -> bool`:是否有队友发来的尚未接收的信息。
- `def GetMessage(self) -> Tuple[int, str]`:按照消息发送顺序获取来自队友的信息,第一个参数为发送该消息的PlayerID。

#### 查询可视范围内的信息

- `std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const` :对于学生,返回所有学生的信息;对于捣蛋鬼,返回可视学生的信息。
- `std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const` :返回所有可视捣蛋鬼的信息。
- `std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const` :返回所有可视道具的信息。
- `std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const` :返回所有可视子弹(攻击)的信息。
- `def GetStudents(self) -> List[THUAI6.Student]` :对于学生,返回所有学生的信息;对于捣蛋鬼,返回可视学生的信息。
- `def GetTrickers(self) -> List[THUAI6.Tricker]` :返回所有可视捣蛋鬼的信息。
- `def GetProps(self) -> List[THUAI6.Prop]` :返回所有可视道具的信息。
- `def GetBullets(self) -> List[THUAI6.Bullet]` :返回所有可视子弹(攻击)的信息。
- `def HaveView(self, gridX: int, gridY: int) -> bool`:判断坐标是否可见

#### 查询特定位置物体的信息

@@ -68,13 +73,13 @@

- `def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType` :返回某一位置场地种类信息。场地种类详见 structure.h 。
- `def IsDoorOpen(self, cellX: int, cellY: int) -> bool`:查询特定位置门是否开启,没有门也返回false
- 以下指令特定位置没有对应物品返回-1
- 以下指令,若查询物品当前在视野内,则返回最新进度;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。
- `def GetChestProgress(self, cellX: int, cellY: int) -> int`:查询特定位置箱子开启进度
- `def GetGateProgress(self, cellX: int, cellY: int) -> int`:查询特定位置校门开启进度
- `def GetClassroomProgress(self, cellX: int, cellY: int) -> int`:查询特定位置教室作业完成进度
- `def GetDoorProgress(self, cellX: int, cellY: int) -> int`:查询特定位置门开启状态
- `def GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState`::查询特定位置隐藏校门状态,没有隐藏校门返回THUAI6::HiddenGateState::Null

#### 其他

@@ -86,11 +91,11 @@

### 辅助函数

`def CellToGrid(cell: int) -> int`:将地图格数 cell 转换为绝对坐标grid。
`def CellToGrid(cell: int) -> int`:将地图格数 cell 转换为绝对坐标 grid。

`def GridToCell(grid: int) -> int`:将绝对坐标 grid 转换为地图格数cell。
`def GridToCell(grid: int) -> int`:将绝对坐标 grid 转换为地图格数 cell。

下面为用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用
下面为用于DEBUG的输出函数,选手仅在开启 Debug 模式的情况下可以使用

~~~python
def Print(self, cont: str) -> None:
@@ -261,6 +266,10 @@ class IAPI(metaclass=ABCMeta):
@abstractmethod
def GetGameInfo(self) -> THUAI6.GameInfo:
pass
@abstractmethod
def HaveView(self, gridX: int, gridY: int) -> bool
pass

# 用于DEBUG的输出函数,仅在DEBUG模式下有效

@@ -317,4 +326,4 @@ class ITrickerAPI(IAPI, metaclass=ABCMeta):
@abstractmethod
def GetSelfInfo(self) -> THUAI6.Tricker:
pass
~~~
~~~

+ 14
- 17
docs/GameRules.md View File

@@ -1,5 +1,5 @@
# 规则
V5.1
V5.3
- [规则](#规则)
- [简则](#简则)
- [地图](#地图)
@@ -140,14 +140,14 @@ $$

### 信息相关
- Bgm (在structures.h/.py中的student类或Tricker类中作为其属性)
1. 不详的感觉(dangerAlert):如果捣蛋鬼进入(学生的警戒半径/捣蛋鬼的隐蔽度)的距离,则学生的dangerAlert=(int)(警戒半径/二者距离)
2. 期待搞事的感觉(trickDesire):如果有学生进入(捣蛋鬼的警戒半径/学生的隐蔽度)的距离,则捣蛋鬼trickDesire=(int)(警戒半径/可被发觉的最近的学生距离)
3. 学习的声音(classVolume): 警戒半径内有人学习时,捣蛋鬼classVolume=(int)((警戒半径x学习进度百分比)/二者距离)
1. 不详的感觉(dangerAlert):如果捣蛋鬼进入(学生的警戒半径/捣蛋鬼的隐蔽度)的距离,则学生的dangerAlert=(警戒半径/二者距离)
2. 期待搞事的感觉(trickDesire):如果有学生进入(捣蛋鬼的警戒半径/学生的隐蔽度)的距离,则捣蛋鬼trickDesire=(警戒半径/可被发觉的最近的学生距离)
3. 学习的声音(classVolume): 警戒半径内有人学习时,捣蛋鬼classVolume=((警戒半径x学习进度百分比)/二者距离)
- 可以向每一个队友发送不超过256字节的信息

### 可视范围
- 小于视野半径
- 对于在草地中的物体,物体中心与玩家中心连线上均为草地方可见
- 对于中心在草地中的物体,物体中心与玩家中心连线上均为草地方可见
- 不在草地的物体,物体中心与玩家中心连线上无墙即可见

### 道具
@@ -194,9 +194,9 @@ $$
| 隐蔽度 | 1.5 | 1 | 0.8 | 0.75|
| 警戒范围 | 22,100 | 17000 | 15300 | 17000
| 视野范围 | 15600 | 13000 | 13000 | 14300|
| 开锁门速度 | 4000 | 4000 | 4000 |4000 |
| 翻窗速度 | 2540 | 2540 | 2794 | 2540|
| 翻箱速度 | 1000 | 1100 | 1000 |1000|
| 开锁门速度/ms | 4000 | 4000 | 4000 |4000 |
| 翻窗速度/s | 2540 | 2540 | 2794 | 2540|
| 翻箱速度/ms | 1000 | 1100 | 1000 |1000|

#### Assassin
- 普通攻击为 CommonAttackOfGhost
@@ -244,17 +244,17 @@ $$

| 学生职业 | 教师Teacher | 健身狂Athlete | 学霸StraightAStudent | 开心果Sunshine |
| :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- |
| 移动速度 | 2700 | 3150 | 2880 | 3000 |
| 移动速度/s | 2700 | 3150 | 2880 | 3000 |
| 最大毅力值 | 30000000 | 3000000 | 3300000 | 3200000 |
| 最大沉迷度 | 600000 | 54,000 | 78,000 | 66,000 |
| 学习一科速度 | 0 | 73 | 135 | 123 |
| 勉励速度 | 80 | 90 | 100 | 120 |
| 勉励速度/ms | 80 | 90 | 100 | 120 |
| 隐蔽度 | 0.5 | 0.9 | 0.9 | 0.8 |
| 警戒范围 | 7500 | 15000 | 13,500 | 15000 |
| 视野范围 | 9,000 | 11000 | 9,000 | 10000 |
| 开锁门速度 | 4000 | 4000 | 4000 | 2800 |
| 翻窗速度 | 1270 | 3048 | 2116 | 2540 |
| 翻箱速度 | 1000 | 1000 | 1000 | 900 |
| 开锁门速度/ms | 4000 | 4000 | 4000 | 2800 |
| 翻窗速度/ms | 1270 | 3048 | 2116 | 2540 |
| 翻箱速度/ms | 1000 | 1000 | 1000 | 900 |

#### 运动员
- 主动技能
@@ -308,18 +308,15 @@ $$
- 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。

### 人物
- EndAllAction()及Move指令调用数总和一帧内不超过10次
- 眩晕状态中的玩家不能再次被眩晕

### 初始状态
- 玩家出生点固定且一定为空地
- 初赛玩家出生点固定且一定为空地

### 道具
- 使用钥匙相当于销毁

### 交互
- 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的
- 被唤醒或被勉励不属于交互状态,翻窗属于交互状态

### 学习与毕业


+ 8
- 11
docs/QandA.md View File

@@ -24,12 +24,6 @@ Q: 怎么开始游戏?
A:
需要确保学生阵营和捣蛋鬼阵营的人数都达到Server.cmd中设定的值。人数不足也可以打开WPF,参考使用文档,修改RunGUIClient.cmd的参数,然后运行RunGUIClient.cmd,这样可以通过WPF运行部分客户端,来达到人数限制。


Q: Mac怎么用?

A:
安装Windows虚拟机
try
## C++

Q:显示API项目已卸载
@@ -37,7 +31,7 @@ Q:显示API项目已卸载

A:可能是没有安装C++

Q:CAPI.sln编译不通过
Q:CAPI.sln编译不通过(第一种)
情况1:
![std_find_trivial](https://raw.githubusercontent.com/shangfengh/THUAI6/new/resource/std_find_trivial.jpg)
情况2:
@@ -52,7 +46,7 @@ A:
![项目属性](https://raw.githubusercontent.com/shangfengh/THUAI6/new/resource/项目属性.png)
确保上图项目属性中平台工具集在 v143,C++17 标准

Q:CAPI编译不通过
Q:CAPI编译不通过(第二种)
![lib](https://raw.githubusercontent.com/shangfengh/THUAI6/new/resource/lib.png)

A:查看`.\win\CAPI\cpp\`文件夹下是否有`lib`文件夹,没有则https://cloud.tsinghua.edu.cn/d/6972138f641d4e81a446/ 下载并复制粘贴
@@ -68,12 +62,15 @@ A:
- 可能措施1.
首先保证Python版本在3.9及以上
- 可能措施2. 更换为国内镜像源
在终端输入
`pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple`
在终端输入 `pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple`
- 可能措施3. 更新pip
`python -m pip install --upgrade pip` (pip 版本最好为23.1)

## 比赛相关
Q:职业数值会修改吗?

A:初赛结束会调数值及机制,增加新角色
A:初赛结束会调数值及机制,增加新角色

Q:初赛后会修改什么呢?

A:技能冷却时间等属性设为不可见;出生点随机性或可选性;增强教师等职业,削弱职业;规范Debug信息

+ 2
- 1
docs/Tool_tutorial.md View File

@@ -65,9 +65,10 @@ int main()
### 线程睡眠



由于移动过程中会阻塞人物角色,因此玩家可能要在移动后让线程休眠一段时间,直到移动结束。C++ 标准库中使线程休眠需要包含头文件:`#include <thread>`。示例用法:

我们推荐小步移动,不太建议玩家使用线程睡眠超过一帧

```cpp
std::this_thread::sleep_for(std::chrono::milliseconds(20)); // 休眠 20 毫秒
std::this_thread::sleep_for(std::chrono::seconds(2)); // 休眠 2 秒


+ 5
- 0
docs/使用文档.md View File

@@ -6,6 +6,11 @@
## 不应当使用Windows特性的库,不建议使用.h结尾的库,建议使用标准库
- 不要使用conio.h,Windows.h

## 请注意下载器不更新AI.py,AI.cpp和脚本
- 最新版AI.cpp和AI.py
![AIcpp](https://raw.githubusercontent.com/shangfengh/THUAI6/new/resource/AIcpp.png)
![AIpy](https://raw.githubusercontent.com/shangfengh/THUAI6/new/resource/AIpy.png)

## C++接口使用说明

- Windows:先查看`.\win\CAPI\cpp\`文件夹下是否有`lib`文件夹,没有则https://cloud.tsinghua.edu.cn/d/6972138f641d4e81a446/ 下载并复制粘贴


+ 1
- 0
installer/Installer/Model.cs View File

@@ -506,6 +506,7 @@ namespace Downloader
string secretId = "***"; //"云 API 密钥 SecretId";
string secretKey = "***"; //"云 API 密钥 SecretKey";


long durationSecond = 1000; // 每次请求签名有效时长,单位为秒
QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider(
secretId, secretKey, durationSecond


+ 1
- 1
logic/Client/Client.csproj View File

@@ -19,7 +19,7 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
</ItemGroup>


+ 33
- 6
logic/Client/MainWindow.xaml.cs View File

@@ -283,6 +283,24 @@ namespace Client
}

private void ZoomMap()
{
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 50; j++)
{
if (mapPatches[i, j] != null && (mapPatches[i, j].Width != UpperLayerOfMap.ActualWidth / 50 || mapPatches[i, j].Height != UpperLayerOfMap.ActualHeight / 50))
{
mapPatches[i, j].Width = UpperLayerOfMap.ActualWidth / 50;
mapPatches[i, j].Height = UpperLayerOfMap.ActualHeight / 50;
mapPatches[i, j].HorizontalAlignment = HorizontalAlignment.Left;
mapPatches[i, j].VerticalAlignment = VerticalAlignment.Top;
mapPatches[i, j].Margin = new Thickness(UpperLayerOfMap.ActualWidth / 50 * j, UpperLayerOfMap.ActualHeight / 50 * i, 0, 0);
}
}
}
}

private void ZoomMapAtFirst()
{
for (int i = 0; i < 50; i++)
{
@@ -299,6 +317,7 @@ namespace Client
}
}
}

private void DrawMap()
{
for (int i = 0; i < defaultMap.GetLength(0); i++)
@@ -560,7 +579,6 @@ namespace Client
}
}

//待修改
private bool CanSee(MessageOfStudent msg)
{
if (msg.PlayerState == PlayerState.Quit || msg.PlayerState == PlayerState.Graduated)
@@ -750,7 +768,10 @@ namespace Client
StatusBarsOfCircumstance.SetValue(data, gateOpened, isEmergencyDrawed, isEmergencyOpened, playerID, isPlaybackMode);
}
if (!hasDrawed && mapFlag)
{
DrawMap();
ZoomMapAtFirst();
}
foreach (var data in listOfHuman)
{
if (data.StudentType != StudentType.Robot)
@@ -1037,7 +1058,6 @@ namespace Client
}
//}
ZoomMap();

}
catch (Exception exc)
{
@@ -1051,7 +1071,7 @@ namespace Client
}
}

// 键盘控制,未完善
// 键盘控制
private void KeyBoardControl(object sender, KeyEventArgs e)
{
if (!isPlaybackMode && !isSpectatorMode)
@@ -1262,7 +1282,6 @@ namespace Client
}
}

// 之后需要修改,现在只具有修改按钮形状的功能,并不能实现暂停/继续
private void ClickToPauseOrContinue(object sender, RoutedEventArgs e)
{
if (!isClientStocked)
@@ -1284,6 +1303,7 @@ namespace Client
}
}
}

// 未复现
private void ClickToConnect(object sender, RoutedEventArgs e)
{
@@ -1297,24 +1317,28 @@ namespace Client
else
WindowState = WindowState.Normal;
}

private void ClickToClose(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}

private void ClickToMinimize(object sender, RoutedEventArgs e)
{
WindowState = WindowState.Minimized;
}

private void DragWindow(object sender, RoutedEventArgs e)
{
DragMove();
}

// 寻求帮助、访问EESAST(部分功能未复
// 寻求帮助、访问EESAST(部分功能未复
private void ClickForHelp(object sender, RoutedEventArgs e)
{
PleaseWait();
}

private void ClickToVisitEESAST(object sender, RoutedEventArgs e)
{
try
@@ -1328,12 +1352,13 @@ namespace Client
}
}

// 配置连接(未复)、我的AI(THUAI5未实现)、获取更新、天梯信息(可能需要网站协助)
// 配置连接(未复)、我的AI(THUAI5未实现)、获取更新、天梯信息(可能需要网站协助)
private void ClickToSetConnect(object sender, RoutedEventArgs e)
{
// ConnectRegister crg = new();
// crg.Show();
}

private void ClickToEnterVS(object sender, RoutedEventArgs e)
{
// try
@@ -1354,10 +1379,12 @@ namespace Client
// }
PleaseWait();
}

private void ClickForUpdate(object sender, RoutedEventArgs e)
{
PleaseWait();
}

private void ClickToCheckLadder(object sender, RoutedEventArgs e)
{
PleaseWait();


+ 1
- 1
logic/ClientTest/ClientTest.csproj View File

@@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
</ItemGroup>


+ 2
- 2
logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs View File

@@ -130,7 +130,7 @@ namespace GameClass.GameObj

public override bool CanAttack(GameObj target)
{
return XY.Distance(this.Position, target.Position) <= BulletBombRange;
return XY.DistanceFloor3(this.Position, target.Position) <= BulletBombRange;
}
public override bool CanBeBombed(GameObjType gameObjType)
{
@@ -176,7 +176,7 @@ namespace GameClass.GameObj

public override bool CanAttack(GameObj target)
{
return XY.Distance(this.Position, target.Position) <= BulletBombRange;
return XY.DistanceFloor3(this.Position, target.Position) <= BulletBombRange;
}
public override bool CanBeBombed(GameObjType gameObjType)
{


+ 1
- 0
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -36,6 +36,7 @@ namespace GameClass.GameObj

public override bool IgnoreCollideExecutor(IGameObj targetObj)
{
if (targetObj == Parent && CanMove) return true;
if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet)
return true;
return false;


+ 1
- 1
logic/GameClass/GameObj/Character/Character.cs View File

@@ -594,7 +594,7 @@ namespace GameClass.GameObj
{
return true;
}
if (targetObj.Type == GameObjType.Character && XY.Distance(targetObj.Position, this.Position) < this.Radius + targetObj.Radius)
if (targetObj.Type == GameObjType.Character && XY.DistanceCeil3(targetObj.Position, this.Position) < this.Radius + targetObj.Radius - GameData.adjustLength)
return true;
return false;
}


+ 1
- 1
logic/GameClass/GameObj/Map/Generator.cs View File

@@ -46,7 +46,7 @@ namespace GameClass.GameObj

public bool Repair(int addDegree, Character character)
{
if (DegreeOfRepair == GameData.degreeOfFixedGenerator) return true;
if (DegreeOfRepair == GameData.degreeOfFixedGenerator) return false;
int orgDegreeOfRepair = degreeOfRepair;
DegreeOfRepair += addDegree;
if (DegreeOfRepair > orgDegreeOfRepair)


+ 5
- 4
logic/GameClass/GameObj/Map/Map.cs View File

@@ -203,7 +203,7 @@ namespace GameClass.GameObj
}
public bool Remove(GameObj gameObj)
{
bool flag = false;
GameObj? ToDel = null;
GameObjLockDict[gameObj.Type].EnterWriteLock();
try
{
@@ -211,8 +211,7 @@ namespace GameClass.GameObj
{
if (gameObj.ID == obj.ID)
{
GameObjDict[gameObj.Type].Remove(obj);
flag = true;
ToDel = obj;
break;
}
}
@@ -221,7 +220,9 @@ namespace GameClass.GameObj
{
GameObjLockDict[gameObj.Type].ExitWriteLock();
}
return flag;
if (ToDel == null) return false;
GameObjDict[gameObj.Type].Remove(ToDel);
return true;
}
public bool RemoveJustFromMap(GameObj gameObj)
{


+ 3
- 3
logic/GameEngine/CollisionChecker.cs View File

@@ -75,7 +75,7 @@ namespace GameEngine
// if (obj.WillCollideWith(square, obj.Position))
// tmpMax = 0;
// else tmpMax =
// Math.Abs(XYPosition.Distance(obj.Position, square.Position) - obj.Radius -
// Math.Abs(XYPosition.DistanceFloor3(obj.Position, square.Position) - obj.Radius -
// (square.Radius / Math.Min(Math.Abs(Math.Cos(angle)), Math.Abs(Math.Sin(angle)))));
// return tmpMax;
// }
@@ -144,7 +144,7 @@ namespace GameEngine
case ShapeType.Circle:
{
// 计算两者之间的距离
double mod = XY.Distance(listObj.Position, obj.Position);
double mod = XY.DistanceFloor3(listObj.Position, obj.Position);
int orgDeltaX = listObj.Position.x - obj.Position.x;
int orgDeltaY = listObj.Position.y - obj.Position.y;

@@ -181,7 +181,7 @@ namespace GameEngine
while (left < right - 1)
{
int mid = (right - left) / 2 + left;
if (obj.WillCollideWith(listObj, obj.Position + new XY((int)(mid * Math.Cos(moveVec.Angle())), (int)(mid * Math.Sin(moveVec.Angle())))))
if (obj.WillCollideWith(listObj, obj.Position + new XY(moveVec, mid)))
{
right = mid;
}


+ 108
- 83
logic/GameEngine/MoveEngine.cs View File

@@ -1,5 +1,4 @@
using System;
using System.Numerics;
using System.Threading;
using Preparation.Interface;
using Preparation.Utility;
@@ -88,110 +87,136 @@ namespace GameEngine
obj.IsMoving = true;

double moveVecLength = 0.0;
XY res = new XY(direction, moveVecLength);
XY res = new(direction, moveVecLength);
double deltaLen = moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res))); // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null;
bool isDestroyed = false;
new FrameRateTaskExecutor<int>(
() => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting && obj.IsMoving,
() =>

bool flag; // 循环标志
do
{
flag = false;
collisionObj = collisionChecker.CheckCollision(obj, obj.Position);
if (collisionObj == null)
break;

switch (OnCollision(obj, collisionObj, res))
{
moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
res = new XY(direction, moveVecLength);
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
break;
case AfterCollision.MoveMax:
break;
}
} while (flag);

// 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志
do
if (!isDestroyed)
{
new FrameRateTaskExecutor<int>(
() => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting && obj.IsMoving,
() =>
{
flag = false;
collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
if (collisionObj == null)
break;
moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
res = new XY(direction, moveVecLength);

switch (OnCollision(obj, collisionObj, res))
// 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志
do
{
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
return false;
case AfterCollision.MoveMax:
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
flag = false;
collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
if (collisionObj == null)
break;
}
} while (flag);

deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res)));
switch (OnCollision(obj, collisionObj, res))
{
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
return false;
case AfterCollision.MoveMax:
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
}
} while (flag);

return true;
},
GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
() =>
{
int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
bool flag;
do
deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res)));

return true;
},
GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
() =>
{
flag = false;
if (!isDestroyed)
int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
bool flag;
do
{
moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
{
obj.MovingSetPos(res, GetPlaceType(obj.Position + res));
}
else
flag = false;
if (!isDestroyed)
{
switch (OnCollision(obj, collisionObj, res))
moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
{
obj.MovingSetPos(res, GetPlaceType(obj.Position + res));
}
else
{
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
break;
case AfterCollision.MoveMax:
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
switch (OnCollision(obj, collisionObj, res))
{
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
break;
case AfterCollision.MoveMax:
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
}
}
}
} while (flag);
if (leftTime > 0 && obj.IsMoving)
{
Thread.Sleep(leftTime); // 多移动的在这里补回来
}
} while (flag);
if (leftTime > 0 && obj.IsMoving)
{
Thread.Sleep(leftTime); // 多移动的在这里补回来
}
lock (obj.MoveLock)
obj.IsMoving = false; // 结束移动
EndMove(obj);
return 0;
},
maxTotalDuration: moveTime
)
{
AllowTimeExceed = true,
MaxTolerantTimeExceedCount = ulong.MaxValue,
TimeExceedAction = b =>
lock (obj.MoveLock)
obj.IsMoving = false; // 结束移动
EndMove(obj);
return 0;
},
maxTotalDuration: moveTime
)
{
if (b)
Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");
AllowTimeExceed = true,
MaxTolerantTimeExceedCount = ulong.MaxValue,
TimeExceedAction = b =>
{
if (b)
Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");

#if DEBUG
else
{
Console.WriteLine("Debug info: Object moving time exceed for once.");
}
else
{
Console.WriteLine("Debug info: Object moving time exceed for once.");
}
#endif
}
}.Start();
}
}.Start();
}
}
).Start();
}


+ 4
- 6
logic/Gaming/ActionManager.cs View File

@@ -1,6 +1,4 @@
using System;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;
using GameClass.GameObj;
using GameEngine;
@@ -37,6 +35,7 @@ namespace Gaming
}
public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
{
if (moveTimeInMilliseconds < 5) return false;
if (!playerToMove.Commandable() || !TryToStop()) return false;
characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
@@ -81,20 +80,19 @@ namespace Gaming
loopToDo: () =>
{
if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player))
{
characterManager.SetPlayerState(player);
gameMap.NumOfRepairedGenerators++;
}
if (generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator)
characterManager.SetPlayerState(player);
},
timeInterval: GameData.frameDuration,
finallyReturn: () => 0
)
.Start();
--generatorForFix.NumOfFixing;
}

)
{ IsBackground = true }.Start();
--generatorForFix.NumOfFixing;

return true;
}


+ 6
- 3
logic/Gaming/AttackManager.cs View File

@@ -34,6 +34,7 @@ namespace Gaming
Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64);
if (obj.CanMove && ((Bullet)obj).TypeOfBullet != BulletType.JumpyDumpty)
BulletBomb((Bullet)obj, null);
obj.CanMove = false;
}
);
this.characterManager = characterManager;
@@ -171,17 +172,19 @@ namespace Gaming
return false;
Debugger.Output(player, player.CharacterType.ToString() + "Attack in " + player.BulletOfPlayer.ToString());

Debugger.Output(player, player.Position.ToString() + " " + player.Radius.ToString() + " " + BulletFactory.BulletRadius(player.BulletOfPlayer).ToString());
XY res = player.Position + new XY // 子弹紧贴人物生成。
(
(int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle)),
(int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle))
(int)(Math.Abs((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle))) * ((Math.Cos(angle) > 0) ? 1 : -1),
(int)(Math.Abs((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle))) * ((Math.Sin(angle) > 0) ? 1 : -1)
);

Bullet? bullet = player.Attack(res, gameMap.GetPlaceType(res));

if (bullet != null)
{
Debugger.Output(player, "Attack in " + bullet.ToString());
player.FacingDirection = new(angle, bullet.BulletAttackRange);
Debugger.Output(bullet, "Attack in " + bullet.Position.ToString());
bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0;
bullet.CanMove = true;
gameMap.Add(bullet);


+ 10
- 13
logic/Gaming/CharacterManager .cs View File

@@ -1,13 +1,9 @@
using System;
using System.Threading;
using System.Collections.Generic;
using GameClass.GameObj;
using Preparation.Utility;
using GameEngine;
using Preparation.Interface;
using Timothy.FrameRateTask;
using System.Numerics;
using System.Timers;

namespace Gaming
{
@@ -135,10 +131,10 @@ namespace Gaming
double bgmVolume = 0;
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
if (!person.IsGhost() && XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
if (!person.IsGhost() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
{
if ((double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position) > bgmVolume)
bgmVolume = newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position);
if ((double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position) > bgmVolume)
bgmVolume = newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position);
}
}
newPlayer.AddBgm(BgmType.StudentIsApproaching, bgmVolume);
@@ -151,11 +147,11 @@ namespace Gaming
{
if (!noise)
{
if (XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position));
if (XY.DistanceFloor3(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position));
else newPlayer.AddBgm(BgmType.GhostIsComing, 0);
}
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.Distance(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
{
TimePinningDown += GameData.checkInterval;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);
@@ -180,10 +176,10 @@ namespace Gaming
double bgmVolume = 0;
foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator])
{
if (XY.Distance(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius)
if (XY.DistanceFloor3(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius)
{
if (generator.NumOfFixing > 0 && (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position) > bgmVolume)
bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position);
if (generator.NumOfFixing > 0 && (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.DistanceFloor3(newPlayer.Position, generator.Position) > bgmVolume)
bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.DistanceFloor3(newPlayer.Position, generator.Position);
}
}
newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, bgmVolume);
@@ -383,6 +379,7 @@ namespace Gaming
#if DEBUG
Debugger.Output(player, "die.");
#endif
if (player.PlayerState == PlayerStateType.Deceased) return;
player.RemoveFromGame(PlayerStateType.Deceased);

for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++)


+ 2
- 2
logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs View File

@@ -51,7 +51,7 @@ namespace Gaming
{
if (!person.IsGhost() && player.CharacterType != CharacterType.Robot && !person.NoHp())
{
double dis = XY.Distance(person.Position, player.Position);
double dis = XY.DistanceFloor3(person.Position, player.Position);
if (dis >= player.AlertnessRadius)
{
person.AddMoveSpeed(GameData.checkIntervalWhenShowTime, dis / player.AlertnessRadius);
@@ -185,7 +185,7 @@ namespace Gaming
{
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{
if (!character.IsGhost() && !character.NoHp() && XY.Distance(character.Position, player.Position) <= player.ViewRange)
if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange)
{
if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl))
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl));


+ 1
- 1
logic/Preparation/Interface/IMoveable.cs View File

@@ -24,7 +24,7 @@ namespace Preparation.Interface

if (targetObj.Shape == ShapeType.Circle)
{
return XY.Distance(nextPos, targetObj.Position) < targetObj.Radius + Radius;
return XY.DistanceCeil3(nextPos, targetObj.Position) < targetObj.Radius + Radius;
}
else // Square
{


+ 1
- 1
logic/Preparation/Preparation.csproj View File

@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
</ItemGroup>

<ItemGroup>


+ 2
- 1
logic/Preparation/Utility/GameData.cs View File

@@ -9,7 +9,8 @@ namespace Preparation.Utility
#region 基本常数
public const int numOfStepPerSecond = 20; // 每秒行走的步数

public const int tolerancesLength = 10;
public const int tolerancesLength = 3;
public const int adjustLength = 3;

public const int frameDuration = 50; // 每帧时长
public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长


+ 12
- 2
logic/Preparation/Utility/XY.cs View File

@@ -62,14 +62,24 @@ namespace Preparation.Utility
{
return v1.x != v2.x || v1.y != v2.y;
}
public static double Distance(XY p1, XY p2)

public static double DistanceFloor3(XY p1, XY p2)
{
long c = (((long)(p1.x - p2.x) * (p1.x - p2.x)) + ((long)(p1.y - p2.y) * (p1.y - p2.y))) * 1000000;
long t = c / 2 + 1;
while (t * t > c || (t + 1) * (t + 1) < c)
while (t * t > c || (t + 1) * (t + 1) <= c)
t = (c / t + t) / 2;
return (double)t / 1000.0;
}
public static double DistanceCeil3(XY p1, XY p2)
{
long c = (((long)(p1.x - p2.x) * (p1.x - p2.x)) + ((long)(p1.y - p2.y) * (p1.y - p2.y))) * 1000000;
long t = c / 2 + 1;
while (t * t > c || (t + 1) * (t + 1) <= c)
t = (c / t + t) / 2;
if (t * t == c) return (double)t / 1000.0;
else return (double)(t + 1) / 1000.0;
}
public double Length()
{
return Math.Sqrt(((long)x * x) + ((long)y * y));


+ 3
- 1
logic/README.md View File

@@ -47,4 +47,6 @@

## 开发人员

- ......(自己加)
- 黄淞:底层逻辑
- 游笑权:Client
- 高思研:Server

+ 1
- 1
logic/Server/Server.csproj View File

@@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
<PackageReference Include="Grpc.Tools" Version="2.53.0">


+ 1
- 1
logic/cmd/playback.cmd View File

@@ -1,5 +1,5 @@
@echo off

start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --playbackFile .\test.thuaipb --playbackSpeed 2
start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --playbackFile .\longtest.thuaipb --playbackSpeed 4

ping -n 2 127.0.0.1 > NUL

+ 1
- 1
playback/Playback/Playback.csproj View File

@@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
</ItemGroup>

<ItemGroup>


BIN
resource/AIcpp.png View File

Before After
Width: 1413  |  Height: 1253  |  Size: 57 kB

BIN
resource/AIpy.png View File

Before After
Width: 1453  |  Height: 1241  |  Size: 57 kB

Loading…
Cancel
Save