| @@ -0,0 +1,995 @@ | |||||
| #include <array> | |||||
| #include <cmath> | |||||
| #include <queue> | |||||
| #include <sstream> | |||||
| #include <stack> | |||||
| #include <thread> | |||||
| #include <utility> | |||||
| #include <vector> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #define PI 3.14159265358979323846 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| struct CellPos { | |||||
| int x, y; | |||||
| }; | |||||
| struct GridPos { | |||||
| int x, y; | |||||
| }; | |||||
| class Action; | |||||
| class BFS; | |||||
| class Communication; | |||||
| class Escape; | |||||
| class GameState; | |||||
| class Action { | |||||
| public: | |||||
| enum class Direction { | |||||
| null, up, down, left, right, | |||||
| up_left, up_right, down_left, down_right | |||||
| }; //移动方式 | |||||
| static const std::vector <Direction> allMove; //所有的移动方式 | |||||
| static int isAtCellFalseCnt; | |||||
| static int classNo; //要去的教室位置 | |||||
| static CellPos lastNextPos; | |||||
| static CellPos lastDesPos; | |||||
| static Direction lastMove; | |||||
| static CellPos PredictPos; | |||||
| static bool isValidMove(CellPos judgePos, Direction comeDir, GameState state, IStudentAPI& api); | |||||
| static bool isValidMove(CellPos judgePos, Direction comeDir, GameState state, ITrickerAPI& api); | |||||
| static double getAngle(GridPos grid1, GridPos grid2); //计算两点间方位角 | |||||
| static void Move(CellPos des, IStudentAPI& api); | |||||
| static void Move(CellPos des, ITrickerAPI& api); | |||||
| static void Move(Direction directon, IStudentAPI& api); | |||||
| static void Move(Direction direction, ITrickerAPI& api); | |||||
| static CellPos directionToPos(CellPos nowPos, Direction direction); //根据当前坐标和移动方式计算出下一个坐标 | |||||
| static Direction posToDirection(CellPos nowPos, CellPos nextPos); //目标点的坐标转化为移动方式 | |||||
| }; | |||||
| class BFS { | |||||
| public: | |||||
| static bool vis[Constants::rows][Constants::cols]; | |||||
| static CellPos now; | |||||
| static CellPos nxt; | |||||
| static CellPos pre[Constants::rows][Constants::cols]; | |||||
| static std::pair<CellPos, int> bfs(CellPos start, CellPos destination, ITrickerAPI& api); | |||||
| static std::pair<std::pair<CellPos, CellPos>, int> bfs(CellPos start, THUAI6::PlaceType destination, ITrickerAPI& api); //return pair<next_point,destination> | |||||
| }; | |||||
| class Communication { | |||||
| public: | |||||
| static void receiveMessage(IStudentAPI& api, int64_t myID); | |||||
| static void sendClassroomPos(IStudentAPI& api, int64_t myID, CellPos classroomPos); | |||||
| static void sendTrickerPos(IStudentAPI& api, int64_t myID); | |||||
| /* | |||||
| 通信协议:AA BB CC DD | |||||
| (AA, BB)为学完的教室的坐标 | |||||
| (CC, DD)为看到的捣蛋鬼的坐标 | |||||
| 若AA, BB均为-1,则忽略教室坐标,只读捣蛋鬼坐标 | |||||
| 若CC, DD均为-1,则忽略捣蛋鬼坐标,只读教室坐标 | |||||
| */ | |||||
| }; | |||||
| class Escape { | |||||
| public: | |||||
| //static std::pair<CellPos, Action::Direction> minimax(CellPos start, THUAI6::PlaceType destination); //规避算法 | |||||
| }; | |||||
| class GameState { | |||||
| public: | |||||
| static int readCnt; | |||||
| static int clsFinishCnt; | |||||
| THUAI6::PlaceType gameMap[Constants::rows][Constants::cols]; //存图信息 | |||||
| std::vector <CellPos> ClassRoom; | |||||
| std::vector <CellPos> Gate; | |||||
| std::vector <CellPos> HiddenGate; | |||||
| std::vector <CellPos> Window; | |||||
| std::vector <CellPos> Door3; | |||||
| std::vector <CellPos> Door5; | |||||
| std::vector <CellPos> Door6; | |||||
| std::vector <CellPos> Chest; | |||||
| std::vector <std::pair<GridPos,int>> ValidMoveBuffer; | |||||
| CellPos studentPos[4]; //四个学生的坐标 | |||||
| GridPos studentGrid[4]; | |||||
| CellPos trickerPos; //捣蛋鬼的坐标 | |||||
| bool isAtCell(GridPos myPos, GridPos cellPos) const; //判断人物是否完全在格点内 | |||||
| bool isAtPosition(CellPos myPos, CellPos pos) const; | |||||
| bool isAtPos(CellPos myPos, CellPos pos) const; //判断character坐标与pos坐标是否在一个九宫格内 | |||||
| bool isAttackable(GridPos trickerPos, GridPos studentPos) const; //判断是否可以攻击 | |||||
| bool isClassroomFinished(CellPos classroomPos, IStudentAPI& studentapi) const; //判断教室是否学完了 | |||||
| bool isDoor(CellPos pos) const; | |||||
| bool isGateFinished(CellPos gatePos, IStudentAPI& api) const; //判断大门是否开了 | |||||
| bool isTrickerVisible() const; //判断tricker是否可见 | |||||
| bool isType(CellPos pos, THUAI6::PlaceType type) const; | |||||
| bool canReach(CellPos pos, ITrickerAPI& api) const; | |||||
| bool readyToGraduate() const; //判断当前局面是否可毕业 | |||||
| double gridDistance(GridPos grid1, GridPos grid2) const; //计算两点间以grid计的距离 | |||||
| void deleteStudiedClassroom(CellPos classroomPos); //把学完的教室删除,变成墙 | |||||
| void getMap(IStudentAPI& api); //获取地图格点信息 | |||||
| void getMap(ITrickerAPI& api); //获取地图格点信息 | |||||
| std::pair<std::pair<bool, std::pair<CellPos, GridPos>>, std::pair<double, double>> getClosestStudent(GridPos trickerPos, ITrickerAPI& api); //找到视野中最近的学生 | |||||
| //CellPos isGrassAround(CellPos, IStudentAPI& api) const; | |||||
| }; | |||||
| const std::vector <Action::Direction> Action::allMove = { | |||||
| Direction::up, | |||||
| Direction::down, | |||||
| Direction::left, | |||||
| Direction::right, | |||||
| Direction::up_left, | |||||
| Direction::up_right, | |||||
| Direction::down_left, | |||||
| Direction::down_right | |||||
| }; | |||||
| int Action::isAtCellFalseCnt = 0; | |||||
| int Action::classNo = 0; | |||||
| CellPos Action::lastNextPos = { -1,-1 }; | |||||
| Action::Direction Action::lastMove = Direction::null; | |||||
| CellPos Action::lastDesPos = { -1,-1 }; | |||||
| bool BFS::vis[Constants::rows][Constants::cols] = { false }; | |||||
| CellPos BFS::now = { -1,-1 }; | |||||
| CellPos BFS::nxt = { -1,-1 }; | |||||
| CellPos BFS::pre[Constants::rows][Constants::cols] = { {-1,-1} }; | |||||
| int GameState::readCnt = 0; | |||||
| int GameState::clsFinishCnt = 0; | |||||
| GameState gameState; | |||||
| bool Action::isValidMove(CellPos judgePos, Direction comeDir, GameState state, IStudentAPI& api) | |||||
| { | |||||
| auto students = api.GetStudents(); | |||||
| for (auto it = students.begin(); it != students.end(); it++) | |||||
| { | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 390)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 390)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y + 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y - 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 280)][api.GridToCell((*it)->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 280)][api.GridToCell((*it)->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 280)][api.GridToCell((*it)->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 280)][api.GridToCell((*it)->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| } | |||||
| auto trickers = api.GetTrickers(); | |||||
| for (auto it = trickers.begin(); it != trickers.end(); it++) | |||||
| { | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 390)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 390)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y + 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y - 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 280)][api.GridToCell((*it)->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 280)][api.GridToCell((*it)->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 280)][api.GridToCell((*it)->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 280)][api.GridToCell((*it)->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| } | |||||
| switch (comeDir) | |||||
| { | |||||
| case Action::Direction::up_left: { | |||||
| if (state.gameMap[judgePos.x + 1][judgePos.y] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x + 1][judgePos.y] != THUAI6::PlaceType::Grass)return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y + 1] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x][judgePos.y + 1] != THUAI6::PlaceType::Grass)return false; | |||||
| break; | |||||
| } | |||||
| case Action::Direction::up_right: { | |||||
| if (state.gameMap[judgePos.x + 1][judgePos.y] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x + 1][judgePos.y] != THUAI6::PlaceType::Grass)return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y - 1] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x][judgePos.y - 1] != THUAI6::PlaceType::Grass)return false; | |||||
| break; | |||||
| } | |||||
| case Action::Direction::down_right: { | |||||
| if (state.gameMap[judgePos.x - 1][judgePos.y] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x - 1][judgePos.y] != THUAI6::PlaceType::Grass)return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y - 1] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x][judgePos.y - 1] != THUAI6::PlaceType::Grass)return false; | |||||
| break; | |||||
| } | |||||
| case Action::Direction::down_left: { | |||||
| if (state.gameMap[judgePos.x - 1][judgePos.y] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x - 1][judgePos.y] != THUAI6::PlaceType::Grass)return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y + 1] != THUAI6::PlaceType::Land && state.gameMap[judgePos.x][judgePos.y + 1] != THUAI6::PlaceType::Grass)return false; | |||||
| break; | |||||
| } | |||||
| default:break; | |||||
| } | |||||
| return state.gameMap[judgePos.x][judgePos.y] == THUAI6::PlaceType::Land | |||||
| || state.gameMap[judgePos.x][judgePos.y] == THUAI6::PlaceType::Grass | |||||
| || state.gameMap[judgePos.x][judgePos.y] == THUAI6::PlaceType::Window | |||||
| || (state.isDoor(judgePos) && api.IsDoorOpen(judgePos.x, judgePos.y)); | |||||
| } | |||||
| bool Action::isValidMove(CellPos judgePos, Direction comeDir, GameState state, ITrickerAPI& api) | |||||
| { | |||||
| for (int i=0; i<gameState.ValidMoveBuffer.size(); i++) { | |||||
| auto it = &gameState.ValidMoveBuffer[i].first; | |||||
| if (api.HaveView(it->x, it->y)|| (api.GetFrameCount()- gameState.ValidMoveBuffer[i].second)>=100) { | |||||
| gameState.ValidMoveBuffer.erase(gameState.ValidMoveBuffer.begin()+i); | |||||
| i--; | |||||
| } | |||||
| else { | |||||
| state.gameMap[api.GridToCell(it->x)][api.GridToCell(it->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x + 390)][api.GridToCell(it->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x - 390)][api.GridToCell(it->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x)][api.GridToCell(it->y + 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x)][api.GridToCell(it->y - 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x + 280)][api.GridToCell(it->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x + 280)][api.GridToCell(it->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x - 280)][api.GridToCell(it->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell(it->x - 280)][api.GridToCell(it->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| } | |||||
| } | |||||
| auto students = api.GetStudents(); | |||||
| for (auto it = students.begin(); it != students.end(); it++) | |||||
| { | |||||
| if ((*it)->determination>0) | |||||
| continue; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 390)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 390)][api.GridToCell((*it)->y)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y + 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x)][api.GridToCell((*it)->y - 390)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 280)][api.GridToCell((*it)->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x + 280)][api.GridToCell((*it)->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 280)][api.GridToCell((*it)->y + 280)] = THUAI6::PlaceType::Wall; | |||||
| state.gameMap[api.GridToCell((*it)->x - 280)][api.GridToCell((*it)->y - 280)] = THUAI6::PlaceType::Wall; | |||||
| gameState.ValidMoveBuffer.push_back({ { (*it)->x ,(*it)->y },api.GetFrameCount()}); | |||||
| } | |||||
| switch (comeDir) | |||||
| { | |||||
| case Action::Direction::up_left: { | |||||
| if (state.gameMap[judgePos.x + 1][judgePos.y] != THUAI6::PlaceType::Land )return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y + 1] != THUAI6::PlaceType::Land )return false; | |||||
| break; | |||||
| } | |||||
| case Action::Direction::up_right: { | |||||
| if (state.gameMap[judgePos.x + 1][judgePos.y] != THUAI6::PlaceType::Land )return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y - 1] != THUAI6::PlaceType::Land )return false; | |||||
| break; | |||||
| } | |||||
| case Action::Direction::down_right: { | |||||
| if (state.gameMap[judgePos.x - 1][judgePos.y] != THUAI6::PlaceType::Land )return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y - 1] != THUAI6::PlaceType::Land )return false; | |||||
| break; | |||||
| } | |||||
| case Action::Direction::down_left: { | |||||
| if (state.gameMap[judgePos.x - 1][judgePos.y] != THUAI6::PlaceType::Land )return false; | |||||
| if (state.gameMap[judgePos.x][judgePos.y + 1] != THUAI6::PlaceType::Land )return false; | |||||
| break; | |||||
| } | |||||
| default:break; | |||||
| } | |||||
| return state.canReach(judgePos, api); | |||||
| } | |||||
| double Action::getAngle(GridPos grid1, GridPos grid2) | |||||
| { | |||||
| return atan2(grid2.y - grid1.y, grid2.x - grid1.x); | |||||
| } | |||||
| void Action::Move(CellPos destination, IStudentAPI& api) | |||||
| { | |||||
| int desX = api.CellToGrid(destination.x); | |||||
| int desY = api.CellToGrid(destination.y); | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.Move(50, getAngle({ self->x,self->y }, { desX,desY })); | |||||
| } | |||||
| void Action::Move(CellPos destination, ITrickerAPI& api) | |||||
| { | |||||
| int desX = api.CellToGrid(destination.x); | |||||
| int desY = api.CellToGrid(destination.y); | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.Move(50, getAngle({ self->x,self->y }, { desX,desY })); | |||||
| } | |||||
| void Action::Move(Direction direction, IStudentAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| int x = api.GridToCell(self->x); | |||||
| int y = api.GridToCell(self->y); | |||||
| Move(Action::directionToPos({ x, y }, direction), api); | |||||
| } | |||||
| void Action::Move(Direction direction, ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| int x = api.GridToCell(self->x); | |||||
| int y = api.GridToCell(self->y); | |||||
| Move(Action::directionToPos({ x, y }, direction), api); | |||||
| } | |||||
| CellPos Action::directionToPos(CellPos nowPos, Direction direction) | |||||
| { | |||||
| switch (direction) | |||||
| { | |||||
| case Action::Direction::up: | |||||
| return { nowPos.x - 1, nowPos.y }; | |||||
| case Action::Direction::down: | |||||
| return { nowPos.x + 1, nowPos.y }; | |||||
| case Action::Direction::left: | |||||
| return { nowPos.x, nowPos.y - 1 }; | |||||
| case Action::Direction::right: | |||||
| return { nowPos.x, nowPos.y + 1 }; | |||||
| case Action::Direction::up_left: | |||||
| return { nowPos.x - 1, nowPos.y - 1 }; | |||||
| case Action::Direction::up_right: | |||||
| return { nowPos.x - 1, nowPos.y + 1 }; | |||||
| case Action::Direction::down_left: | |||||
| return { nowPos.x + 1, nowPos.y - 1 }; | |||||
| case Action::Direction::down_right: | |||||
| return { nowPos.x + 1, nowPos.y + 1 }; | |||||
| } | |||||
| return { -1,-1 }; | |||||
| } | |||||
| Action::Direction Action::posToDirection(CellPos nowPos, CellPos nextPos) | |||||
| { | |||||
| if (nextPos.x == nowPos.x + 1 && nextPos.y == nowPos.y) | |||||
| return Action::Direction::down; | |||||
| if (nextPos.x == nowPos.x - 1 && nextPos.y == nowPos.y) | |||||
| return Action::Direction::up; | |||||
| if (nextPos.x == nowPos.x && nextPos.y == nowPos.y + 1) | |||||
| return Action::Direction::right; | |||||
| if (nextPos.x == nowPos.x && nextPos.y == nowPos.y - 1) | |||||
| return Action::Direction::left; | |||||
| if (nextPos.x == nowPos.x + 1 && nextPos.y == nowPos.y + 1) | |||||
| return Action::Direction::down_right; | |||||
| if (nextPos.x == nowPos.x - 1 && nextPos.y == nowPos.y + 1) | |||||
| return Action::Direction::up_right; | |||||
| if (nextPos.x == nowPos.x + 1 && nextPos.y == nowPos.y - 1) | |||||
| return Action::Direction::down_left; | |||||
| if (nextPos.x == nowPos.x - 1 && nextPos.y == nowPos.y - 1) | |||||
| return Action::Direction::up_left; | |||||
| return Action::Direction::null; | |||||
| } | |||||
| std::pair<CellPos, int> BFS::bfs(CellPos start, CellPos destination, ITrickerAPI& api) | |||||
| { | |||||
| int length = 0; | |||||
| std::queue<CellPos> posQ; | |||||
| memset(vis, false, sizeof(vis)); | |||||
| vis[start.x][start.y] = true; | |||||
| posQ.push(start); | |||||
| while (!posQ.empty()) | |||||
| { | |||||
| now = posQ.front(); | |||||
| posQ.pop(); | |||||
| for (auto move = Action::allMove.begin(); move != Action::allMove.end(); move++) | |||||
| { | |||||
| nxt = Action::directionToPos(now, *move); | |||||
| if (nxt.x == destination.x && nxt.y == destination.y && !gameState.canReach(nxt, api)) | |||||
| { | |||||
| pre[nxt.x][nxt.y] = now; | |||||
| now = nxt; | |||||
| break; | |||||
| } | |||||
| if (!Action::isValidMove(nxt, *move, gameState, api) || vis[nxt.x][nxt.y]) | |||||
| continue; | |||||
| if (nxt.x == destination.x && nxt.y == destination.y) | |||||
| { | |||||
| pre[nxt.x][nxt.y] = now; | |||||
| now = nxt; | |||||
| break; | |||||
| } | |||||
| vis[nxt.x][nxt.y] = true; | |||||
| posQ.push(nxt); | |||||
| pre[nxt.x][nxt.y] = now; | |||||
| } | |||||
| if (now.x == nxt.x && now.y == nxt.y) | |||||
| { | |||||
| while (pre[now.x][now.y].x != start.x || pre[now.x][now.y].y != start.y) | |||||
| { | |||||
| now = pre[now.x][now.y]; | |||||
| length++; | |||||
| } | |||||
| return { now, length + 1 }; | |||||
| } | |||||
| } | |||||
| return { {-1,-1},-1 }; | |||||
| } | |||||
| std::pair<std::pair<CellPos, CellPos>,int> BFS::bfs(CellPos start, THUAI6::PlaceType destination, ITrickerAPI& api) | |||||
| { | |||||
| int length = 0; | |||||
| std::queue<CellPos> posQ; | |||||
| if (gameState.gameMap[start.x][start.y] == destination) { | |||||
| return { {start,start},0 }; | |||||
| } | |||||
| memset(vis, false, sizeof(vis)); | |||||
| vis[start.x][start.y] = true; | |||||
| posQ.push(start); | |||||
| while (!posQ.empty()) | |||||
| { | |||||
| now = posQ.front(); | |||||
| posQ.pop(); | |||||
| for (auto move = Action::allMove.begin(); move != Action::allMove.end(); move++) | |||||
| { | |||||
| nxt = Action::directionToPos(now, *move); | |||||
| if (gameState.gameMap[nxt.x][nxt.y] == destination && !gameState.canReach(nxt, api)) | |||||
| { | |||||
| pre[nxt.x][nxt.y] = now; | |||||
| now = nxt; | |||||
| break; | |||||
| } | |||||
| if (!Action::isValidMove(nxt, *move, gameState, api) || vis[nxt.x][nxt.y]) | |||||
| continue; | |||||
| if (gameState.gameMap[nxt.x][nxt.y] == destination) | |||||
| { | |||||
| pre[nxt.x][nxt.y] = now; | |||||
| now = nxt; | |||||
| break; | |||||
| } | |||||
| vis[nxt.x][nxt.y] = true; | |||||
| posQ.push(nxt); | |||||
| pre[nxt.x][nxt.y] = now; | |||||
| } | |||||
| if (now.x == nxt.x && now.y == nxt.y) | |||||
| { | |||||
| while (pre[now.x][now.y].x != start.x || pre[now.x][now.y].y != start.y) { | |||||
| length++; | |||||
| now = pre[now.x][now.y]; | |||||
| } | |||||
| return { { now,nxt },length }; | |||||
| } | |||||
| } | |||||
| return { { {-1,-1},{-1,-1} },1000 }; | |||||
| } | |||||
| void Communication::sendClassroomPos(IStudentAPI& api, int64_t myID, CellPos classroomPos) { | |||||
| auto message = fmt::format("{} {} {} {}", classroomPos.x, classroomPos.y, -1, -1); | |||||
| for (int id = 0; id <= 3; id++) | |||||
| if (id != myID) | |||||
| api.SendTextMessage(id, message); | |||||
| } | |||||
| void Communication::sendTrickerPos(IStudentAPI& api, int64_t myID) { | |||||
| auto trickers = api.GetTrickers(); | |||||
| if (trickers.empty()) | |||||
| return; | |||||
| CellPos trickerPos = { api.GridToCell(trickers[0]->x), api.GridToCell(trickers[0]->y) }; | |||||
| auto message = fmt::format("{} {} {} {}", -1, -1, trickerPos.x, trickerPos.y); | |||||
| for (int i = 0; i <= 3; i++) | |||||
| if (i != myID) | |||||
| api.SendTextMessage(i, message); | |||||
| } | |||||
| void Communication::receiveMessage(IStudentAPI& api, int64_t myID) { | |||||
| CellPos classroomPos, trickerPos; | |||||
| std::pair<int64_t, std::string> message; | |||||
| std::stringstream ss; | |||||
| gameState.trickerPos = { -1, -1 }; | |||||
| auto trickers = api.GetTrickers(); | |||||
| if (!trickers.empty()) | |||||
| gameState.trickerPos = { api.GridToCell(trickers[0]->x), api.GridToCell(trickers[0]->y) }; | |||||
| while (api.HaveMessage()) { | |||||
| ss.clear(); | |||||
| message = api.GetMessage(); | |||||
| ss.str(message.second); | |||||
| ss >> classroomPos.x >> classroomPos.y >> trickerPos.x >> trickerPos.y; | |||||
| if (classroomPos.x != -1 && classroomPos.y != -1) | |||||
| gameState.deleteStudiedClassroom(classroomPos); | |||||
| if (trickerPos.x != -1 && trickerPos.y != -1) | |||||
| gameState.trickerPos = trickerPos; | |||||
| } | |||||
| } | |||||
| inline bool GameState::isAtCell(GridPos myPos, GridPos cellPos) const { | |||||
| return abs(myPos.x - cellPos.x) < 100 && abs(myPos.y - cellPos.y) < 100; | |||||
| } | |||||
| inline bool GameState::isAtPosition(CellPos character, CellPos pos) const { | |||||
| return (abs(character.x - pos.x) <= 1 && abs(character.y - pos.y) <= 1); | |||||
| } | |||||
| inline bool GameState::isAtPos(CellPos character, CellPos pos) const { | |||||
| return (abs(character.x - pos.x) <= 1 && abs(character.y - pos.y) == 0) || (abs(character.y - pos.y) <= 1 && abs(character.x - pos.x) == 0); | |||||
| } | |||||
| inline bool GameState::isAttackable(GridPos trickerPos, GridPos studentPos) const { | |||||
| return gridDistance(trickerPos, studentPos) <= 1500; | |||||
| } | |||||
| inline bool GameState::isClassroomFinished(CellPos classroomPos, IStudentAPI& api) const { | |||||
| return api.GetClassroomProgress(classroomPos.x, classroomPos.y) >= Constants::maxClassroomProgress; | |||||
| } | |||||
| inline bool GameState::isDoor(CellPos pos) const { | |||||
| return gameMap[pos.x][pos.y] == THUAI6::PlaceType::Door3 | |||||
| || gameMap[pos.x][pos.y] == THUAI6::PlaceType::Door5 | |||||
| || gameMap[pos.x][pos.y] == THUAI6::PlaceType::Door6; | |||||
| } | |||||
| inline bool GameState::isGateFinished(CellPos gatePos, IStudentAPI& api) const { | |||||
| return api.GetGateProgress(gatePos.x, gatePos.y) >= Constants::maxGateProgress; | |||||
| } | |||||
| inline bool GameState::isTrickerVisible() const { | |||||
| return trickerPos.x == -1 && trickerPos.y == -1; | |||||
| } | |||||
| inline bool GameState::isType(CellPos pos, THUAI6::PlaceType type) const { | |||||
| return gameMap[pos.x][pos.y] == type; | |||||
| } | |||||
| inline bool GameState::canReach(CellPos pos, ITrickerAPI& api) const { | |||||
| return gameMap[pos.x][pos.y] == THUAI6::PlaceType::Window | |||||
| || gameMap[pos.x][pos.y] == THUAI6::PlaceType::Grass | |||||
| || gameMap[pos.x][pos.y] == THUAI6::PlaceType::Land | |||||
| || (isDoor(pos) && api.IsDoorOpen(pos.x, pos.y)); | |||||
| } | |||||
| inline bool GameState::readyToGraduate() const { | |||||
| return clsFinishCnt >= Constants::numOfRequiredClassroomForGate; | |||||
| } | |||||
| double GameState::gridDistance(GridPos grid1, GridPos grid2) const { | |||||
| return sqrt(pow((grid1.x - grid2.x), 2) + pow((grid1.y - grid2.y), 2)); | |||||
| } | |||||
| void GameState::deleteStudiedClassroom(CellPos classroomPos) { | |||||
| for (auto it = gameState.ClassRoom.begin(); it != gameState.ClassRoom.end(); it++) | |||||
| if (it->x == classroomPos.x && it->y == classroomPos.y) | |||||
| { | |||||
| ClassRoom.erase(it); | |||||
| gameState.gameMap[classroomPos.x][classroomPos.y] = THUAI6::PlaceType::Wall; | |||||
| clsFinishCnt++; | |||||
| break; | |||||
| } | |||||
| } | |||||
| void GameState::getMap(IStudentAPI& api) { | |||||
| for (int i = 0; i < Constants::rows; i++) { | |||||
| for (int j = 0; j < Constants::cols; j++) { | |||||
| gameMap[i][j] = (api.GetFullMap()[i][j]); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::ClassRoom) ClassRoom.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::Gate) Gate.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::HiddenGate) HiddenGate.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::Window) Window.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::Chest) Chest.push_back({ i, j }); | |||||
| } | |||||
| } | |||||
| } | |||||
| void GameState::getMap(ITrickerAPI& api) { | |||||
| for (int i = 0; i < Constants::rows; i++) { | |||||
| for (int j = 0; j < Constants::cols; j++) { | |||||
| gameMap[i][j] = (api.GetFullMap()[i][j]); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::ClassRoom) ClassRoom.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::Gate) Gate.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::HiddenGate) HiddenGate.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::Window) Window.push_back({ i, j }); | |||||
| if (gameMap[i][j] == THUAI6::PlaceType::Chest) Chest.push_back({ i, j }); | |||||
| } | |||||
| } | |||||
| } | |||||
| std::pair<std::pair<bool, std::pair<CellPos,GridPos>>, std::pair<double, double>> GameState::getClosestStudent(GridPos trickerPos, ITrickerAPI& api) | |||||
| { | |||||
| auto students = api.GetStudents(); | |||||
| double minDistance = 1e5; | |||||
| CellPos studentPos = { -1,-1 }; | |||||
| GridPos studentGrid = { -1,-1 }; | |||||
| bool updated[4] = { false }; | |||||
| bool inSight = false; | |||||
| double angle[5] = { -1,-1,-1,-1,-1 }; | |||||
| int picked = 4; | |||||
| for (auto it = students.begin(); it != students.end(); it++) | |||||
| { | |||||
| if ((*it)->determination<=0) { | |||||
| gameState.studentPos[(*it)->playerID].x = -1; | |||||
| gameState.studentPos[(*it)->playerID].y = -1; | |||||
| gameState.studentGrid[(*it)->playerID].x = -1; | |||||
| gameState.studentGrid[(*it)->playerID].y = -1; | |||||
| continue; | |||||
| } | |||||
| angle[(*it)->playerID] = (*it)->facingDirection; | |||||
| gameState.studentPos[(*it)->playerID].x = api.GridToCell((*it)->x); | |||||
| gameState.studentPos[(*it)->playerID].y = api.GridToCell((*it)->y); | |||||
| gameState.studentGrid[(*it)->playerID].x = (*it)->x; | |||||
| gameState.studentGrid[(*it)->playerID].y = (*it)->y; | |||||
| updated[(*it)->playerID] = true; | |||||
| } | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (minDistance > gridDistance(trickerPos, { api.CellToGrid(gameState.studentPos[i].x),api.CellToGrid(gameState.studentPos[i].y) }) && gameState.studentPos[i].x >= 0) | |||||
| { | |||||
| minDistance = gridDistance(trickerPos, { api.CellToGrid(gameState.studentPos[i].x),api.CellToGrid(gameState.studentPos[i].y) }); | |||||
| studentPos = gameState.studentPos[i]; | |||||
| studentGrid = gameState.studentGrid[i]; | |||||
| inSight = updated[i]; | |||||
| picked = i; | |||||
| } | |||||
| } | |||||
| return { {inSight,{studentPos,studentGrid}},{minDistance,angle[picked]} }; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) {} | |||||
| int AttackCount = 0; | |||||
| int needNew = 0; | |||||
| bool del=false; | |||||
| GridPos lastGrid = { -1,-1 }; | |||||
| int stuck = 0; | |||||
| int classcount = 0; | |||||
| int pointing = -1; | |||||
| //bool attackToGrass = false; | |||||
| std::vector <CellPos> traversal; | |||||
| std::vector <CellPos> gates; | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| if (gameState.readCnt < 1) | |||||
| { | |||||
| gameState.getMap(api); | |||||
| gameState.readCnt++; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| //studentPos赋初值,代表视野没有,也没记住 | |||||
| gameState.studentPos[i] = { -1,-1 }; | |||||
| gameState.studentGrid[i] = { -1,-1 }; | |||||
| } | |||||
| if (gameState.gameMap[5][9] == THUAI6::PlaceType::Grass) { | |||||
| traversal.push_back({ 6,24 }); | |||||
| traversal.push_back({ 8,31 });//class | |||||
| traversal.push_back({ 10,38 });//class | |||||
| traversal.push_back({ 12,46 }); | |||||
| traversal.push_back({ 19,41 });//class | |||||
| //traversal.push_back({ 18,46 }); | |||||
| //traversal.push_back({ 23,40 }); | |||||
| traversal.push_back({ 21,27 }); | |||||
| traversal.push_back({ 28,26 });//class | |||||
| traversal.push_back({ 33,40 });//class | |||||
| //traversal.push_back({ 39,38 }); | |||||
| traversal.push_back({ 44,32 });//class | |||||
| traversal.push_back({ 40,12 });//class | |||||
| //traversal.push_back({ 46,9 }); | |||||
| //traversal.push_back({ 36,7 }); | |||||
| traversal.push_back({ 30,7 });//class | |||||
| traversal.push_back({ 22,18 });//class | |||||
| traversal.push_back({ 18,5 });//class | |||||
| traversal.push_back({ 8,16 }); | |||||
| traversal.push_back({ 7,21 }); | |||||
| gates.push_back({ 46,45 }); | |||||
| gates.push_back({ 5,6 }); | |||||
| } | |||||
| else if (gameState.gameMap[5][9] == THUAI6::PlaceType::Wall) { | |||||
| traversal.push_back({ 16,20 }); | |||||
| traversal.push_back({ 16,16 });//class | |||||
| traversal.push_back({ 20,16 }); | |||||
| traversal.push_back({ 29,16 }); | |||||
| traversal.push_back({ 33,16 });//class | |||||
| traversal.push_back({ 33,20 }); | |||||
| traversal.push_back({ 33,29 }); | |||||
| traversal.push_back({ 33,33 });//class | |||||
| traversal.push_back({ 29,33 }); | |||||
| traversal.push_back({ 20,33 }); | |||||
| traversal.push_back({ 16,33 });//class | |||||
| traversal.push_back({ 16,29 }); | |||||
| traversal.push_back({ 6,13 });//class | |||||
| traversal.push_back({ 6,36 });//class | |||||
| traversal.push_back({ 13,43 });//class | |||||
| traversal.push_back({ 34,45 }); | |||||
| traversal.push_back({ 43,36 });//class | |||||
| traversal.push_back({ 43,13 });//class | |||||
| traversal.push_back({ 36,6 });//class | |||||
| traversal.push_back({ 15,4 }); | |||||
| gates.push_back({ 47,6 }); | |||||
| gates.push_back({ 2,43 }); | |||||
| } | |||||
| else { | |||||
| traversal = gameState.ClassRoom; | |||||
| } | |||||
| api.Print("Map read"); | |||||
| } | |||||
| if (traversal.size()) | |||||
| Action::classNo = Action::classNo % traversal.size(); | |||||
| else | |||||
| Action::classNo = -1; | |||||
| auto self = api.GetSelfInfo(); | |||||
| GridPos myGrid = { self->x,self->y }; | |||||
| CellPos myCell = { api.GridToCell(self->x), api.GridToCell(self->y) }; | |||||
| api.Print(fmt::format("mypos {} {}", myCell.x, myCell.y)); | |||||
| if (self->playerState == THUAI6::PlayerState::Swinging || self->playerState == THUAI6::PlayerState::Attacking || self->playerState == THUAI6::PlayerState::Stunned) { | |||||
| gameState.getClosestStudent(myGrid, api); | |||||
| api.Print("not idle"); | |||||
| return; | |||||
| } | |||||
| if (gameState.isAtCell(myGrid, { api.CellToGrid(myCell.x),api.CellToGrid(myCell.y) }) || Action::isAtCellFalseCnt > 15) | |||||
| //if (gameState.isAtCell(myGrid, { api.CellToGrid(myCell.x),api.CellToGrid(myCell.y) })) | |||||
| { | |||||
| //在格点上或者连续10次进不到格点 | |||||
| bool notAtCell = Action::isAtCellFalseCnt > 15; | |||||
| Action::isAtCellFalseCnt = 0; | |||||
| api.Print(fmt::format("notAtCell {}", notAtCell)); | |||||
| if (!notAtCell) { | |||||
| if (abs(myGrid.x - lastGrid.x) <= 5 && abs(myGrid.y - lastGrid.y) <= 5) { | |||||
| stuck++; | |||||
| api.Print(fmt::format("stuck {}", stuck)); | |||||
| } | |||||
| else { | |||||
| stuck = 0; | |||||
| api.Print("stuck zero"); | |||||
| } | |||||
| if (stuck >= 5) { | |||||
| if (pointing <= 1) { | |||||
| auto angle = Action::getAngle({ api.CellToGrid(Action::directionToPos(myCell, Action::lastMove).x), api.CellToGrid(Action::directionToPos(myCell, Action::lastMove).y) }, { api.CellToGrid(myCell.x), api.CellToGrid(myCell.y) }); | |||||
| angle = angle + pointing * PI / 4; | |||||
| api.Attack(angle); | |||||
| pointing++; | |||||
| } | |||||
| else { | |||||
| pointing = -1; | |||||
| stuck = 0; | |||||
| } | |||||
| api.Print(fmt::format("{} {} set to wall", Action::lastNextPos.x, Action::lastNextPos.y)); | |||||
| stuck = 0; | |||||
| gameState.gameMap[Action::lastNextPos.x][Action::lastNextPos.y] = THUAI6::PlaceType::Wall; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (gameState.studentPos[i].x == Action::lastNextPos.x && gameState.studentPos[i].y == Action::lastNextPos.y) { | |||||
| gameState.studentPos[i].x = -1; | |||||
| gameState.studentPos[i].y = -1; | |||||
| gameState.studentGrid[i].x = -1; | |||||
| gameState.studentGrid[i].y = -1; | |||||
| } | |||||
| } | |||||
| } | |||||
| lastGrid = myGrid; | |||||
| } | |||||
| else{ | |||||
| if (gameState.gameMap[Action::lastNextPos.x][Action::lastNextPos.y] == THUAI6::PlaceType::Grass && AttackCount < 5) { | |||||
| GridPos target = { api.CellToGrid(Action::lastNextPos.x),api.CellToGrid(Action::lastNextPos.y) }; | |||||
| api.Attack(Action::getAngle(myGrid, target)); | |||||
| api.Print(fmt::format("attacknum {}", AttackCount)); | |||||
| api.Print(fmt::format("attackto {} {}", Action::lastNextPos.x, Action::lastNextPos.y)); | |||||
| AttackCount++; | |||||
| } | |||||
| else { | |||||
| AttackCount = 0; | |||||
| del = true; | |||||
| //Action::lastNextPos = Action::directionToPos(Action::lastNextPos, Action::lastMove); | |||||
| api.Print(fmt::format("delete {} {}", Action::lastNextPos.x, Action::lastNextPos.y)); | |||||
| gameState.gameMap[Action::lastNextPos.x][Action::lastNextPos.y] = THUAI6::PlaceType::Wall; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (gameState.studentPos[i].x == Action::lastNextPos.x && gameState.studentPos[i].y == Action::lastNextPos.y) { | |||||
| gameState.studentPos[i].x = -1; | |||||
| gameState.studentPos[i].y = -1; | |||||
| gameState.studentGrid[i].x = -1; | |||||
| gameState.studentGrid[i].y = -1; | |||||
| } | |||||
| } | |||||
| } | |||||
| Action::lastNextPos = Action::directionToPos(Action::lastNextPos, Action::lastMove); | |||||
| return; | |||||
| } | |||||
| auto closestStudent = gameState.getClosestStudent(myGrid, api); | |||||
| bool inSight = closestStudent.first.first; | |||||
| auto stuCellPos = closestStudent.first.second.first; | |||||
| GridPos stuGridPos = closestStudent.first.second.second; | |||||
| auto distance = closestStudent.second.first; | |||||
| api.Print(fmt::format("insight {} stupos {} {} distance {}", inSight, stuCellPos.x, stuCellPos.y, distance)); | |||||
| if (distance < Constants::basicTrickerViewRange)//如果视野里有学生,且未沉迷,则判断攻击条件,不满足则追击学生 | |||||
| { | |||||
| Action::lastDesPos = { stuCellPos.x,stuCellPos.y }; | |||||
| bool isClose; | |||||
| if (inSight) { | |||||
| isClose = gameState.isAtPos(myCell, stuCellPos); | |||||
| api.UseSkill(0); | |||||
| //if ((!api.GetSelfInfo()->buff.empty())&&api.GetSelfInfo()->buff[0]==THUAI6::TrickerBuffType::Invisible) | |||||
| //api.Print("invisible"); | |||||
| api.UseSkill(1); | |||||
| if (api.GetSelfInfo()->bulletType == THUAI6::BulletType::FlyingKnife) { | |||||
| //api.Print("knife"); | |||||
| api.Attack(Action::getAngle(myGrid, stuGridPos)); | |||||
| } | |||||
| } | |||||
| else { | |||||
| isClose = gameState.isAtCell(myGrid, {api.CellToGrid(stuCellPos.x),api.CellToGrid(stuCellPos.y)}) || (gameState.isAtPosition(myCell, stuCellPos) && notAtCell); | |||||
| } | |||||
| api.Print(fmt::format("isclose {}", isClose)); | |||||
| if (isClose) { | |||||
| if (inSight) { | |||||
| api.Attack(Action::getAngle(myGrid, stuGridPos)); | |||||
| } | |||||
| /* | |||||
| else if (notAtCell && AttackCount < 3) { | |||||
| api.Attack(Action::getAngle(myGrid, stuGridPos)); | |||||
| AttackCount++; | |||||
| } | |||||
| else if (del) { | |||||
| //AttackCount = 0; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (gameState.studentPos[i].x == stuCellPos.x && gameState.studentPos[i].y == stuCellPos.y) { | |||||
| gameState.studentPos[i].x = -1; | |||||
| gameState.studentPos[i].y = -1; | |||||
| } | |||||
| } | |||||
| del = false; | |||||
| }*/ | |||||
| else { | |||||
| if (gameState.gameMap[myCell.x][myCell.y] != THUAI6::PlaceType::Grass) { | |||||
| int newx, newy; | |||||
| /* | |||||
| if (gameState.gameMap[stuCellPos.x][stuCellPos.y] == THUAI6::PlaceType::Grass) { | |||||
| newx = stuCellPos.x; | |||||
| newy = stuCellPos.y; | |||||
| } | |||||
| for (int i = -1; i <= 1; i = i + 2) { | |||||
| if (gameState.gameMap[stuCellPos.x + i][stuCellPos.y] == THUAI6::PlaceType::Grass) { | |||||
| newx = stuCellPos.x + i; | |||||
| newy = stuCellPos.y; | |||||
| } | |||||
| } | |||||
| for (int j = -1; j <= 1; j = j + 2) { | |||||
| if (gameState.gameMap[stuCellPos.x][stuCellPos.y + j] == THUAI6::PlaceType::Grass) { | |||||
| newx = stuCellPos.x; | |||||
| newy = stuCellPos.y + j; | |||||
| } | |||||
| } | |||||
| */ | |||||
| api.Print("bfsing"); | |||||
| auto result = BFS::bfs(myCell, THUAI6::PlaceType::Grass, api); | |||||
| api.Print(fmt::format("newdist {}", result.second)); | |||||
| if (result.second < 5&&result.second>=0) { | |||||
| newx = result.first.second.x; | |||||
| newy = result.first.second.y; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (gameState.studentPos[i].x == stuCellPos.x && gameState.studentPos[i].y == stuCellPos.y) { | |||||
| gameState.studentPos[i].x = newx; | |||||
| gameState.studentPos[i].y = newy; | |||||
| gameState.studentGrid[i].x = api.CellToGrid(newx); | |||||
| gameState.studentGrid[i].y = api.CellToGrid(newy); | |||||
| } | |||||
| } | |||||
| api.Print(fmt::format("newxy {} {}", newx, newy)); | |||||
| } | |||||
| //needNew=3; | |||||
| //return; | |||||
| } | |||||
| else { | |||||
| api.Print("not bfsing"); | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (gameState.studentPos[i].x == stuCellPos.x && gameState.studentPos[i].y == stuCellPos.y) { | |||||
| gameState.studentPos[i].x = -1; | |||||
| gameState.studentPos[i].y = -1; | |||||
| gameState.studentGrid[i].x = -1; | |||||
| gameState.studentGrid[i].y = -1; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| api.Print(fmt::format("my pos {} {}", myCell.x, myCell.y)); | |||||
| api.Print(fmt::format("stu pos {} {}", stuCellPos.x, stuCellPos.y)); | |||||
| auto result = BFS::bfs(myCell, stuCellPos, api); | |||||
| auto nextPos = result.first; | |||||
| api.Print(fmt::format("next {} {}", nextPos.x, nextPos.y)); | |||||
| if (nextPos.x == -1) { | |||||
| for (int i = 0; i < 4; i++) { | |||||
| if (gameState.studentPos[i].x == stuCellPos.x && gameState.studentPos[i].y == stuCellPos.y) { | |||||
| gameState.studentPos[i].x = -1; | |||||
| gameState.studentPos[i].y = -1; | |||||
| } | |||||
| } | |||||
| return; | |||||
| } | |||||
| if (gameState.isType(nextPos, THUAI6::PlaceType::Window)) | |||||
| api.SkipWindow(); | |||||
| Action::Move(nextPos, api); | |||||
| Action::lastNextPos = nextPos; | |||||
| Action::lastMove = Action::posToDirection(nextPos, myCell); | |||||
| } | |||||
| } | |||||
| else//视野里没有学生,就遍历教室并前往 | |||||
| { | |||||
| if (classcount >= 7) { | |||||
| auto classroomCellPos = gates[0]; | |||||
| GridPos classroomGridPos = { api.CellToGrid(classroomCellPos.x),api.CellToGrid(classroomCellPos.y) }; | |||||
| api.Print(fmt::format("class pos {} {}", classroomCellPos.x, classroomCellPos.y)); | |||||
| auto result = BFS::bfs(myCell, classroomCellPos, api); | |||||
| auto nextPos = result.first; | |||||
| api.Print(fmt::format("next pos {} {}", nextPos.x, nextPos.y)); | |||||
| if (nextPos.x == -1) { | |||||
| return; | |||||
| } | |||||
| if (!gameState.isAtPosition(myCell, classroomCellPos)) | |||||
| { | |||||
| if (gameState.isType(nextPos, THUAI6::PlaceType::Window)) | |||||
| api.SkipWindow(); | |||||
| Action::Move(nextPos, api); | |||||
| Action::lastNextPos = nextPos; | |||||
| Action::lastMove = Action::posToDirection(nextPos, myCell); | |||||
| } | |||||
| return; | |||||
| } | |||||
| auto classroomCellPos = traversal[Action::classNo]; | |||||
| GridPos classroomGridPos = { api.CellToGrid(classroomCellPos.x),api.CellToGrid(classroomCellPos.y) }; | |||||
| if (gameState.gameMap[classroomCellPos.x][classroomCellPos.y] == THUAI6::PlaceType::ClassRoom) { | |||||
| if (api.GetClassroomProgress(classroomCellPos.x, classroomCellPos.y) >= Constants::maxClassroomProgress) { | |||||
| traversal.erase(traversal.begin() + Action::classNo); | |||||
| classcount++; | |||||
| } | |||||
| //else if (api.GetClassroomProgress(classroomCellPos.x, classroomCellPos.y) == 0 && api.HaveView(classroomCellPos.x, classroomCellPos.y)) | |||||
| else if (api.GetClassroomProgress(classroomCellPos.x, classroomCellPos.y) == 0 && api.HaveView(classroomGridPos.x, classroomGridPos.y)) | |||||
| Action::classNo++; | |||||
| else | |||||
| { | |||||
| api.Print(fmt::format("class pos {} {}", classroomCellPos.x, classroomCellPos.y)); | |||||
| auto result = BFS::bfs(myCell, classroomCellPos, api); | |||||
| auto nextPos = result.first; | |||||
| api.Print(fmt::format("next pos {} {}", nextPos.x, nextPos.y)); | |||||
| if (nextPos.x == -1) { | |||||
| Action::classNo++; | |||||
| return; | |||||
| } | |||||
| if (!gameState.isAtPosition(myCell, classroomCellPos)) | |||||
| { | |||||
| if (gameState.isType(nextPos, THUAI6::PlaceType::Window)) | |||||
| api.SkipWindow(); | |||||
| Action::Move(nextPos, api); | |||||
| Action::lastNextPos = nextPos; | |||||
| Action::lastMove = Action::posToDirection(nextPos, myCell); | |||||
| } | |||||
| else | |||||
| { | |||||
| //if (api.GetClassroomProgress(classroomCellPos.x, classroomCellPos.y) > 0) | |||||
| //api.Attack(Action::getAngle(myGrid, classroomGridPos)); | |||||
| //else | |||||
| Action::classNo++; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (gameState.gameMap[classroomCellPos.x][classroomCellPos.y] == THUAI6::PlaceType::Grass) { | |||||
| api.Print(fmt::format("grass pos {} {}", classroomCellPos.x, classroomCellPos.y)); | |||||
| auto result = BFS::bfs(myCell, classroomCellPos, api); | |||||
| auto nextPos = result.first; | |||||
| api.Print(fmt::format("next pos {} {}", nextPos.x, nextPos.y)); | |||||
| if (nextPos.x == -1) { | |||||
| Action::classNo++; | |||||
| return; | |||||
| } | |||||
| if (!gameState.isAtCell(myGrid, classroomGridPos)) | |||||
| { | |||||
| if (gameState.isType(nextPos, THUAI6::PlaceType::Window)) | |||||
| api.SkipWindow(); | |||||
| Action::Move(nextPos, api); | |||||
| Action::lastNextPos = nextPos; | |||||
| Action::lastMove = Action::posToDirection(nextPos, myCell); | |||||
| } | |||||
| else | |||||
| { | |||||
| Action::classNo++; | |||||
| } | |||||
| } | |||||
| else { | |||||
| Action::classNo++; | |||||
| } | |||||
| } | |||||
| //if (needNew > 0) { | |||||
| //needNew--; | |||||
| //} | |||||
| } | |||||
| else | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| GridPos myGrid = { self->x,self->y }; | |||||
| CellPos myCell = { api.GridToCell(self->x), api.GridToCell(self->y) }; | |||||
| gameState.getClosestStudent(myGrid, api); | |||||
| Action::Move(Action::lastNextPos, api); | |||||
| Action::isAtCellFalseCnt++; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,382 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include<math.h> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| int flag1 = 0, flag2 = 0, flag3 = 0; | |||||
| long long c = 0; | |||||
| int ii = -1, jj = 0, iii = -1; | |||||
| int count = 0; | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Athlete, | |||||
| THUAI6::StudentType::Athlete, | |||||
| THUAI6::StudentType::Athlete, | |||||
| THUAI6::StudentType::Athlete }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::ANoisyPerson; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| long long UpLeft(int dx, int dy, int x, int y, ITrickerAPI& api) //会输出一串形如101000111的数字(数字应有dx+dy-1位,若不足,则在高位自动补足0)// | |||||
| { //从右往左每个数字代表一步,0代表向上一步,1代表向左一步// | |||||
| int a = 0, flag = 0; | |||||
| if (dx == 0) | |||||
| { | |||||
| for (int i = 1; i < dy; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - i) == THUAI6::PlaceType::Wall|| api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - i) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 1; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| flag = 0; | |||||
| if (dy == 0) | |||||
| { | |||||
| for (int i = 0; i < dx; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x) - i, api.GridToCell(y)) == THUAI6::PlaceType::Wall|| api.GetPlaceType(api.GridToCell(x) - i, api.GridToCell(y)) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 0; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| if (UpLeft(dx, dy - 1, x, y - 1000, api) != 2 && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - 1) != THUAI6::PlaceType::Wall&& api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - 1) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx, dy-1, x , y-1000, api) + 1; | |||||
| } | |||||
| if (UpLeft(dx - 1, dy, x - 1000, y, api) != 2 && api.GetPlaceType(api.GridToCell(x)-1, api.GridToCell(y)) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x)-1, api.GridToCell(y)) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx - 1, dy, x - 1000, y, api); | |||||
| } | |||||
| else return 2; | |||||
| } | |||||
| long long UpRight(int dx, int dy, int x, int y, ITrickerAPI& api) //会输出一串形如101000111的数字(数字应有dx+dy-1位,若不足,则在高位自动补足0)// | |||||
| { //从右往左每个数字代表一步,0代表向上一步,1代表向右一步// | |||||
| int a = 0, flag = 0; | |||||
| if (dx == 0) | |||||
| { | |||||
| for (int i = 1; i < dy; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + i) == THUAI6::PlaceType::Wall || api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + i) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 1; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| flag = 0; | |||||
| if (dy == 0) | |||||
| { | |||||
| for (int i = 0; i < dx; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x) - i, api.GridToCell(y)) == THUAI6::PlaceType::Wall || api.GetPlaceType(api.GridToCell(x) - i, api.GridToCell(y)) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 0; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| if (UpLeft(dx, dy - 1, x, y + 1000, api) != 2 && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + 1) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + 1) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx, dy - 1, x, y + 1000, api) + 1; | |||||
| } | |||||
| if (UpLeft(dx - 1, dy, x - 1000, y, api) != 2 && api.GetPlaceType(api.GridToCell(x) - 1, api.GridToCell(y)) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x) - 1, api.GridToCell(y)) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx - 1, dy, x - 1000, y, api); | |||||
| } | |||||
| else return 2; | |||||
| } | |||||
| long long DownLeft(int dx, int dy, int x, int y, ITrickerAPI& api) //会输出一串形如101000111的数字(数字应有dx+dy-1位,若不足,则在高位自动补足0)// | |||||
| { //从右往左每个数字代表一步,0代表向下一步,1代表向左一步// | |||||
| int a = 0, flag = 0; | |||||
| if (dx == 0) | |||||
| { | |||||
| for (int i = 1; i < dy; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - i) == THUAI6::PlaceType::Wall || api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - i) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 1; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| flag = 0; | |||||
| if (dy == 0) | |||||
| { | |||||
| for (int i = 0; i < dx; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x) + i, api.GridToCell(y)) == THUAI6::PlaceType::Wall || api.GetPlaceType(api.GridToCell(x) + i, api.GridToCell(y)) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 0; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| if (UpLeft(dx, dy - 1, x, y - 1000, api) != 2 && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - 1) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) - 1) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx, dy - 1, x, y - 1000, api) + 1; | |||||
| } | |||||
| if (UpLeft(dx - 1, dy, x + 1000, y, api) != 2 && api.GetPlaceType(api.GridToCell(x) + 1, api.GridToCell(y)) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x) + 1, api.GridToCell(y)) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx - 1, dy, x + 1000, y, api); | |||||
| } | |||||
| else return 2; | |||||
| } | |||||
| long long DownRight(int dx, int dy, int x, int y, ITrickerAPI& api) //会输出一串形如101000111的数字(数字应有dx+dy-1位,若不足,则在高位自动补足0)// | |||||
| { //从右往左每个数字代表一步,0代表向下一步,1代表向右一步// | |||||
| int a = 0, flag = 0; | |||||
| if (dx == 0) | |||||
| { | |||||
| for (int i = 1; i < dy; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + i) == THUAI6::PlaceType::Wall || api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + i) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 1; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| flag = 0; | |||||
| if (dy == 0) | |||||
| { | |||||
| for (int i = 0; i < dx; i++) | |||||
| { | |||||
| if (api.GetPlaceType(api.GridToCell(x) + i, api.GridToCell(y)) == THUAI6::PlaceType::Wall || api.GetPlaceType(api.GridToCell(x) + i, api.GridToCell(y)) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| flag = 1; break; | |||||
| } | |||||
| else a = a * 10 + 0; | |||||
| } | |||||
| if (flag == 1) return 2; | |||||
| else return a; | |||||
| } | |||||
| if (UpLeft(dx, dy - 1, x, y + 1000, api) != 2 && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + 1) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x), api.GridToCell(y) + 1) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx, dy - 1, x, y + 1000, api) + 1; | |||||
| } | |||||
| if (UpLeft(dx - 1, dy, x + 1000, y, api) != 2 && api.GetPlaceType(api.GridToCell(x) + 1, api.GridToCell(y)) != THUAI6::PlaceType::Wall && api.GetPlaceType(api.GridToCell(x) + 1, api.GridToCell(y)) != THUAI6::PlaceType::Window) | |||||
| { | |||||
| return 10 * UpLeft(dx - 1, dy, x + 1000, y, api); | |||||
| } | |||||
| else return 2; | |||||
| } | |||||
| void DownRight(long long c, ITrickerAPI& api) | |||||
| { | |||||
| if (c % 10 == 0) | |||||
| { | |||||
| api.MoveDown(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| if (c % 10 == 1) | |||||
| { | |||||
| api.MoveRight(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void DownLeft(long long c, ITrickerAPI& api) | |||||
| { | |||||
| if (c % 10 == 0) | |||||
| { | |||||
| api.MoveDown(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| if (c % 10 == 1) | |||||
| { | |||||
| api.MoveLeft(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void UpRight(long long c, ITrickerAPI& api) | |||||
| { | |||||
| if (c % 10 == 0) | |||||
| { | |||||
| api.MoveUp(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| if (c % 10 == 1) | |||||
| { | |||||
| api.MoveRight(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void UpLeft(long long c, ITrickerAPI& api) | |||||
| { | |||||
| if (c % 10 == 0) | |||||
| { | |||||
| api.MoveUp(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| if (c % 10 == 1) | |||||
| { | |||||
| api.MoveLeft(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(300)); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 2) | |||||
| { | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| double dx, dy, aa=0; | |||||
| int ddx, ddy; | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto st = api.GetStudents(); | |||||
| if (!st.empty()) | |||||
| { | |||||
| api.UseSkill(0); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(810)); | |||||
| if (flag1 == 0) | |||||
| { | |||||
| dx = st[0]->x - self->x; | |||||
| dy = st[0]->y - self->y; | |||||
| ddx = abs(api.GridToCell(dx)); | |||||
| ddy = abs(api.GridToCell(dy)); | |||||
| if (dx >= 0&&dy >= 0) { c = DownRight(ddx, ddy, self->x, self->y, api); jj = 1; } | |||||
| if (dx >= 0&&dy < 0) { c = DownLeft(ddx, ddy, self->x, self->y, api); jj = 2; } | |||||
| if (dx < 0&&dy >= 0) { c = UpRight(ddx, ddy, self->x, self->y, api); jj = 3; } | |||||
| if (dx < 0&&dy < 0) { c = UpLeft(ddx, ddy, self->x, self->y, api); jj = 4; } | |||||
| ii = ddx + ddy - 1; | |||||
| flag1 = 1; | |||||
| } | |||||
| if (ii > 0 && c != 2 && jj == 1) | |||||
| { | |||||
| DownRight(c, api); | |||||
| c = c / 10; | |||||
| ii--; | |||||
| } | |||||
| if (ii > 0 && c != 2 && jj == 2) | |||||
| { | |||||
| DownLeft(c, api); | |||||
| c = c / 10; | |||||
| ii--; | |||||
| } | |||||
| if (ii > 0 && c != 2 && jj == 3) | |||||
| { | |||||
| UpRight(c, api); | |||||
| c = c / 10; | |||||
| ii--; | |||||
| } | |||||
| if (ii > 0 && c != 2 && jj == 4) | |||||
| { | |||||
| UpLeft(c, api); | |||||
| c = c / 10; | |||||
| ii--; | |||||
| } | |||||
| if (ii == 0) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto st = api.GetStudents(); | |||||
| if (!st.empty()) | |||||
| { | |||||
| if (api.GridToCell(st[0]->x) == api.GridToCell(self->x) && st[0]->y > self->y) aa = 1.5707963; | |||||
| if (api.GridToCell(st[0]->x) == api.GridToCell(self->x) && st[0]->y < self->y) aa = 4.7123889; | |||||
| if (api.GridToCell(st[0]->y) == api.GridToCell(self->y) && st[0]->x > self->x) aa = 0; | |||||
| if (api.GridToCell(st[0]->y) == api.GridToCell(self->y) && st[0]->x < self->x) aa = 3.1415926; | |||||
| api.Attack(aa); | |||||
| } | |||||
| } | |||||
| } | |||||
| if(st.empty()) | |||||
| { | |||||
| if (count==0) | |||||
| { | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(5000)); | |||||
| api.MoveUp(2 * 259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(540)); | |||||
| } | |||||
| if (count < 13) | |||||
| { | |||||
| api.MoveLeft(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(270)); | |||||
| count++; | |||||
| } | |||||
| if (count == 13) | |||||
| { | |||||
| api.MoveDown(2 * 259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(530)); | |||||
| count++; | |||||
| } | |||||
| } | |||||
| if (count == 14) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| c = DownRight(9, 5, self->x, self->y, api); | |||||
| iii = 13; count++; ii = -1; | |||||
| } | |||||
| if (count == 15) | |||||
| { | |||||
| if (iii > 0 && c != 2) | |||||
| { | |||||
| DownRight(c, api); | |||||
| c = c / 10; | |||||
| iii--; | |||||
| } | |||||
| if (iii == 1) | |||||
| { | |||||
| api.MoveDown(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(280)); | |||||
| count++; flag1 = 0; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| } | |||||
| } | |||||
| if (st.empty() && api.GridToCell(self->x) == 23 && api.GridToCell(self->y) == 12 && count == 16) | |||||
| { | |||||
| count++; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(5000)); | |||||
| api.MoveRight(259.6053998 * 2); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(550)); | |||||
| api.MoveDown(259.6053998 * 13); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(3500)); | |||||
| ii = -1; | |||||
| } | |||||
| if (count < 26 && count >= 17) | |||||
| { | |||||
| api.MoveRight(259.6053998); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(280)); | |||||
| count++; | |||||
| } | |||||
| if (count == 26) | |||||
| { | |||||
| ii = 0; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,735 @@ | |||||
| import PyAPI.structures as THUAI6 | |||||
| from PyAPI.Interface import * | |||||
| from typing import Union, Final, cast, List | |||||
| from PyAPI.constants import * | |||||
| import queue | |||||
| import time | |||||
| # add | |||||
| import numpy as np | |||||
| import math | |||||
| import copy | |||||
| from collections import deque | |||||
| class Setting: | |||||
| # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| @staticmethod | |||||
| def asynchronous() -> bool: | |||||
| return True | |||||
| # 选手需要依次将player0到player4的职业都定义 | |||||
| @staticmethod | |||||
| def studentType() -> List[THUAI6.StudentType]: | |||||
| return [THUAI6.StudentType.Athlete, THUAI6.StudentType.Teacher, THUAI6.StudentType.StraightAStudent, THUAI6.StudentType.Sunshine] | |||||
| @staticmethod | |||||
| def trickerType() -> THUAI6.TrickerType: | |||||
| return THUAI6.TrickerType.ANoisyPerson | |||||
| # 辅助函数 | |||||
| numOfGridPerCell: Final[int] = 1000 | |||||
| class AssistFunction: | |||||
| @staticmethod | |||||
| def CellToGrid(cell: int) -> int: | |||||
| return cell * numOfGridPerCell + numOfGridPerCell // 2 | |||||
| @staticmethod | |||||
| def GridToCell(grid: int) -> int: | |||||
| return grid // numOfGridPerCell | |||||
| # additional function_tricker | |||||
| pi = 3.1415926535 | |||||
| class cel: | |||||
| x:int | |||||
| y:int | |||||
| gridx:int | |||||
| gridy:int | |||||
| def __init__(self,x1:int, y1:int,type = 0):#type==0采用0-50赋值 | |||||
| if type ==0: | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| self.gridx = AssistFunction.CellToGrid(x1) | |||||
| self.gridy = AssistFunction.CellToGrid(y1) | |||||
| else: | |||||
| self.x = AssistFunction.GridToCell(x1) | |||||
| self.y = AssistFunction.GridToCell(y1) | |||||
| self.gridx = AssistFunction.CellToGrid(self.x) | |||||
| self.gridy = AssistFunction.CellToGrid(self.y) | |||||
| class precise_cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int, y1:int): | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| def gridtocell(x:int,y:int) -> cel:#返回坐标对应的cell | |||||
| return cel(AssistFunction.GridToCell(x),AssistFunction.GridToCell(y)) | |||||
| def distocell(x:int,y:int,dis:float,angle:float) -> List[cel]:#返回以一个点为中心长度为dis的cell | |||||
| allcell=[] | |||||
| for i in range(0,2*pi,angle): | |||||
| x1 = x+dis*math.cos(i) | |||||
| y1 = y+dis*math.sin(i) | |||||
| if x1>0 and x1<50000 and y1 >0 and y1<50000: | |||||
| the = gridtocell(x1,y1) | |||||
| if len(allcell) == 0: | |||||
| allcell.append(the) | |||||
| elif allcell[-1].x != the.x or allcell[-1].y != the.y: | |||||
| allcell.append(the) | |||||
| return allcell | |||||
| # Land = 1 | |||||
| # Wall = 2 | |||||
| # Grass = 3 | |||||
| # ClassRoom = 4 | |||||
| # Gate = 5 | |||||
| # HiddenGate = 6 | |||||
| # Window = 7 | |||||
| # Door3 = 8 | |||||
| # Door5 = 9 | |||||
| # Door6 = 10 | |||||
| # Chest = 11 | |||||
| def findpa(now:cel,dest:cel,map:List[List[THUAI6.PlaceType]]) -> cel: | |||||
| return cel(1,1) | |||||
| def dist(x1:int,y1:int,x2:int,y2:int) ->float: | |||||
| return math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) | |||||
| def toangle(nowx:int,nowy:int,tox:int,toy:int) ->float: | |||||
| if toy >= nowy: | |||||
| return math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| else: | |||||
| return -math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| def toangle_cel(now:cel,dest:cel) ->float: | |||||
| if dest.y >= now.y: | |||||
| return math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| else: | |||||
| return -math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| def findway_DFS(grid, start:cel, end:cel) -> List[cel]: | |||||
| row, col = 50,50 | |||||
| visited = set() | |||||
| path =[] | |||||
| class anan: | |||||
| angle:float | |||||
| to:int#0,1,2,3,下右上左 | |||||
| def __init__(self,angel:float,to:int) -> None: | |||||
| self.angle = angel | |||||
| self.to = to | |||||
| pass | |||||
| def azhe(jjj:anan)->float: | |||||
| return jjj.angle | |||||
| def help(now:cel,path:List[cel])->List[cel]: | |||||
| if now == end: | |||||
| return path | |||||
| if now in visited: | |||||
| oo = [] | |||||
| return oo | |||||
| visited.add(now) | |||||
| angle = toangle_cel(now,end) | |||||
| directions = [(1, 0), (0, 1), (-1, 0), (0, -1)] | |||||
| chae = [anan(abs(angle),0),anan(abs(angle-pi/2),1),anan(abs(angle-pi),2),anan(abs(angle-pi*1.5),3)] | |||||
| chae.sort(key = azhe) | |||||
| for i in chae: | |||||
| dx,dy = directions[i.to] | |||||
| new_x, new_y = now.x + dx, now.y + dy | |||||
| new =cel(new_x, new_y) | |||||
| if (grid[new_x][new_y]).value in [1,3,7,8,9,10]: | |||||
| path.append(new) | |||||
| visited.remove(now) | |||||
| return help(path[-1], path) | |||||
| return help(start,path = path) | |||||
| # additional function_tricker | |||||
| # additional function_student | |||||
| pi=3.1415926535 | |||||
| class Cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int,y1:int): | |||||
| self.x=x1 | |||||
| self.y=y1 | |||||
| #计算距离 | |||||
| def distance(target:Cel,api:IStudentAPI,ai)->float: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| return math.sqrt((target.x-selfinfo.x)**2+(target.y-selfinfo.y)**2) | |||||
| movetime=50 | |||||
| #为避免没到格子中心卡墙角,用cel规划路径,用grid精确地走 | |||||
| def Goto(target:Cel,api:IStudentAPI,ai)->None: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #当前格子坐标 | |||||
| #if self.nowpath==None: | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| nowpath=ai.BFSpath(selfinfo,cur,target,api)#规划路径 | |||||
| #print('self.nowpath') | |||||
| #for k in nowpath: | |||||
| # print(k.x,k.y) | |||||
| while len(nowpath)!=0:#能否这样判断不空 | |||||
| first=nowpath[0] | |||||
| x1=1000*first.x+500 | |||||
| y1=1000*first.y+500 | |||||
| #print('curgrid',selfx,selfy) | |||||
| #print('first ',x1,y1) | |||||
| if cur.x==first.x and cur.y==first.y: | |||||
| a=nowpath.pop(0) | |||||
| else: | |||||
| #print('not the same') | |||||
| #print('first',x1,y1) | |||||
| #print('cur',cur.x,cur.y) | |||||
| if x1==selfx and y1<selfy:api.MoveLeft(movetime) | |||||
| elif x1==selfx and y1>selfy:api.MoveRight(movetime) | |||||
| elif x1<selfx and y1==selfy:api.MoveUp(movetime) | |||||
| elif x1>selfx and y1==selfy:api.MoveDown(movetime) | |||||
| elif x1<selfx and y1<selfy:api.Move(movetime,-pi*3/4) | |||||
| elif x1<selfx and y1>selfy:api.Move(movetime,pi*3/4) | |||||
| elif x1>selfx and y1<selfy:api.Move(movetime,-pi/4) | |||||
| elif x1>selfx and y1>selfy:api.Move(movetime,pi/4) | |||||
| #api.PrintSelfInfo() | |||||
| selfinfo1=api.GetSelfInfo() | |||||
| selfx1=selfinfo1.x | |||||
| selfy1=selfinfo1.y | |||||
| #print('aftermove',selfx1,selfy1) | |||||
| #cur1=gridtocell(selfinfo1.x,selfinfo1.y) | |||||
| #print('info',cur1.x,cur1.y) | |||||
| if(selfx==selfx1 and selfy==selfy1): | |||||
| #print('not moved') | |||||
| api.MoveRight(movetime) | |||||
| # api.MoveRight(movetime) | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #print('State ',selfinfo.playerState.name) | |||||
| #api.PrintSelfInfo() | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| # additional function_student | |||||
| class AI(IAI): | |||||
| map = None | |||||
| # tricker | |||||
| allgates_tr=[] | |||||
| allwindows_tr = [] | |||||
| alldoor3_tr = [] | |||||
| alldoor5_tr = [] | |||||
| alldoor6_tr = [] | |||||
| allclassrooms_tr = [] | |||||
| allboxes_tr = [] | |||||
| allhiddengates_tr = [] | |||||
| studentposition_previous_frame=None#precise_cel | |||||
| studentposition_this_frame=None#precise_cel | |||||
| my_previous_frame=None#precise_cel | |||||
| my_this_frame=None#precise_cel | |||||
| now_path=None#List[cel] | |||||
| move_along_path=None#bool | |||||
| def closest_chest(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allboxes_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxChestProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def closest_classroom(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allclassrooms_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxClassroomProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def wall_exist_precise(self,now:precise_cel,dest:precise_cel)->bool: | |||||
| k = (dest.y-now.y)/(dest.x-now.x) | |||||
| # y = k(x-now.x)+now.y | |||||
| long = dest.x-now.x | |||||
| for i in range(0,long,801): | |||||
| y1= k*i+now.y | |||||
| ok = cel(now.x+i,y1) | |||||
| if self.map[ok.x][ok.y].value in [2,4,5,6,11]: | |||||
| return True | |||||
| return False | |||||
| def near_chest(self,now:cel,api)->bool: | |||||
| eight = [cel(now.x,now.y+1),cel(now.x,now.y-1),cel(now.x+1,now.y),cel(now.x-1,now.y),cel(now.x+1,now.y+1),cel(now.x-1,now.y-1),cel(now.x+1,now.y-1),cel(now.x-1,now.y+1)] | |||||
| for i in eight: | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if self.map[i.x][i.y].name == 'Chest' and jindu < Constants.maxChestProgress: | |||||
| return True | |||||
| return False | |||||
| # student | |||||
| allgates=[] | |||||
| allwindows = [] | |||||
| alldoors3 = [] | |||||
| alldoors5 = [] | |||||
| alldoors6 = [] | |||||
| allclassrooms = [] | |||||
| allboxes = [] | |||||
| allhiddengates = [] | |||||
| last_cel:Cel | |||||
| cur_cel:Cel | |||||
| #movetime=10#移动时间是多少? | |||||
| #nowpath:deque[Cel]#存BFS得出的路径直到路径走�?? | |||||
| #move_along_path=0#直到走完路径,避免每帧调用一�?? | |||||
| def Todestin(self,api:IStudentAPI): | |||||
| return | |||||
| def Iscanreach(self,point:Cel,target:Cel)->bool:#判断两个点能否直接到 | |||||
| if(target.x<0 or target.x>50 | |||||
| or target.y<0 or target.y>50 | |||||
| or self.map[target.x][target.y].name=='Wall' | |||||
| or (target.x==point.x and target.y==point.y)):#或超出地图、墙壁、与初始点重�?? | |||||
| return False | |||||
| else:#上下左右�?? | |||||
| if(abs(point.x-target.x)+abs(point.y-target.y)==1): | |||||
| return True | |||||
| else:#斜对角线是门的话先不�?? | |||||
| name1=self.map[target.x][target.y].name | |||||
| if(name1=='Door3' or name1=='Door5' or name1=='Door6'): | |||||
| return False | |||||
| else: | |||||
| name2=self.map[target.x][point.y].name | |||||
| name3=self.map[point.x][target.y].name | |||||
| if name2=='Wall' or name3=='Wall': | |||||
| return False | |||||
| else: | |||||
| return True | |||||
| #对角线的两侧有墙壁的话也不行�??45度的时候走不过�?? | |||||
| #隐藏门和大校门? | |||||
| def getSurroundCell(self,info:THUAI6.Student,point:Cel)->deque[Cel]:#找到该点周围8个点中能到的 | |||||
| surroundCell=[]#存能到的 | |||||
| curprop=info.prop | |||||
| global next | |||||
| for xi in range(point.x-1,point.x+2): | |||||
| for yj in range(point.y-1,point.y+2): | |||||
| other=Cel(xi,yj) | |||||
| if(self.Iscanreach(point,other)): | |||||
| sname=self.map[other.x][other.y].name | |||||
| if(sname=='Door3'):#是门的话,判断有无对应钥匙? | |||||
| if 1 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door5'): | |||||
| if 2 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door6'): | |||||
| if 3 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Window'):#是窗的话爬出�?? | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| else: | |||||
| next=other | |||||
| surroundCell.append(next) | |||||
| return surroundCell | |||||
| #判断坐标是否在list里,searched用到,也是数字相同但不同对象 | |||||
| def InList(self,point:Cel,llist:List)->bool: | |||||
| for k in llist: | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #坐标是否在字典的key里,每一次初始化类的对象,尽管数字相同但还是不同对象,可以重�??=?还可以?(后面简化一�?? | |||||
| def InDic(self,point:Cel,DDix:dict)->bool: | |||||
| for k in DDix.keys(): | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #广度搜索找路径,如果 | |||||
| #路径不存在的情况�?? | |||||
| def BFSpath(self,info:THUAI6.Student,point:Cel,target:Cel,api:IStudentAPI)->List[Cel]: | |||||
| #self_info=api.GetSelfInfo(self) | |||||
| searched=[]#已经搜过的点 | |||||
| q=deque() | |||||
| q.append(point)#初始点加�?? | |||||
| #定义搜索过的点的父节�?? | |||||
| parents=dict() | |||||
| path=[target] | |||||
| while q: | |||||
| cur_=q.popleft() | |||||
| #如果点没有被搜过 | |||||
| if not self.InList(cur_,searched): | |||||
| #api.Print('not serached'+'cur_ '+str(cur_.x)+' '+str(cur_.y)) | |||||
| if(cur_.x !=target.x or cur_.y!=target.y): | |||||
| #print('!=here') | |||||
| sur=self.getSurroundCell(info,cur_) | |||||
| for k in sur: | |||||
| if not self.InList(k,searched): | |||||
| q.append(k) | |||||
| if not self.InDic(k,parents): | |||||
| parents[k]=cur_ | |||||
| searched.append(cur_) | |||||
| else: | |||||
| #print('cur_=target') | |||||
| key=Cel(target.x,target.y) | |||||
| while key.x!=point.x or key.y!=point.y: | |||||
| for k in parents: | |||||
| if k.x==key.x and k.y==key.y: | |||||
| father=parents[k] | |||||
| path.append(father) | |||||
| key=father | |||||
| #往回找到路径后,将列表反转 | |||||
| path.reverse() | |||||
| break | |||||
| return path | |||||
| #�?7间教室学�? | |||||
| def Study(self,api:IStudentAPI)->None: | |||||
| leng=len(self.allclassrooms) | |||||
| ifdone=np.zeros((leng,1),dtype=bool) | |||||
| class_count=0#已经学的教室的间�? | |||||
| #找到距离self最近的教室并前往,途中捡道�? | |||||
| while class_count<7: | |||||
| my_filter=filter(lambda x:ifdone[self.allclassrooms.index(x)]==0,self.allclassrooms)#已经学过的教室就不要去了 | |||||
| result=list(my_filter) | |||||
| dis_to_class=[] | |||||
| for k in result: | |||||
| dis=distance(k,api,self) | |||||
| dis_to_class.append(dis) | |||||
| num=dis_to_class.index(min(dis_to_class))#result中距离最近的教室的编�? | |||||
| which_class=result[num] | |||||
| x1=which_class.x | |||||
| y1=which_class.y | |||||
| #print('nearest ',x1,y1) | |||||
| #在classroom列表中找回这个点的位置,并在ifdone中修改标�? | |||||
| index_of_class=next((i for i,xx in enumerate(self.allclassrooms) if xx.x==x1 and xx.y==y1),None) | |||||
| ifdone[index_of_class]=1 | |||||
| Goto(which_class,api,self) | |||||
| api.StartLearning()#开始学�? | |||||
| class_count=class_count+1 | |||||
| # function | |||||
| def __init__(self, pID: int): | |||||
| self.__playerID = pID | |||||
| def StudentPlay(self, api: IStudentAPI) -> None: | |||||
| return | |||||
| # 鍏叡鎿嶄綔 | |||||
| if self.map==None: | |||||
| self.map=api.GetFullMap() | |||||
| xi=0 | |||||
| yj=0 | |||||
| for i in self.map: | |||||
| yj=0 | |||||
| for j in i: | |||||
| if j.name=='ClassRoom': | |||||
| self.allclassrooms.append(Cel(xi,yj)) | |||||
| if j.name=='Gate': | |||||
| self.allgates.append(Cel(xi,yj)) | |||||
| if j.name=='HiddenGate': | |||||
| self.allhiddengates.append(Cel(xi,yj)) | |||||
| if j.name=='Window':#��һ���������ĺ������� | |||||
| self.allwindows.append(Cel(xi,yj)) | |||||
| if j.name=='Door3': | |||||
| self.alldoors3.append(Cel(xi,yj)) | |||||
| if j.name=='Door5': | |||||
| self.alldoors5.append(Cel(xi,yj)) | |||||
| if j.name=='Door6': | |||||
| self.alldoors6.append(Cel(xi,yj)) | |||||
| if j.name=='Chest': | |||||
| self.allboxes.append(Cel(xi,yj)) | |||||
| yj=yj+1 | |||||
| xi=xi+1 | |||||
| print(self.map) | |||||
| if self.__playerID == 0: | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| #api.StartLearning() | |||||
| #api.PrintSelfInfo() | |||||
| #print('end') | |||||
| #self.Study(api) | |||||
| elif self.__playerID == 1: | |||||
| # 鐜╁�??1鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 2: | |||||
| # 鐜╁�??2鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 3: | |||||
| # 鐜╁�??3鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| # 鍙互鍐欐垚if self.__playerID<2涔嬬被鐨勫啓娉� | |||||
| # 鍏叡鎿嶄綔 | |||||
| return | |||||
| def TrickerPlay(self, api: ITrickerAPI) -> None: | |||||
| #api.EndAllAction() | |||||
| if self.map is None: | |||||
| self.map = api.GetFullMap() | |||||
| yj = 0 | |||||
| xi = 0 | |||||
| for i in self.map: | |||||
| yj = 0 | |||||
| for j in i: | |||||
| if j.name == 'ClassRoom': | |||||
| self.allclassrooms_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Gate': | |||||
| self.allgates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'HiddenGate': | |||||
| self.allhiddengates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Window': | |||||
| self.allwindows_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door3': | |||||
| self.alldoor3_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door5': | |||||
| self.alldoor5_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door6': | |||||
| self.alldoor6_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Chest': | |||||
| self.allboxes_tr.append(cel(xi,yj)) | |||||
| yj = yj + 1 | |||||
| xi = xi + 1 | |||||
| if self.my_previous_frame is None: | |||||
| self.my_previous_frame = precise_cel(0,0) | |||||
| selfInfo = api.GetSelfInfo() | |||||
| self.my_this_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.trickerType.name == 'ANoisyPerson': | |||||
| student_see = api.GetStudents() | |||||
| now = cel(selfInfo.x,selfInfo.y,1) | |||||
| dest= cel(0,0) | |||||
| this_dest:cel | |||||
| if self.near_chest(now,api): | |||||
| api.StartOpenChest() | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| if len(student_see) == 0: | |||||
| self.move_along_path = True | |||||
| dest = cel(0,0) | |||||
| if selfInfo.trickDesire == 0: | |||||
| #找教室 | |||||
| dest = self.closest_classroom(now.x,now.y) | |||||
| else: | |||||
| dis = float(ANoisyPerson.alertnessRadius)/selfInfo.trickDesire | |||||
| a=1 | |||||
| else: | |||||
| self.move_along_path = False | |||||
| closestudent:THUAI6.Student | |||||
| closestudent = None | |||||
| cloest = 1000000000000 | |||||
| attacklong = Constants.basicAttackShortRange | |||||
| for i in student_see: | |||||
| dis = dist(i.x,i.y,selfInfo.x,selfInfo.y) | |||||
| if dis < cloest : | |||||
| cloest = dis | |||||
| if i.playerState.name != 'Addicted': | |||||
| closestudent = i | |||||
| if closestudent is not None: | |||||
| dest = cel(closestudent.x,closestudent.y,1) | |||||
| else: | |||||
| dest = self.closest_chest(now.x,now.y,api) | |||||
| #self.move_along_path = True | |||||
| if closestudent is not None: | |||||
| if closestudent.studentType.name == 'Athlete': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Teacher': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'StraightAStudent': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Sunshine': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| # if self.now_path is None and self.move_along_path == True: | |||||
| # self.now_path = findway_DFS(self.map,now,dest) | |||||
| # if self.move_along_path == True: | |||||
| # dest = self.now_path[1] | |||||
| this_dest = dest | |||||
| angle = toangle(selfInfo.x,selfInfo.y,this_dest.gridx,this_dest.gridy) | |||||
| if self.my_previous_frame.x == self.my_this_frame.x and self.my_previous_frame.y == self.my_this_frame.y: | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.MoveUp(10) | |||||
| return | |||||
| self.my_previous_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.Move(60,angle) | |||||
| # hide in grass | |||||
| # attack | |||||
| # trick around | |||||
| # find chest | |||||
| # find classroom | |||||
| elif selfInfo.trickerType.name == 'Idol': | |||||
| a=1 | |||||
| elif selfInfo.trickerType.name == 'Assassin': | |||||
| a=1 | |||||
| return | |||||
| # playerID=4, GUID=678, x=21500, y=25500 | |||||
| # speed=3960, view range=15600, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, 0.0, -1.0] | |||||
| # state=Idle, bullet=CommonAttackOfTricker, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=Assassin, trick desire=2.4405367634838275, class volume=0.0 | |||||
| # buff= | |||||
| # playerID=2, GUID=694, x=12500, y=26500 | |||||
| # speed=2880, view range=9000, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, -1.0, -1.0] | |||||
| # state=Idle, bullet=NullBulletType, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=StraightAStudent, determination=3300000, addiction=0, danger alert=0.0 | |||||
| # learning speed=135, encourage speed=100, encourage progress=0, rouse progress=0 | |||||
| # buff= | |||||
| @@ -0,0 +1,735 @@ | |||||
| import PyAPI.structures as THUAI6 | |||||
| from PyAPI.Interface import * | |||||
| from typing import Union, Final, cast, List | |||||
| from PyAPI.constants import * | |||||
| import queue | |||||
| import time | |||||
| # add | |||||
| import numpy as np | |||||
| import math | |||||
| import copy | |||||
| from collections import deque | |||||
| class Setting: | |||||
| # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| @staticmethod | |||||
| def asynchronous() -> bool: | |||||
| return True | |||||
| # 选手需要依次将player0到player4的职业都定义 | |||||
| @staticmethod | |||||
| def studentType() -> List[THUAI6.StudentType]: | |||||
| return [THUAI6.StudentType.Athlete, THUAI6.StudentType.Teacher, THUAI6.StudentType.StraightAStudent, THUAI6.StudentType.Sunshine] | |||||
| @staticmethod | |||||
| def trickerType() -> THUAI6.TrickerType: | |||||
| return THUAI6.TrickerType.ANoisyPerson | |||||
| # 辅助函数 | |||||
| numOfGridPerCell: Final[int] = 1000 | |||||
| class AssistFunction: | |||||
| @staticmethod | |||||
| def CellToGrid(cell: int) -> int: | |||||
| return cell * numOfGridPerCell + numOfGridPerCell // 2 | |||||
| @staticmethod | |||||
| def GridToCell(grid: int) -> int: | |||||
| return grid // numOfGridPerCell | |||||
| # additional function_tricker | |||||
| pi = 3.1415926535 | |||||
| class cel: | |||||
| x:int | |||||
| y:int | |||||
| gridx:int | |||||
| gridy:int | |||||
| def __init__(self,x1:int, y1:int,type = 0):#type==0采用0-50赋值 | |||||
| if type ==0: | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| self.gridx = AssistFunction.CellToGrid(x1) | |||||
| self.gridy = AssistFunction.CellToGrid(y1) | |||||
| else: | |||||
| self.x = AssistFunction.GridToCell(x1) | |||||
| self.y = AssistFunction.GridToCell(y1) | |||||
| self.gridx = AssistFunction.CellToGrid(self.x) | |||||
| self.gridy = AssistFunction.CellToGrid(self.y) | |||||
| class precise_cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int, y1:int): | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| def gridtocell(x:int,y:int) -> cel:#返回坐标对应的cell | |||||
| return cel(AssistFunction.GridToCell(x),AssistFunction.GridToCell(y)) | |||||
| def distocell(x:int,y:int,dis:float,angle:float) -> List[cel]:#返回以一个点为中心长度为dis的cell | |||||
| allcell=[] | |||||
| for i in range(0,2*pi,angle): | |||||
| x1 = x+dis*math.cos(i) | |||||
| y1 = y+dis*math.sin(i) | |||||
| if x1>0 and x1<50000 and y1 >0 and y1<50000: | |||||
| the = gridtocell(x1,y1) | |||||
| if len(allcell) == 0: | |||||
| allcell.append(the) | |||||
| elif allcell[-1].x != the.x or allcell[-1].y != the.y: | |||||
| allcell.append(the) | |||||
| return allcell | |||||
| # Land = 1 | |||||
| # Wall = 2 | |||||
| # Grass = 3 | |||||
| # ClassRoom = 4 | |||||
| # Gate = 5 | |||||
| # HiddenGate = 6 | |||||
| # Window = 7 | |||||
| # Door3 = 8 | |||||
| # Door5 = 9 | |||||
| # Door6 = 10 | |||||
| # Chest = 11 | |||||
| def findpa(now:cel,dest:cel,map:List[List[THUAI6.PlaceType]]) -> cel: | |||||
| return cel(1,1) | |||||
| def dist(x1:int,y1:int,x2:int,y2:int) ->float: | |||||
| return math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) | |||||
| def toangle(nowx:int,nowy:int,tox:int,toy:int) ->float: | |||||
| if toy >= nowy: | |||||
| return math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| else: | |||||
| return -math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| def toangle_cel(now:cel,dest:cel) ->float: | |||||
| if dest.y >= now.y: | |||||
| return math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| else: | |||||
| return -math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| def findway_DFS(grid, start:cel, end:cel) -> List[cel]: | |||||
| row, col = 50,50 | |||||
| visited = set() | |||||
| path =[] | |||||
| class anan: | |||||
| angle:float | |||||
| to:int#0,1,2,3,下右上左 | |||||
| def __init__(self,angel:float,to:int) -> None: | |||||
| self.angle = angel | |||||
| self.to = to | |||||
| pass | |||||
| def azhe(jjj:anan)->float: | |||||
| return jjj.angle | |||||
| def help(now:cel,path:List[cel])->List[cel]: | |||||
| if now == end: | |||||
| return path | |||||
| if now in visited: | |||||
| oo = [] | |||||
| return oo | |||||
| visited.add(now) | |||||
| angle = toangle_cel(now,end) | |||||
| directions = [(1, 0), (0, 1), (-1, 0), (0, -1)] | |||||
| chae = [anan(abs(angle),0),anan(abs(angle-pi/2),1),anan(abs(angle-pi),2),anan(abs(angle-pi*1.5),3)] | |||||
| chae.sort(key = azhe) | |||||
| for i in chae: | |||||
| dx,dy = directions[i.to] | |||||
| new_x, new_y = now.x + dx, now.y + dy | |||||
| new =cel(new_x, new_y) | |||||
| if (grid[new_x][new_y]).value in [1,3,7,8,9,10]: | |||||
| path.append(new) | |||||
| visited.remove(now) | |||||
| return help(path[-1], path) | |||||
| return help(start,path = path) | |||||
| # additional function_tricker | |||||
| # additional function_student | |||||
| pi=3.1415926535 | |||||
| class Cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int,y1:int): | |||||
| self.x=x1 | |||||
| self.y=y1 | |||||
| #计算距离 | |||||
| def distance(target:Cel,api:IStudentAPI,ai)->float: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| return math.sqrt((target.x-selfinfo.x)**2+(target.y-selfinfo.y)**2) | |||||
| movetime=50 | |||||
| #为避免没到格子中心卡墙角,用cel规划路径,用grid精确地走 | |||||
| def Goto(target:Cel,api:IStudentAPI,ai)->None: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #当前格子坐标 | |||||
| #if self.nowpath==None: | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| nowpath=ai.BFSpath(selfinfo,cur,target,api)#规划路径 | |||||
| #print('self.nowpath') | |||||
| #for k in nowpath: | |||||
| # print(k.x,k.y) | |||||
| while len(nowpath)!=0:#能否这样判断不空 | |||||
| first=nowpath[0] | |||||
| x1=1000*first.x+500 | |||||
| y1=1000*first.y+500 | |||||
| #print('curgrid',selfx,selfy) | |||||
| #print('first ',x1,y1) | |||||
| if cur.x==first.x and cur.y==first.y: | |||||
| a=nowpath.pop(0) | |||||
| else: | |||||
| #print('not the same') | |||||
| #print('first',x1,y1) | |||||
| #print('cur',cur.x,cur.y) | |||||
| if x1==selfx and y1<selfy:api.MoveLeft(movetime) | |||||
| elif x1==selfx and y1>selfy:api.MoveRight(movetime) | |||||
| elif x1<selfx and y1==selfy:api.MoveUp(movetime) | |||||
| elif x1>selfx and y1==selfy:api.MoveDown(movetime) | |||||
| elif x1<selfx and y1<selfy:api.Move(movetime,-pi*3/4) | |||||
| elif x1<selfx and y1>selfy:api.Move(movetime,pi*3/4) | |||||
| elif x1>selfx and y1<selfy:api.Move(movetime,-pi/4) | |||||
| elif x1>selfx and y1>selfy:api.Move(movetime,pi/4) | |||||
| #api.PrintSelfInfo() | |||||
| selfinfo1=api.GetSelfInfo() | |||||
| selfx1=selfinfo1.x | |||||
| selfy1=selfinfo1.y | |||||
| #print('aftermove',selfx1,selfy1) | |||||
| #cur1=gridtocell(selfinfo1.x,selfinfo1.y) | |||||
| #print('info',cur1.x,cur1.y) | |||||
| if(selfx==selfx1 and selfy==selfy1): | |||||
| #print('not moved') | |||||
| api.MoveRight(movetime) | |||||
| # api.MoveRight(movetime) | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #print('State ',selfinfo.playerState.name) | |||||
| #api.PrintSelfInfo() | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| # additional function_student | |||||
| class AI(IAI): | |||||
| map = None | |||||
| # tricker | |||||
| allgates_tr=[] | |||||
| allwindows_tr = [] | |||||
| alldoor3_tr = [] | |||||
| alldoor5_tr = [] | |||||
| alldoor6_tr = [] | |||||
| allclassrooms_tr = [] | |||||
| allboxes_tr = [] | |||||
| allhiddengates_tr = [] | |||||
| studentposition_previous_frame=None#precise_cel | |||||
| studentposition_this_frame=None#precise_cel | |||||
| my_previous_frame=None#precise_cel | |||||
| my_this_frame=None#precise_cel | |||||
| now_path=None#List[cel] | |||||
| move_along_path=None#bool | |||||
| def closest_chest(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allboxes_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxChestProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def closest_classroom(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allclassrooms_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxClassroomProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def wall_exist_precise(self,now:precise_cel,dest:precise_cel)->bool: | |||||
| k = (dest.y-now.y)/(dest.x-now.x) | |||||
| # y = k(x-now.x)+now.y | |||||
| long = dest.x-now.x | |||||
| for i in range(0,long,801): | |||||
| y1= k*i+now.y | |||||
| ok = cel(now.x+i,y1) | |||||
| if self.map[ok.x][ok.y].value in [2,4,5,6,11]: | |||||
| return True | |||||
| return False | |||||
| def near_chest(self,now:cel,api)->bool: | |||||
| eight = [cel(now.x,now.y+1),cel(now.x,now.y-1),cel(now.x+1,now.y),cel(now.x-1,now.y),cel(now.x+1,now.y+1),cel(now.x-1,now.y-1),cel(now.x+1,now.y-1),cel(now.x-1,now.y+1)] | |||||
| for i in eight: | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if self.map[i.x][i.y].name == 'Chest' and jindu < Constants.maxChestProgress: | |||||
| return True | |||||
| return False | |||||
| # student | |||||
| allgates=[] | |||||
| allwindows = [] | |||||
| alldoors3 = [] | |||||
| alldoors5 = [] | |||||
| alldoors6 = [] | |||||
| allclassrooms = [] | |||||
| allboxes = [] | |||||
| allhiddengates = [] | |||||
| last_cel:Cel | |||||
| cur_cel:Cel | |||||
| #movetime=10#移动时间是多少? | |||||
| #nowpath:deque[Cel]#存BFS得出的路径直到路径走�?? | |||||
| #move_along_path=0#直到走完路径,避免每帧调用一�?? | |||||
| def Todestin(self,api:IStudentAPI): | |||||
| return | |||||
| def Iscanreach(self,point:Cel,target:Cel)->bool:#判断两个点能否直接到 | |||||
| if(target.x<0 or target.x>50 | |||||
| or target.y<0 or target.y>50 | |||||
| or self.map[target.x][target.y].name=='Wall' | |||||
| or (target.x==point.x and target.y==point.y)):#或超出地图、墙壁、与初始点重�?? | |||||
| return False | |||||
| else:#上下左右�?? | |||||
| if(abs(point.x-target.x)+abs(point.y-target.y)==1): | |||||
| return True | |||||
| else:#斜对角线是门的话先不�?? | |||||
| name1=self.map[target.x][target.y].name | |||||
| if(name1=='Door3' or name1=='Door5' or name1=='Door6'): | |||||
| return False | |||||
| else: | |||||
| name2=self.map[target.x][point.y].name | |||||
| name3=self.map[point.x][target.y].name | |||||
| if name2=='Wall' or name3=='Wall': | |||||
| return False | |||||
| else: | |||||
| return True | |||||
| #对角线的两侧有墙壁的话也不行�??45度的时候走不过�?? | |||||
| #隐藏门和大校门? | |||||
| def getSurroundCell(self,info:THUAI6.Student,point:Cel)->deque[Cel]:#找到该点周围8个点中能到的 | |||||
| surroundCell=[]#存能到的 | |||||
| curprop=info.prop | |||||
| global next | |||||
| for xi in range(point.x-1,point.x+2): | |||||
| for yj in range(point.y-1,point.y+2): | |||||
| other=Cel(xi,yj) | |||||
| if(self.Iscanreach(point,other)): | |||||
| sname=self.map[other.x][other.y].name | |||||
| if(sname=='Door3'):#是门的话,判断有无对应钥匙? | |||||
| if 1 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door5'): | |||||
| if 2 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door6'): | |||||
| if 3 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Window'):#是窗的话爬出�?? | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| else: | |||||
| next=other | |||||
| surroundCell.append(next) | |||||
| return surroundCell | |||||
| #判断坐标是否在list里,searched用到,也是数字相同但不同对象 | |||||
| def InList(self,point:Cel,llist:List)->bool: | |||||
| for k in llist: | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #坐标是否在字典的key里,每一次初始化类的对象,尽管数字相同但还是不同对象,可以重�??=?还可以?(后面简化一�?? | |||||
| def InDic(self,point:Cel,DDix:dict)->bool: | |||||
| for k in DDix.keys(): | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #广度搜索找路径,如果 | |||||
| #路径不存在的情况�?? | |||||
| def BFSpath(self,info:THUAI6.Student,point:Cel,target:Cel,api:IStudentAPI)->List[Cel]: | |||||
| #self_info=api.GetSelfInfo(self) | |||||
| searched=[]#已经搜过的点 | |||||
| q=deque() | |||||
| q.append(point)#初始点加�?? | |||||
| #定义搜索过的点的父节�?? | |||||
| parents=dict() | |||||
| path=[target] | |||||
| while q: | |||||
| cur_=q.popleft() | |||||
| #如果点没有被搜过 | |||||
| if not self.InList(cur_,searched): | |||||
| #api.Print('not serached'+'cur_ '+str(cur_.x)+' '+str(cur_.y)) | |||||
| if(cur_.x !=target.x or cur_.y!=target.y): | |||||
| #print('!=here') | |||||
| sur=self.getSurroundCell(info,cur_) | |||||
| for k in sur: | |||||
| if not self.InList(k,searched): | |||||
| q.append(k) | |||||
| if not self.InDic(k,parents): | |||||
| parents[k]=cur_ | |||||
| searched.append(cur_) | |||||
| else: | |||||
| #print('cur_=target') | |||||
| key=Cel(target.x,target.y) | |||||
| while key.x!=point.x or key.y!=point.y: | |||||
| for k in parents: | |||||
| if k.x==key.x and k.y==key.y: | |||||
| father=parents[k] | |||||
| path.append(father) | |||||
| key=father | |||||
| #往回找到路径后,将列表反转 | |||||
| path.reverse() | |||||
| break | |||||
| return path | |||||
| #�?7间教室学�? | |||||
| def Study(self,api:IStudentAPI)->None: | |||||
| leng=len(self.allclassrooms) | |||||
| ifdone=np.zeros((leng,1),dtype=bool) | |||||
| class_count=0#已经学的教室的间�? | |||||
| #找到距离self最近的教室并前往,途中捡道�? | |||||
| while class_count<7: | |||||
| my_filter=filter(lambda x:ifdone[self.allclassrooms.index(x)]==0,self.allclassrooms)#已经学过的教室就不要去了 | |||||
| result=list(my_filter) | |||||
| dis_to_class=[] | |||||
| for k in result: | |||||
| dis=distance(k,api,self) | |||||
| dis_to_class.append(dis) | |||||
| num=dis_to_class.index(min(dis_to_class))#result中距离最近的教室的编�? | |||||
| which_class=result[num] | |||||
| x1=which_class.x | |||||
| y1=which_class.y | |||||
| #print('nearest ',x1,y1) | |||||
| #在classroom列表中找回这个点的位置,并在ifdone中修改标�? | |||||
| index_of_class=next((i for i,xx in enumerate(self.allclassrooms) if xx.x==x1 and xx.y==y1),None) | |||||
| ifdone[index_of_class]=1 | |||||
| Goto(which_class,api,self) | |||||
| api.StartLearning()#开始学�? | |||||
| class_count=class_count+1 | |||||
| # function | |||||
| def __init__(self, pID: int): | |||||
| self.__playerID = pID | |||||
| def StudentPlay(self, api: IStudentAPI) -> None: | |||||
| return | |||||
| # 鍏叡鎿嶄綔 | |||||
| if self.map==None: | |||||
| self.map=api.GetFullMap() | |||||
| xi=0 | |||||
| yj=0 | |||||
| for i in self.map: | |||||
| yj=0 | |||||
| for j in i: | |||||
| if j.name=='ClassRoom': | |||||
| self.allclassrooms.append(Cel(xi,yj)) | |||||
| if j.name=='Gate': | |||||
| self.allgates.append(Cel(xi,yj)) | |||||
| if j.name=='HiddenGate': | |||||
| self.allhiddengates.append(Cel(xi,yj)) | |||||
| if j.name=='Window':#��һ���������ĺ������� | |||||
| self.allwindows.append(Cel(xi,yj)) | |||||
| if j.name=='Door3': | |||||
| self.alldoors3.append(Cel(xi,yj)) | |||||
| if j.name=='Door5': | |||||
| self.alldoors5.append(Cel(xi,yj)) | |||||
| if j.name=='Door6': | |||||
| self.alldoors6.append(Cel(xi,yj)) | |||||
| if j.name=='Chest': | |||||
| self.allboxes.append(Cel(xi,yj)) | |||||
| yj=yj+1 | |||||
| xi=xi+1 | |||||
| print(self.map) | |||||
| if self.__playerID == 0: | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| #api.StartLearning() | |||||
| #api.PrintSelfInfo() | |||||
| #print('end') | |||||
| #self.Study(api) | |||||
| elif self.__playerID == 1: | |||||
| # 鐜╁�??1鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 2: | |||||
| # 鐜╁�??2鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 3: | |||||
| # 鐜╁�??3鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| # 鍙互鍐欐垚if self.__playerID<2涔嬬被鐨勫啓娉� | |||||
| # 鍏叡鎿嶄綔 | |||||
| return | |||||
| def TrickerPlay(self, api: ITrickerAPI) -> None: | |||||
| #api.EndAllAction() | |||||
| if self.map is None: | |||||
| self.map = api.GetFullMap() | |||||
| yj = 0 | |||||
| xi = 0 | |||||
| for i in self.map: | |||||
| yj = 0 | |||||
| for j in i: | |||||
| if j.name == 'ClassRoom': | |||||
| self.allclassrooms_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Gate': | |||||
| self.allgates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'HiddenGate': | |||||
| self.allhiddengates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Window': | |||||
| self.allwindows_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door3': | |||||
| self.alldoor3_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door5': | |||||
| self.alldoor5_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door6': | |||||
| self.alldoor6_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Chest': | |||||
| self.allboxes_tr.append(cel(xi,yj)) | |||||
| yj = yj + 1 | |||||
| xi = xi + 1 | |||||
| if self.my_previous_frame is None: | |||||
| self.my_previous_frame = precise_cel(0,0) | |||||
| selfInfo = api.GetSelfInfo() | |||||
| self.my_this_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.trickerType.name == 'ANoisyPerson': | |||||
| student_see = api.GetStudents() | |||||
| now = cel(selfInfo.x,selfInfo.y,1) | |||||
| dest= cel(0,0) | |||||
| this_dest:cel | |||||
| if self.near_chest(now,api): | |||||
| api.StartOpenChest() | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| if len(student_see) == 0: | |||||
| self.move_along_path = True | |||||
| dest = cel(0,0) | |||||
| if selfInfo.trickDesire == 0: | |||||
| #找教室 | |||||
| dest = self.closest_classroom(now.x,now.y) | |||||
| else: | |||||
| dis = float(ANoisyPerson.alertnessRadius)/selfInfo.trickDesire | |||||
| a=1 | |||||
| else: | |||||
| self.move_along_path = False | |||||
| closestudent:THUAI6.Student | |||||
| closestudent = None | |||||
| cloest = 1000000000000 | |||||
| attacklong = Constants.basicAttackShortRange | |||||
| for i in student_see: | |||||
| dis = dist(i.x,i.y,selfInfo.x,selfInfo.y) | |||||
| if dis < cloest : | |||||
| cloest = dis | |||||
| if i.playerState.name != 'Addicted': | |||||
| closestudent = i | |||||
| if closestudent is not None: | |||||
| dest = cel(closestudent.x,closestudent.y,1) | |||||
| else: | |||||
| dest = self.closest_chest(now.x,now.y,api) | |||||
| #self.move_along_path = True | |||||
| if closestudent is not None: | |||||
| if closestudent.studentType.name == 'Athlete': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Teacher': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'StraightAStudent': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Sunshine': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| # if self.now_path is None and self.move_along_path == True: | |||||
| # self.now_path = findway_DFS(self.map,now,dest) | |||||
| # if self.move_along_path == True: | |||||
| # dest = self.now_path[1] | |||||
| this_dest = dest | |||||
| angle = toangle(selfInfo.x,selfInfo.y,this_dest.gridx,this_dest.gridy) | |||||
| if self.my_previous_frame.x == self.my_this_frame.x and self.my_previous_frame.y == self.my_this_frame.y: | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.MoveUp(10) | |||||
| return | |||||
| self.my_previous_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.Move(60,angle) | |||||
| # hide in grass | |||||
| # attack | |||||
| # trick around | |||||
| # find chest | |||||
| # find classroom | |||||
| elif selfInfo.trickerType.name == 'Idol': | |||||
| a=1 | |||||
| elif selfInfo.trickerType.name == 'Assassin': | |||||
| a=1 | |||||
| return | |||||
| # playerID=4, GUID=678, x=21500, y=25500 | |||||
| # speed=3960, view range=15600, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, 0.0, -1.0] | |||||
| # state=Idle, bullet=CommonAttackOfTricker, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=Assassin, trick desire=2.4405367634838275, class volume=0.0 | |||||
| # buff= | |||||
| # playerID=2, GUID=694, x=12500, y=26500 | |||||
| # speed=2880, view range=9000, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, -1.0, -1.0] | |||||
| # state=Idle, bullet=NullBulletType, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=StraightAStudent, determination=3300000, addiction=0, danger alert=0.0 | |||||
| # learning speed=135, encourage speed=100, encourage progress=0, rouse progress=0 | |||||
| # buff= | |||||
| @@ -0,0 +1,735 @@ | |||||
| import PyAPI.structures as THUAI6 | |||||
| from PyAPI.Interface import * | |||||
| from typing import Union, Final, cast, List | |||||
| from PyAPI.constants import * | |||||
| import queue | |||||
| import time | |||||
| # add | |||||
| import numpy as np | |||||
| import math | |||||
| import copy | |||||
| from collections import deque | |||||
| class Setting: | |||||
| # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| @staticmethod | |||||
| def asynchronous() -> bool: | |||||
| return True | |||||
| # 选手需要依次将player0到player4的职业都定义 | |||||
| @staticmethod | |||||
| def studentType() -> List[THUAI6.StudentType]: | |||||
| return [THUAI6.StudentType.Athlete, THUAI6.StudentType.Teacher, THUAI6.StudentType.StraightAStudent, THUAI6.StudentType.Sunshine] | |||||
| @staticmethod | |||||
| def trickerType() -> THUAI6.TrickerType: | |||||
| return THUAI6.TrickerType.ANoisyPerson | |||||
| # 辅助函数 | |||||
| numOfGridPerCell: Final[int] = 1000 | |||||
| class AssistFunction: | |||||
| @staticmethod | |||||
| def CellToGrid(cell: int) -> int: | |||||
| return cell * numOfGridPerCell + numOfGridPerCell // 2 | |||||
| @staticmethod | |||||
| def GridToCell(grid: int) -> int: | |||||
| return grid // numOfGridPerCell | |||||
| # additional function_tricker | |||||
| pi = 3.1415926535 | |||||
| class cel: | |||||
| x:int | |||||
| y:int | |||||
| gridx:int | |||||
| gridy:int | |||||
| def __init__(self,x1:int, y1:int,type = 0):#type==0采用0-50赋值 | |||||
| if type ==0: | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| self.gridx = AssistFunction.CellToGrid(x1) | |||||
| self.gridy = AssistFunction.CellToGrid(y1) | |||||
| else: | |||||
| self.x = AssistFunction.GridToCell(x1) | |||||
| self.y = AssistFunction.GridToCell(y1) | |||||
| self.gridx = AssistFunction.CellToGrid(self.x) | |||||
| self.gridy = AssistFunction.CellToGrid(self.y) | |||||
| class precise_cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int, y1:int): | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| def gridtocell(x:int,y:int) -> cel:#返回坐标对应的cell | |||||
| return cel(AssistFunction.GridToCell(x),AssistFunction.GridToCell(y)) | |||||
| def distocell(x:int,y:int,dis:float,angle:float) -> List[cel]:#返回以一个点为中心长度为dis的cell | |||||
| allcell=[] | |||||
| for i in range(0,2*pi,angle): | |||||
| x1 = x+dis*math.cos(i) | |||||
| y1 = y+dis*math.sin(i) | |||||
| if x1>0 and x1<50000 and y1 >0 and y1<50000: | |||||
| the = gridtocell(x1,y1) | |||||
| if len(allcell) == 0: | |||||
| allcell.append(the) | |||||
| elif allcell[-1].x != the.x or allcell[-1].y != the.y: | |||||
| allcell.append(the) | |||||
| return allcell | |||||
| # Land = 1 | |||||
| # Wall = 2 | |||||
| # Grass = 3 | |||||
| # ClassRoom = 4 | |||||
| # Gate = 5 | |||||
| # HiddenGate = 6 | |||||
| # Window = 7 | |||||
| # Door3 = 8 | |||||
| # Door5 = 9 | |||||
| # Door6 = 10 | |||||
| # Chest = 11 | |||||
| def findpa(now:cel,dest:cel,map:List[List[THUAI6.PlaceType]]) -> cel: | |||||
| return cel(1,1) | |||||
| def dist(x1:int,y1:int,x2:int,y2:int) ->float: | |||||
| return math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) | |||||
| def toangle(nowx:int,nowy:int,tox:int,toy:int) ->float: | |||||
| if toy >= nowy: | |||||
| return math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| else: | |||||
| return -math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| def toangle_cel(now:cel,dest:cel) ->float: | |||||
| if dest.y >= now.y: | |||||
| return math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| else: | |||||
| return -math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| def findway_DFS(grid, start:cel, end:cel) -> List[cel]: | |||||
| row, col = 50,50 | |||||
| visited = set() | |||||
| path =[] | |||||
| class anan: | |||||
| angle:float | |||||
| to:int#0,1,2,3,下右上左 | |||||
| def __init__(self,angel:float,to:int) -> None: | |||||
| self.angle = angel | |||||
| self.to = to | |||||
| pass | |||||
| def azhe(jjj:anan)->float: | |||||
| return jjj.angle | |||||
| def help(now:cel,path:List[cel])->List[cel]: | |||||
| if now == end: | |||||
| return path | |||||
| if now in visited: | |||||
| oo = [] | |||||
| return oo | |||||
| visited.add(now) | |||||
| angle = toangle_cel(now,end) | |||||
| directions = [(1, 0), (0, 1), (-1, 0), (0, -1)] | |||||
| chae = [anan(abs(angle),0),anan(abs(angle-pi/2),1),anan(abs(angle-pi),2),anan(abs(angle-pi*1.5),3)] | |||||
| chae.sort(key = azhe) | |||||
| for i in chae: | |||||
| dx,dy = directions[i.to] | |||||
| new_x, new_y = now.x + dx, now.y + dy | |||||
| new =cel(new_x, new_y) | |||||
| if (grid[new_x][new_y]).value in [1,3,7,8,9,10]: | |||||
| path.append(new) | |||||
| visited.remove(now) | |||||
| return help(path[-1], path) | |||||
| return help(start,path = path) | |||||
| # additional function_tricker | |||||
| # additional function_student | |||||
| pi=3.1415926535 | |||||
| class Cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int,y1:int): | |||||
| self.x=x1 | |||||
| self.y=y1 | |||||
| #计算距离 | |||||
| def distance(target:Cel,api:IStudentAPI,ai)->float: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| return math.sqrt((target.x-selfinfo.x)**2+(target.y-selfinfo.y)**2) | |||||
| movetime=50 | |||||
| #为避免没到格子中心卡墙角,用cel规划路径,用grid精确地走 | |||||
| def Goto(target:Cel,api:IStudentAPI,ai)->None: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #当前格子坐标 | |||||
| #if self.nowpath==None: | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| nowpath=ai.BFSpath(selfinfo,cur,target,api)#规划路径 | |||||
| #print('self.nowpath') | |||||
| #for k in nowpath: | |||||
| # print(k.x,k.y) | |||||
| while len(nowpath)!=0:#能否这样判断不空 | |||||
| first=nowpath[0] | |||||
| x1=1000*first.x+500 | |||||
| y1=1000*first.y+500 | |||||
| #print('curgrid',selfx,selfy) | |||||
| #print('first ',x1,y1) | |||||
| if cur.x==first.x and cur.y==first.y: | |||||
| a=nowpath.pop(0) | |||||
| else: | |||||
| #print('not the same') | |||||
| #print('first',x1,y1) | |||||
| #print('cur',cur.x,cur.y) | |||||
| if x1==selfx and y1<selfy:api.MoveLeft(movetime) | |||||
| elif x1==selfx and y1>selfy:api.MoveRight(movetime) | |||||
| elif x1<selfx and y1==selfy:api.MoveUp(movetime) | |||||
| elif x1>selfx and y1==selfy:api.MoveDown(movetime) | |||||
| elif x1<selfx and y1<selfy:api.Move(movetime,-pi*3/4) | |||||
| elif x1<selfx and y1>selfy:api.Move(movetime,pi*3/4) | |||||
| elif x1>selfx and y1<selfy:api.Move(movetime,-pi/4) | |||||
| elif x1>selfx and y1>selfy:api.Move(movetime,pi/4) | |||||
| #api.PrintSelfInfo() | |||||
| selfinfo1=api.GetSelfInfo() | |||||
| selfx1=selfinfo1.x | |||||
| selfy1=selfinfo1.y | |||||
| #print('aftermove',selfx1,selfy1) | |||||
| #cur1=gridtocell(selfinfo1.x,selfinfo1.y) | |||||
| #print('info',cur1.x,cur1.y) | |||||
| if(selfx==selfx1 and selfy==selfy1): | |||||
| #print('not moved') | |||||
| api.MoveRight(movetime) | |||||
| # api.MoveRight(movetime) | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #print('State ',selfinfo.playerState.name) | |||||
| #api.PrintSelfInfo() | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| # additional function_student | |||||
| class AI(IAI): | |||||
| map = None | |||||
| # tricker | |||||
| allgates_tr=[] | |||||
| allwindows_tr = [] | |||||
| alldoor3_tr = [] | |||||
| alldoor5_tr = [] | |||||
| alldoor6_tr = [] | |||||
| allclassrooms_tr = [] | |||||
| allboxes_tr = [] | |||||
| allhiddengates_tr = [] | |||||
| studentposition_previous_frame=None#precise_cel | |||||
| studentposition_this_frame=None#precise_cel | |||||
| my_previous_frame=None#precise_cel | |||||
| my_this_frame=None#precise_cel | |||||
| now_path=None#List[cel] | |||||
| move_along_path=None#bool | |||||
| def closest_chest(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allboxes_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxChestProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def closest_classroom(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allclassrooms_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxClassroomProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def wall_exist_precise(self,now:precise_cel,dest:precise_cel)->bool: | |||||
| k = (dest.y-now.y)/(dest.x-now.x) | |||||
| # y = k(x-now.x)+now.y | |||||
| long = dest.x-now.x | |||||
| for i in range(0,long,801): | |||||
| y1= k*i+now.y | |||||
| ok = cel(now.x+i,y1) | |||||
| if self.map[ok.x][ok.y].value in [2,4,5,6,11]: | |||||
| return True | |||||
| return False | |||||
| def near_chest(self,now:cel,api)->bool: | |||||
| eight = [cel(now.x,now.y+1),cel(now.x,now.y-1),cel(now.x+1,now.y),cel(now.x-1,now.y),cel(now.x+1,now.y+1),cel(now.x-1,now.y-1),cel(now.x+1,now.y-1),cel(now.x-1,now.y+1)] | |||||
| for i in eight: | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if self.map[i.x][i.y].name == 'Chest' and jindu < Constants.maxChestProgress: | |||||
| return True | |||||
| return False | |||||
| # student | |||||
| allgates=[] | |||||
| allwindows = [] | |||||
| alldoors3 = [] | |||||
| alldoors5 = [] | |||||
| alldoors6 = [] | |||||
| allclassrooms = [] | |||||
| allboxes = [] | |||||
| allhiddengates = [] | |||||
| last_cel:Cel | |||||
| cur_cel:Cel | |||||
| #movetime=10#移动时间是多少? | |||||
| #nowpath:deque[Cel]#存BFS得出的路径直到路径走�?? | |||||
| #move_along_path=0#直到走完路径,避免每帧调用一�?? | |||||
| def Todestin(self,api:IStudentAPI): | |||||
| return | |||||
| def Iscanreach(self,point:Cel,target:Cel)->bool:#判断两个点能否直接到 | |||||
| if(target.x<0 or target.x>50 | |||||
| or target.y<0 or target.y>50 | |||||
| or self.map[target.x][target.y].name=='Wall' | |||||
| or (target.x==point.x and target.y==point.y)):#或超出地图、墙壁、与初始点重�?? | |||||
| return False | |||||
| else:#上下左右�?? | |||||
| if(abs(point.x-target.x)+abs(point.y-target.y)==1): | |||||
| return True | |||||
| else:#斜对角线是门的话先不�?? | |||||
| name1=self.map[target.x][target.y].name | |||||
| if(name1=='Door3' or name1=='Door5' or name1=='Door6'): | |||||
| return False | |||||
| else: | |||||
| name2=self.map[target.x][point.y].name | |||||
| name3=self.map[point.x][target.y].name | |||||
| if name2=='Wall' or name3=='Wall': | |||||
| return False | |||||
| else: | |||||
| return True | |||||
| #对角线的两侧有墙壁的话也不行�??45度的时候走不过�?? | |||||
| #隐藏门和大校门? | |||||
| def getSurroundCell(self,info:THUAI6.Student,point:Cel)->deque[Cel]:#找到该点周围8个点中能到的 | |||||
| surroundCell=[]#存能到的 | |||||
| curprop=info.prop | |||||
| global next | |||||
| for xi in range(point.x-1,point.x+2): | |||||
| for yj in range(point.y-1,point.y+2): | |||||
| other=Cel(xi,yj) | |||||
| if(self.Iscanreach(point,other)): | |||||
| sname=self.map[other.x][other.y].name | |||||
| if(sname=='Door3'):#是门的话,判断有无对应钥匙? | |||||
| if 1 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door5'): | |||||
| if 2 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door6'): | |||||
| if 3 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Window'):#是窗的话爬出�?? | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| else: | |||||
| next=other | |||||
| surroundCell.append(next) | |||||
| return surroundCell | |||||
| #判断坐标是否在list里,searched用到,也是数字相同但不同对象 | |||||
| def InList(self,point:Cel,llist:List)->bool: | |||||
| for k in llist: | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #坐标是否在字典的key里,每一次初始化类的对象,尽管数字相同但还是不同对象,可以重�??=?还可以?(后面简化一�?? | |||||
| def InDic(self,point:Cel,DDix:dict)->bool: | |||||
| for k in DDix.keys(): | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #广度搜索找路径,如果 | |||||
| #路径不存在的情况�?? | |||||
| def BFSpath(self,info:THUAI6.Student,point:Cel,target:Cel,api:IStudentAPI)->List[Cel]: | |||||
| #self_info=api.GetSelfInfo(self) | |||||
| searched=[]#已经搜过的点 | |||||
| q=deque() | |||||
| q.append(point)#初始点加�?? | |||||
| #定义搜索过的点的父节�?? | |||||
| parents=dict() | |||||
| path=[target] | |||||
| while q: | |||||
| cur_=q.popleft() | |||||
| #如果点没有被搜过 | |||||
| if not self.InList(cur_,searched): | |||||
| #api.Print('not serached'+'cur_ '+str(cur_.x)+' '+str(cur_.y)) | |||||
| if(cur_.x !=target.x or cur_.y!=target.y): | |||||
| #print('!=here') | |||||
| sur=self.getSurroundCell(info,cur_) | |||||
| for k in sur: | |||||
| if not self.InList(k,searched): | |||||
| q.append(k) | |||||
| if not self.InDic(k,parents): | |||||
| parents[k]=cur_ | |||||
| searched.append(cur_) | |||||
| else: | |||||
| #print('cur_=target') | |||||
| key=Cel(target.x,target.y) | |||||
| while key.x!=point.x or key.y!=point.y: | |||||
| for k in parents: | |||||
| if k.x==key.x and k.y==key.y: | |||||
| father=parents[k] | |||||
| path.append(father) | |||||
| key=father | |||||
| #往回找到路径后,将列表反转 | |||||
| path.reverse() | |||||
| break | |||||
| return path | |||||
| #�?7间教室学�? | |||||
| def Study(self,api:IStudentAPI)->None: | |||||
| leng=len(self.allclassrooms) | |||||
| ifdone=np.zeros((leng,1),dtype=bool) | |||||
| class_count=0#已经学的教室的间�? | |||||
| #找到距离self最近的教室并前往,途中捡道�? | |||||
| while class_count<7: | |||||
| my_filter=filter(lambda x:ifdone[self.allclassrooms.index(x)]==0,self.allclassrooms)#已经学过的教室就不要去了 | |||||
| result=list(my_filter) | |||||
| dis_to_class=[] | |||||
| for k in result: | |||||
| dis=distance(k,api,self) | |||||
| dis_to_class.append(dis) | |||||
| num=dis_to_class.index(min(dis_to_class))#result中距离最近的教室的编�? | |||||
| which_class=result[num] | |||||
| x1=which_class.x | |||||
| y1=which_class.y | |||||
| #print('nearest ',x1,y1) | |||||
| #在classroom列表中找回这个点的位置,并在ifdone中修改标�? | |||||
| index_of_class=next((i for i,xx in enumerate(self.allclassrooms) if xx.x==x1 and xx.y==y1),None) | |||||
| ifdone[index_of_class]=1 | |||||
| Goto(which_class,api,self) | |||||
| api.StartLearning()#开始学�? | |||||
| class_count=class_count+1 | |||||
| # function | |||||
| def __init__(self, pID: int): | |||||
| self.__playerID = pID | |||||
| def StudentPlay(self, api: IStudentAPI) -> None: | |||||
| return | |||||
| # 鍏叡鎿嶄綔 | |||||
| if self.map==None: | |||||
| self.map=api.GetFullMap() | |||||
| xi=0 | |||||
| yj=0 | |||||
| for i in self.map: | |||||
| yj=0 | |||||
| for j in i: | |||||
| if j.name=='ClassRoom': | |||||
| self.allclassrooms.append(Cel(xi,yj)) | |||||
| if j.name=='Gate': | |||||
| self.allgates.append(Cel(xi,yj)) | |||||
| if j.name=='HiddenGate': | |||||
| self.allhiddengates.append(Cel(xi,yj)) | |||||
| if j.name=='Window':#��һ���������ĺ������� | |||||
| self.allwindows.append(Cel(xi,yj)) | |||||
| if j.name=='Door3': | |||||
| self.alldoors3.append(Cel(xi,yj)) | |||||
| if j.name=='Door5': | |||||
| self.alldoors5.append(Cel(xi,yj)) | |||||
| if j.name=='Door6': | |||||
| self.alldoors6.append(Cel(xi,yj)) | |||||
| if j.name=='Chest': | |||||
| self.allboxes.append(Cel(xi,yj)) | |||||
| yj=yj+1 | |||||
| xi=xi+1 | |||||
| print(self.map) | |||||
| if self.__playerID == 0: | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| #api.StartLearning() | |||||
| #api.PrintSelfInfo() | |||||
| #print('end') | |||||
| #self.Study(api) | |||||
| elif self.__playerID == 1: | |||||
| # 鐜╁�??1鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 2: | |||||
| # 鐜╁�??2鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 3: | |||||
| # 鐜╁�??3鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| # 鍙互鍐欐垚if self.__playerID<2涔嬬被鐨勫啓娉� | |||||
| # 鍏叡鎿嶄綔 | |||||
| return | |||||
| def TrickerPlay(self, api: ITrickerAPI) -> None: | |||||
| #api.EndAllAction() | |||||
| if self.map is None: | |||||
| self.map = api.GetFullMap() | |||||
| yj = 0 | |||||
| xi = 0 | |||||
| for i in self.map: | |||||
| yj = 0 | |||||
| for j in i: | |||||
| if j.name == 'ClassRoom': | |||||
| self.allclassrooms_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Gate': | |||||
| self.allgates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'HiddenGate': | |||||
| self.allhiddengates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Window': | |||||
| self.allwindows_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door3': | |||||
| self.alldoor3_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door5': | |||||
| self.alldoor5_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door6': | |||||
| self.alldoor6_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Chest': | |||||
| self.allboxes_tr.append(cel(xi,yj)) | |||||
| yj = yj + 1 | |||||
| xi = xi + 1 | |||||
| if self.my_previous_frame is None: | |||||
| self.my_previous_frame = precise_cel(0,0) | |||||
| selfInfo = api.GetSelfInfo() | |||||
| self.my_this_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.trickerType.name == 'ANoisyPerson': | |||||
| student_see = api.GetStudents() | |||||
| now = cel(selfInfo.x,selfInfo.y,1) | |||||
| dest= cel(0,0) | |||||
| this_dest:cel | |||||
| if self.near_chest(now,api): | |||||
| api.StartOpenChest() | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| if len(student_see) == 0: | |||||
| self.move_along_path = True | |||||
| dest = cel(0,0) | |||||
| if selfInfo.trickDesire == 0: | |||||
| #找教室 | |||||
| dest = self.closest_classroom(now.x,now.y) | |||||
| else: | |||||
| dis = float(ANoisyPerson.alertnessRadius)/selfInfo.trickDesire | |||||
| a=1 | |||||
| else: | |||||
| self.move_along_path = False | |||||
| closestudent:THUAI6.Student | |||||
| closestudent = None | |||||
| cloest = 1000000000000 | |||||
| attacklong = Constants.basicAttackShortRange | |||||
| for i in student_see: | |||||
| dis = dist(i.x,i.y,selfInfo.x,selfInfo.y) | |||||
| if dis < cloest : | |||||
| cloest = dis | |||||
| if i.playerState.name != 'Addicted': | |||||
| closestudent = i | |||||
| if closestudent is not None: | |||||
| dest = cel(closestudent.x,closestudent.y,1) | |||||
| else: | |||||
| dest = self.closest_chest(now.x,now.y,api) | |||||
| #self.move_along_path = True | |||||
| if closestudent is not None: | |||||
| if closestudent.studentType.name == 'Athlete': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Teacher': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'StraightAStudent': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Sunshine': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| # if self.now_path is None and self.move_along_path == True: | |||||
| # self.now_path = findway_DFS(self.map,now,dest) | |||||
| # if self.move_along_path == True: | |||||
| # dest = self.now_path[1] | |||||
| this_dest = dest | |||||
| angle = toangle(selfInfo.x,selfInfo.y,this_dest.gridx,this_dest.gridy) | |||||
| if self.my_previous_frame.x == self.my_this_frame.x and self.my_previous_frame.y == self.my_this_frame.y: | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.MoveUp(10) | |||||
| return | |||||
| self.my_previous_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.Move(60,angle) | |||||
| # hide in grass | |||||
| # attack | |||||
| # trick around | |||||
| # find chest | |||||
| # find classroom | |||||
| elif selfInfo.trickerType.name == 'Idol': | |||||
| a=1 | |||||
| elif selfInfo.trickerType.name == 'Assassin': | |||||
| a=1 | |||||
| return | |||||
| # playerID=4, GUID=678, x=21500, y=25500 | |||||
| # speed=3960, view range=15600, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, 0.0, -1.0] | |||||
| # state=Idle, bullet=CommonAttackOfTricker, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=Assassin, trick desire=2.4405367634838275, class volume=0.0 | |||||
| # buff= | |||||
| # playerID=2, GUID=694, x=12500, y=26500 | |||||
| # speed=2880, view range=9000, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, -1.0, -1.0] | |||||
| # state=Idle, bullet=NullBulletType, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=StraightAStudent, determination=3300000, addiction=0, danger alert=0.0 | |||||
| # learning speed=135, encourage speed=100, encourage progress=0, rouse progress=0 | |||||
| # buff= | |||||
| @@ -0,0 +1,735 @@ | |||||
| import PyAPI.structures as THUAI6 | |||||
| from PyAPI.Interface import * | |||||
| from typing import Union, Final, cast, List | |||||
| from PyAPI.constants import * | |||||
| import queue | |||||
| import time | |||||
| # add | |||||
| import numpy as np | |||||
| import math | |||||
| import copy | |||||
| from collections import deque | |||||
| class Setting: | |||||
| # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| @staticmethod | |||||
| def asynchronous() -> bool: | |||||
| return True | |||||
| # 选手需要依次将player0到player4的职业都定义 | |||||
| @staticmethod | |||||
| def studentType() -> List[THUAI6.StudentType]: | |||||
| return [THUAI6.StudentType.Athlete, THUAI6.StudentType.Teacher, THUAI6.StudentType.StraightAStudent, THUAI6.StudentType.Sunshine] | |||||
| @staticmethod | |||||
| def trickerType() -> THUAI6.TrickerType: | |||||
| return THUAI6.TrickerType.ANoisyPerson | |||||
| # 辅助函数 | |||||
| numOfGridPerCell: Final[int] = 1000 | |||||
| class AssistFunction: | |||||
| @staticmethod | |||||
| def CellToGrid(cell: int) -> int: | |||||
| return cell * numOfGridPerCell + numOfGridPerCell // 2 | |||||
| @staticmethod | |||||
| def GridToCell(grid: int) -> int: | |||||
| return grid // numOfGridPerCell | |||||
| # additional function_tricker | |||||
| pi = 3.1415926535 | |||||
| class cel: | |||||
| x:int | |||||
| y:int | |||||
| gridx:int | |||||
| gridy:int | |||||
| def __init__(self,x1:int, y1:int,type = 0):#type==0采用0-50赋值 | |||||
| if type ==0: | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| self.gridx = AssistFunction.CellToGrid(x1) | |||||
| self.gridy = AssistFunction.CellToGrid(y1) | |||||
| else: | |||||
| self.x = AssistFunction.GridToCell(x1) | |||||
| self.y = AssistFunction.GridToCell(y1) | |||||
| self.gridx = AssistFunction.CellToGrid(self.x) | |||||
| self.gridy = AssistFunction.CellToGrid(self.y) | |||||
| class precise_cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int, y1:int): | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| def gridtocell(x:int,y:int) -> cel:#返回坐标对应的cell | |||||
| return cel(AssistFunction.GridToCell(x),AssistFunction.GridToCell(y)) | |||||
| def distocell(x:int,y:int,dis:float,angle:float) -> List[cel]:#返回以一个点为中心长度为dis的cell | |||||
| allcell=[] | |||||
| for i in range(0,2*pi,angle): | |||||
| x1 = x+dis*math.cos(i) | |||||
| y1 = y+dis*math.sin(i) | |||||
| if x1>0 and x1<50000 and y1 >0 and y1<50000: | |||||
| the = gridtocell(x1,y1) | |||||
| if len(allcell) == 0: | |||||
| allcell.append(the) | |||||
| elif allcell[-1].x != the.x or allcell[-1].y != the.y: | |||||
| allcell.append(the) | |||||
| return allcell | |||||
| # Land = 1 | |||||
| # Wall = 2 | |||||
| # Grass = 3 | |||||
| # ClassRoom = 4 | |||||
| # Gate = 5 | |||||
| # HiddenGate = 6 | |||||
| # Window = 7 | |||||
| # Door3 = 8 | |||||
| # Door5 = 9 | |||||
| # Door6 = 10 | |||||
| # Chest = 11 | |||||
| def findpa(now:cel,dest:cel,map:List[List[THUAI6.PlaceType]]) -> cel: | |||||
| return cel(1,1) | |||||
| def dist(x1:int,y1:int,x2:int,y2:int) ->float: | |||||
| return math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) | |||||
| def toangle(nowx:int,nowy:int,tox:int,toy:int) ->float: | |||||
| if toy >= nowy: | |||||
| return math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| else: | |||||
| return -math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| def toangle_cel(now:cel,dest:cel) ->float: | |||||
| if dest.y >= now.y: | |||||
| return math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| else: | |||||
| return -math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| def findway_DFS(grid, start:cel, end:cel) -> List[cel]: | |||||
| row, col = 50,50 | |||||
| visited = set() | |||||
| path =[] | |||||
| class anan: | |||||
| angle:float | |||||
| to:int#0,1,2,3,下右上左 | |||||
| def __init__(self,angel:float,to:int) -> None: | |||||
| self.angle = angel | |||||
| self.to = to | |||||
| pass | |||||
| def azhe(jjj:anan)->float: | |||||
| return jjj.angle | |||||
| def help(now:cel,path:List[cel])->List[cel]: | |||||
| if now == end: | |||||
| return path | |||||
| if now in visited: | |||||
| oo = [] | |||||
| return oo | |||||
| visited.add(now) | |||||
| angle = toangle_cel(now,end) | |||||
| directions = [(1, 0), (0, 1), (-1, 0), (0, -1)] | |||||
| chae = [anan(abs(angle),0),anan(abs(angle-pi/2),1),anan(abs(angle-pi),2),anan(abs(angle-pi*1.5),3)] | |||||
| chae.sort(key = azhe) | |||||
| for i in chae: | |||||
| dx,dy = directions[i.to] | |||||
| new_x, new_y = now.x + dx, now.y + dy | |||||
| new =cel(new_x, new_y) | |||||
| if (grid[new_x][new_y]).value in [1,3,7,8,9,10]: | |||||
| path.append(new) | |||||
| visited.remove(now) | |||||
| return help(path[-1], path) | |||||
| return help(start,path = path) | |||||
| # additional function_tricker | |||||
| # additional function_student | |||||
| pi=3.1415926535 | |||||
| class Cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int,y1:int): | |||||
| self.x=x1 | |||||
| self.y=y1 | |||||
| #计算距离 | |||||
| def distance(target:Cel,api:IStudentAPI,ai)->float: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| return math.sqrt((target.x-selfinfo.x)**2+(target.y-selfinfo.y)**2) | |||||
| movetime=50 | |||||
| #为避免没到格子中心卡墙角,用cel规划路径,用grid精确地走 | |||||
| def Goto(target:Cel,api:IStudentAPI,ai)->None: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #当前格子坐标 | |||||
| #if self.nowpath==None: | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| nowpath=ai.BFSpath(selfinfo,cur,target,api)#规划路径 | |||||
| #print('self.nowpath') | |||||
| #for k in nowpath: | |||||
| # print(k.x,k.y) | |||||
| while len(nowpath)!=0:#能否这样判断不空 | |||||
| first=nowpath[0] | |||||
| x1=1000*first.x+500 | |||||
| y1=1000*first.y+500 | |||||
| #print('curgrid',selfx,selfy) | |||||
| #print('first ',x1,y1) | |||||
| if cur.x==first.x and cur.y==first.y: | |||||
| a=nowpath.pop(0) | |||||
| else: | |||||
| #print('not the same') | |||||
| #print('first',x1,y1) | |||||
| #print('cur',cur.x,cur.y) | |||||
| if x1==selfx and y1<selfy:api.MoveLeft(movetime) | |||||
| elif x1==selfx and y1>selfy:api.MoveRight(movetime) | |||||
| elif x1<selfx and y1==selfy:api.MoveUp(movetime) | |||||
| elif x1>selfx and y1==selfy:api.MoveDown(movetime) | |||||
| elif x1<selfx and y1<selfy:api.Move(movetime,-pi*3/4) | |||||
| elif x1<selfx and y1>selfy:api.Move(movetime,pi*3/4) | |||||
| elif x1>selfx and y1<selfy:api.Move(movetime,-pi/4) | |||||
| elif x1>selfx and y1>selfy:api.Move(movetime,pi/4) | |||||
| #api.PrintSelfInfo() | |||||
| selfinfo1=api.GetSelfInfo() | |||||
| selfx1=selfinfo1.x | |||||
| selfy1=selfinfo1.y | |||||
| #print('aftermove',selfx1,selfy1) | |||||
| #cur1=gridtocell(selfinfo1.x,selfinfo1.y) | |||||
| #print('info',cur1.x,cur1.y) | |||||
| if(selfx==selfx1 and selfy==selfy1): | |||||
| #print('not moved') | |||||
| api.MoveRight(movetime) | |||||
| # api.MoveRight(movetime) | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #print('State ',selfinfo.playerState.name) | |||||
| #api.PrintSelfInfo() | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| # additional function_student | |||||
| class AI(IAI): | |||||
| map = None | |||||
| # tricker | |||||
| allgates_tr=[] | |||||
| allwindows_tr = [] | |||||
| alldoor3_tr = [] | |||||
| alldoor5_tr = [] | |||||
| alldoor6_tr = [] | |||||
| allclassrooms_tr = [] | |||||
| allboxes_tr = [] | |||||
| allhiddengates_tr = [] | |||||
| studentposition_previous_frame=None#precise_cel | |||||
| studentposition_this_frame=None#precise_cel | |||||
| my_previous_frame=None#precise_cel | |||||
| my_this_frame=None#precise_cel | |||||
| now_path=None#List[cel] | |||||
| move_along_path=None#bool | |||||
| def closest_chest(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allboxes_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxChestProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def closest_classroom(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allclassrooms_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxClassroomProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def wall_exist_precise(self,now:precise_cel,dest:precise_cel)->bool: | |||||
| k = (dest.y-now.y)/(dest.x-now.x) | |||||
| # y = k(x-now.x)+now.y | |||||
| long = dest.x-now.x | |||||
| for i in range(0,long,801): | |||||
| y1= k*i+now.y | |||||
| ok = cel(now.x+i,y1) | |||||
| if self.map[ok.x][ok.y].value in [2,4,5,6,11]: | |||||
| return True | |||||
| return False | |||||
| def near_chest(self,now:cel,api)->bool: | |||||
| eight = [cel(now.x,now.y+1),cel(now.x,now.y-1),cel(now.x+1,now.y),cel(now.x-1,now.y),cel(now.x+1,now.y+1),cel(now.x-1,now.y-1),cel(now.x+1,now.y-1),cel(now.x-1,now.y+1)] | |||||
| for i in eight: | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if self.map[i.x][i.y].name == 'Chest' and jindu < Constants.maxChestProgress: | |||||
| return True | |||||
| return False | |||||
| # student | |||||
| allgates=[] | |||||
| allwindows = [] | |||||
| alldoors3 = [] | |||||
| alldoors5 = [] | |||||
| alldoors6 = [] | |||||
| allclassrooms = [] | |||||
| allboxes = [] | |||||
| allhiddengates = [] | |||||
| last_cel:Cel | |||||
| cur_cel:Cel | |||||
| #movetime=10#移动时间是多少? | |||||
| #nowpath:deque[Cel]#存BFS得出的路径直到路径走�?? | |||||
| #move_along_path=0#直到走完路径,避免每帧调用一�?? | |||||
| def Todestin(self,api:IStudentAPI): | |||||
| return | |||||
| def Iscanreach(self,point:Cel,target:Cel)->bool:#判断两个点能否直接到 | |||||
| if(target.x<0 or target.x>50 | |||||
| or target.y<0 or target.y>50 | |||||
| or self.map[target.x][target.y].name=='Wall' | |||||
| or (target.x==point.x and target.y==point.y)):#或超出地图、墙壁、与初始点重�?? | |||||
| return False | |||||
| else:#上下左右�?? | |||||
| if(abs(point.x-target.x)+abs(point.y-target.y)==1): | |||||
| return True | |||||
| else:#斜对角线是门的话先不�?? | |||||
| name1=self.map[target.x][target.y].name | |||||
| if(name1=='Door3' or name1=='Door5' or name1=='Door6'): | |||||
| return False | |||||
| else: | |||||
| name2=self.map[target.x][point.y].name | |||||
| name3=self.map[point.x][target.y].name | |||||
| if name2=='Wall' or name3=='Wall': | |||||
| return False | |||||
| else: | |||||
| return True | |||||
| #对角线的两侧有墙壁的话也不行�??45度的时候走不过�?? | |||||
| #隐藏门和大校门? | |||||
| def getSurroundCell(self,info:THUAI6.Student,point:Cel)->deque[Cel]:#找到该点周围8个点中能到的 | |||||
| surroundCell=[]#存能到的 | |||||
| curprop=info.prop | |||||
| global next | |||||
| for xi in range(point.x-1,point.x+2): | |||||
| for yj in range(point.y-1,point.y+2): | |||||
| other=Cel(xi,yj) | |||||
| if(self.Iscanreach(point,other)): | |||||
| sname=self.map[other.x][other.y].name | |||||
| if(sname=='Door3'):#是门的话,判断有无对应钥匙? | |||||
| if 1 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door5'): | |||||
| if 2 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door6'): | |||||
| if 3 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Window'):#是窗的话爬出�?? | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| else: | |||||
| next=other | |||||
| surroundCell.append(next) | |||||
| return surroundCell | |||||
| #判断坐标是否在list里,searched用到,也是数字相同但不同对象 | |||||
| def InList(self,point:Cel,llist:List)->bool: | |||||
| for k in llist: | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #坐标是否在字典的key里,每一次初始化类的对象,尽管数字相同但还是不同对象,可以重�??=?还可以?(后面简化一�?? | |||||
| def InDic(self,point:Cel,DDix:dict)->bool: | |||||
| for k in DDix.keys(): | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #广度搜索找路径,如果 | |||||
| #路径不存在的情况�?? | |||||
| def BFSpath(self,info:THUAI6.Student,point:Cel,target:Cel,api:IStudentAPI)->List[Cel]: | |||||
| #self_info=api.GetSelfInfo(self) | |||||
| searched=[]#已经搜过的点 | |||||
| q=deque() | |||||
| q.append(point)#初始点加�?? | |||||
| #定义搜索过的点的父节�?? | |||||
| parents=dict() | |||||
| path=[target] | |||||
| while q: | |||||
| cur_=q.popleft() | |||||
| #如果点没有被搜过 | |||||
| if not self.InList(cur_,searched): | |||||
| #api.Print('not serached'+'cur_ '+str(cur_.x)+' '+str(cur_.y)) | |||||
| if(cur_.x !=target.x or cur_.y!=target.y): | |||||
| #print('!=here') | |||||
| sur=self.getSurroundCell(info,cur_) | |||||
| for k in sur: | |||||
| if not self.InList(k,searched): | |||||
| q.append(k) | |||||
| if not self.InDic(k,parents): | |||||
| parents[k]=cur_ | |||||
| searched.append(cur_) | |||||
| else: | |||||
| #print('cur_=target') | |||||
| key=Cel(target.x,target.y) | |||||
| while key.x!=point.x or key.y!=point.y: | |||||
| for k in parents: | |||||
| if k.x==key.x and k.y==key.y: | |||||
| father=parents[k] | |||||
| path.append(father) | |||||
| key=father | |||||
| #往回找到路径后,将列表反转 | |||||
| path.reverse() | |||||
| break | |||||
| return path | |||||
| #�?7间教室学�? | |||||
| def Study(self,api:IStudentAPI)->None: | |||||
| leng=len(self.allclassrooms) | |||||
| ifdone=np.zeros((leng,1),dtype=bool) | |||||
| class_count=0#已经学的教室的间�? | |||||
| #找到距离self最近的教室并前往,途中捡道�? | |||||
| while class_count<7: | |||||
| my_filter=filter(lambda x:ifdone[self.allclassrooms.index(x)]==0,self.allclassrooms)#已经学过的教室就不要去了 | |||||
| result=list(my_filter) | |||||
| dis_to_class=[] | |||||
| for k in result: | |||||
| dis=distance(k,api,self) | |||||
| dis_to_class.append(dis) | |||||
| num=dis_to_class.index(min(dis_to_class))#result中距离最近的教室的编�? | |||||
| which_class=result[num] | |||||
| x1=which_class.x | |||||
| y1=which_class.y | |||||
| #print('nearest ',x1,y1) | |||||
| #在classroom列表中找回这个点的位置,并在ifdone中修改标�? | |||||
| index_of_class=next((i for i,xx in enumerate(self.allclassrooms) if xx.x==x1 and xx.y==y1),None) | |||||
| ifdone[index_of_class]=1 | |||||
| Goto(which_class,api,self) | |||||
| api.StartLearning()#开始学�? | |||||
| class_count=class_count+1 | |||||
| # function | |||||
| def __init__(self, pID: int): | |||||
| self.__playerID = pID | |||||
| def StudentPlay(self, api: IStudentAPI) -> None: | |||||
| return | |||||
| # 鍏叡鎿嶄綔 | |||||
| if self.map==None: | |||||
| self.map=api.GetFullMap() | |||||
| xi=0 | |||||
| yj=0 | |||||
| for i in self.map: | |||||
| yj=0 | |||||
| for j in i: | |||||
| if j.name=='ClassRoom': | |||||
| self.allclassrooms.append(Cel(xi,yj)) | |||||
| if j.name=='Gate': | |||||
| self.allgates.append(Cel(xi,yj)) | |||||
| if j.name=='HiddenGate': | |||||
| self.allhiddengates.append(Cel(xi,yj)) | |||||
| if j.name=='Window':#��һ���������ĺ������� | |||||
| self.allwindows.append(Cel(xi,yj)) | |||||
| if j.name=='Door3': | |||||
| self.alldoors3.append(Cel(xi,yj)) | |||||
| if j.name=='Door5': | |||||
| self.alldoors5.append(Cel(xi,yj)) | |||||
| if j.name=='Door6': | |||||
| self.alldoors6.append(Cel(xi,yj)) | |||||
| if j.name=='Chest': | |||||
| self.allboxes.append(Cel(xi,yj)) | |||||
| yj=yj+1 | |||||
| xi=xi+1 | |||||
| print(self.map) | |||||
| if self.__playerID == 0: | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| #api.StartLearning() | |||||
| #api.PrintSelfInfo() | |||||
| #print('end') | |||||
| #self.Study(api) | |||||
| elif self.__playerID == 1: | |||||
| # 鐜╁�??1鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 2: | |||||
| # 鐜╁�??2鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 3: | |||||
| # 鐜╁�??3鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| # 鍙互鍐欐垚if self.__playerID<2涔嬬被鐨勫啓娉� | |||||
| # 鍏叡鎿嶄綔 | |||||
| return | |||||
| def TrickerPlay(self, api: ITrickerAPI) -> None: | |||||
| #api.EndAllAction() | |||||
| if self.map is None: | |||||
| self.map = api.GetFullMap() | |||||
| yj = 0 | |||||
| xi = 0 | |||||
| for i in self.map: | |||||
| yj = 0 | |||||
| for j in i: | |||||
| if j.name == 'ClassRoom': | |||||
| self.allclassrooms_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Gate': | |||||
| self.allgates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'HiddenGate': | |||||
| self.allhiddengates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Window': | |||||
| self.allwindows_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door3': | |||||
| self.alldoor3_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door5': | |||||
| self.alldoor5_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door6': | |||||
| self.alldoor6_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Chest': | |||||
| self.allboxes_tr.append(cel(xi,yj)) | |||||
| yj = yj + 1 | |||||
| xi = xi + 1 | |||||
| if self.my_previous_frame is None: | |||||
| self.my_previous_frame = precise_cel(0,0) | |||||
| selfInfo = api.GetSelfInfo() | |||||
| self.my_this_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.trickerType.name == 'ANoisyPerson': | |||||
| student_see = api.GetStudents() | |||||
| now = cel(selfInfo.x,selfInfo.y,1) | |||||
| dest= cel(0,0) | |||||
| this_dest:cel | |||||
| if self.near_chest(now,api): | |||||
| api.StartOpenChest() | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| if len(student_see) == 0: | |||||
| self.move_along_path = True | |||||
| dest = cel(0,0) | |||||
| if selfInfo.trickDesire == 0: | |||||
| #找教室 | |||||
| dest = self.closest_classroom(now.x,now.y) | |||||
| else: | |||||
| dis = float(ANoisyPerson.alertnessRadius)/selfInfo.trickDesire | |||||
| a=1 | |||||
| else: | |||||
| self.move_along_path = False | |||||
| closestudent:THUAI6.Student | |||||
| closestudent = None | |||||
| cloest = 1000000000000 | |||||
| attacklong = Constants.basicAttackShortRange | |||||
| for i in student_see: | |||||
| dis = dist(i.x,i.y,selfInfo.x,selfInfo.y) | |||||
| if dis < cloest : | |||||
| cloest = dis | |||||
| if i.playerState.name != 'Addicted': | |||||
| closestudent = i | |||||
| if closestudent is not None: | |||||
| dest = cel(closestudent.x,closestudent.y,1) | |||||
| else: | |||||
| dest = self.closest_chest(now.x,now.y,api) | |||||
| #self.move_along_path = True | |||||
| if closestudent is not None: | |||||
| if closestudent.studentType.name == 'Athlete': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Teacher': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'StraightAStudent': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Sunshine': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| # if self.now_path is None and self.move_along_path == True: | |||||
| # self.now_path = findway_DFS(self.map,now,dest) | |||||
| # if self.move_along_path == True: | |||||
| # dest = self.now_path[1] | |||||
| this_dest = dest | |||||
| angle = toangle(selfInfo.x,selfInfo.y,this_dest.gridx,this_dest.gridy) | |||||
| if self.my_previous_frame.x == self.my_this_frame.x and self.my_previous_frame.y == self.my_this_frame.y: | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.MoveUp(10) | |||||
| return | |||||
| self.my_previous_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.Move(60,angle) | |||||
| # hide in grass | |||||
| # attack | |||||
| # trick around | |||||
| # find chest | |||||
| # find classroom | |||||
| elif selfInfo.trickerType.name == 'Idol': | |||||
| a=1 | |||||
| elif selfInfo.trickerType.name == 'Assassin': | |||||
| a=1 | |||||
| return | |||||
| # playerID=4, GUID=678, x=21500, y=25500 | |||||
| # speed=3960, view range=15600, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, 0.0, -1.0] | |||||
| # state=Idle, bullet=CommonAttackOfTricker, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=Assassin, trick desire=2.4405367634838275, class volume=0.0 | |||||
| # buff= | |||||
| # playerID=2, GUID=694, x=12500, y=26500 | |||||
| # speed=2880, view range=9000, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, -1.0, -1.0] | |||||
| # state=Idle, bullet=NullBulletType, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=StraightAStudent, determination=3300000, addiction=0, danger alert=0.0 | |||||
| # learning speed=135, encourage speed=100, encourage progress=0, rouse progress=0 | |||||
| # buff= | |||||
| @@ -0,0 +1,734 @@ | |||||
| import PyAPI.structures as THUAI6 | |||||
| from PyAPI.Interface import * | |||||
| from typing import Union, Final, cast, List | |||||
| from PyAPI.constants import * | |||||
| import queue | |||||
| import time | |||||
| # add | |||||
| import numpy as np | |||||
| import math | |||||
| import copy | |||||
| from collections import deque | |||||
| class Setting: | |||||
| # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| @staticmethod | |||||
| def asynchronous() -> bool: | |||||
| return True | |||||
| # 选手需要依次将player0到player4的职业都定义 | |||||
| @staticmethod | |||||
| def studentType() -> List[THUAI6.StudentType]: | |||||
| return [THUAI6.StudentType.Athlete, THUAI6.StudentType.Teacher, THUAI6.StudentType.StraightAStudent, THUAI6.StudentType.Sunshine] | |||||
| @staticmethod | |||||
| def trickerType() -> THUAI6.TrickerType: | |||||
| return THUAI6.TrickerType.ANoisyPerson | |||||
| # 辅助函数 | |||||
| numOfGridPerCell: Final[int] = 1000 | |||||
| class AssistFunction: | |||||
| @staticmethod | |||||
| def CellToGrid(cell: int) -> int: | |||||
| return cell * numOfGridPerCell + numOfGridPerCell // 2 | |||||
| @staticmethod | |||||
| def GridToCell(grid: int) -> int: | |||||
| return grid // numOfGridPerCell | |||||
| # additional function_tricker | |||||
| pi = 3.1415926535 | |||||
| class cel: | |||||
| x:int | |||||
| y:int | |||||
| gridx:int | |||||
| gridy:int | |||||
| def __init__(self,x1:int, y1:int,type = 0):#type==0采用0-50赋值 | |||||
| if type ==0: | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| self.gridx = AssistFunction.CellToGrid(x1) | |||||
| self.gridy = AssistFunction.CellToGrid(y1) | |||||
| else: | |||||
| self.x = AssistFunction.GridToCell(x1) | |||||
| self.y = AssistFunction.GridToCell(y1) | |||||
| self.gridx = AssistFunction.CellToGrid(self.x) | |||||
| self.gridy = AssistFunction.CellToGrid(self.y) | |||||
| class precise_cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int, y1:int): | |||||
| self.x = x1 | |||||
| self.y = y1 | |||||
| def gridtocell(x:int,y:int) -> cel:#返回坐标对应的cell | |||||
| return cel(AssistFunction.GridToCell(x),AssistFunction.GridToCell(y)) | |||||
| def distocell(x:int,y:int,dis:float,angle:float) -> List[cel]:#返回以一个点为中心长度为dis的cell | |||||
| allcell=[] | |||||
| for i in range(0,2*pi,angle): | |||||
| x1 = x+dis*math.cos(i) | |||||
| y1 = y+dis*math.sin(i) | |||||
| if x1>0 and x1<50000 and y1 >0 and y1<50000: | |||||
| the = gridtocell(x1,y1) | |||||
| if len(allcell) == 0: | |||||
| allcell.append(the) | |||||
| elif allcell[-1].x != the.x or allcell[-1].y != the.y: | |||||
| allcell.append(the) | |||||
| return allcell | |||||
| # Land = 1 | |||||
| # Wall = 2 | |||||
| # Grass = 3 | |||||
| # ClassRoom = 4 | |||||
| # Gate = 5 | |||||
| # HiddenGate = 6 | |||||
| # Window = 7 | |||||
| # Door3 = 8 | |||||
| # Door5 = 9 | |||||
| # Door6 = 10 | |||||
| # Chest = 11 | |||||
| def findpa(now:cel,dest:cel,map:List[List[THUAI6.PlaceType]]) -> cel: | |||||
| return cel(1,1) | |||||
| def dist(x1:int,y1:int,x2:int,y2:int) ->float: | |||||
| return math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) | |||||
| def toangle(nowx:int,nowy:int,tox:int,toy:int) ->float: | |||||
| if toy >= nowy: | |||||
| return math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| else: | |||||
| return -math.acos((tox-nowx)/dist(nowx,nowy,tox,toy)) | |||||
| def toangle_cel(now:cel,dest:cel) ->float: | |||||
| if dest.y >= now.y: | |||||
| return math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| else: | |||||
| return -math.acos((dest.x-now.x)/dist(now.x,now.y,dest.x,dest.y)) | |||||
| def findway_DFS(grid, start:cel, end:cel) -> List[cel]: | |||||
| row, col = 50,50 | |||||
| visited = set() | |||||
| path =[] | |||||
| class anan: | |||||
| angle:float | |||||
| to:int#0,1,2,3,下右上左 | |||||
| def __init__(self,angel:float,to:int) -> None: | |||||
| self.angle = angel | |||||
| self.to = to | |||||
| pass | |||||
| def azhe(jjj:anan)->float: | |||||
| return jjj.angle | |||||
| def help(now:cel,path:List[cel])->List[cel]: | |||||
| if now == end: | |||||
| return path | |||||
| if now in visited: | |||||
| oo = [] | |||||
| return oo | |||||
| visited.add(now) | |||||
| angle = toangle_cel(now,end) | |||||
| directions = [(1, 0), (0, 1), (-1, 0), (0, -1)] | |||||
| chae = [anan(abs(angle),0),anan(abs(angle-pi/2),1),anan(abs(angle-pi),2),anan(abs(angle-pi*1.5),3)] | |||||
| chae.sort(key = azhe) | |||||
| for i in chae: | |||||
| dx,dy = directions[i.to] | |||||
| new_x, new_y = now.x + dx, now.y + dy | |||||
| new =cel(new_x, new_y) | |||||
| if (grid[new_x][new_y]).value in [1,3,7,8,9,10]: | |||||
| path.append(new) | |||||
| visited.remove(now) | |||||
| return help(path[-1], path) | |||||
| return help(start,path = path) | |||||
| # additional function_tricker | |||||
| # additional function_student | |||||
| pi=3.1415926535 | |||||
| class Cel: | |||||
| x:int | |||||
| y:int | |||||
| def __init__(self,x1:int,y1:int): | |||||
| self.x=x1 | |||||
| self.y=y1 | |||||
| #计算距离 | |||||
| def distance(target:Cel,api:IStudentAPI,ai)->float: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| return math.sqrt((target.x-selfinfo.x)**2+(target.y-selfinfo.y)**2) | |||||
| movetime=50 | |||||
| #为避免没到格子中心卡墙角,用cel规划路径,用grid精确地走 | |||||
| def Goto(target:Cel,api:IStudentAPI,ai)->None: | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #当前格子坐标 | |||||
| #if self.nowpath==None: | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| nowpath=ai.BFSpath(selfinfo,cur,target,api)#规划路径 | |||||
| #print('self.nowpath') | |||||
| #for k in nowpath: | |||||
| # print(k.x,k.y) | |||||
| while len(nowpath)!=0:#能否这样判断不空 | |||||
| first=nowpath[0] | |||||
| x1=1000*first.x+500 | |||||
| y1=1000*first.y+500 | |||||
| #print('curgrid',selfx,selfy) | |||||
| #print('first ',x1,y1) | |||||
| if cur.x==first.x and cur.y==first.y: | |||||
| a=nowpath.pop(0) | |||||
| else: | |||||
| #print('not the same') | |||||
| #print('first',x1,y1) | |||||
| #print('cur',cur.x,cur.y) | |||||
| if x1==selfx and y1<selfy:api.MoveLeft(movetime) | |||||
| elif x1==selfx and y1>selfy:api.MoveRight(movetime) | |||||
| elif x1<selfx and y1==selfy:api.MoveUp(movetime) | |||||
| elif x1>selfx and y1==selfy:api.MoveDown(movetime) | |||||
| elif x1<selfx and y1<selfy:api.Move(movetime,-pi*3/4) | |||||
| elif x1<selfx and y1>selfy:api.Move(movetime,pi*3/4) | |||||
| elif x1>selfx and y1<selfy:api.Move(movetime,-pi/4) | |||||
| elif x1>selfx and y1>selfy:api.Move(movetime,pi/4) | |||||
| #api.PrintSelfInfo() | |||||
| selfinfo1=api.GetSelfInfo() | |||||
| selfx1=selfinfo1.x | |||||
| selfy1=selfinfo1.y | |||||
| #print('aftermove',selfx1,selfy1) | |||||
| #cur1=gridtocell(selfinfo1.x,selfinfo1.y) | |||||
| #print('info',cur1.x,cur1.y) | |||||
| if(selfx==selfx1 and selfy==selfy1): | |||||
| #print('not moved') | |||||
| api.MoveRight(movetime) | |||||
| # api.MoveRight(movetime) | |||||
| selfinfo=api.GetSelfInfo() | |||||
| #print('State ',selfinfo.playerState.name) | |||||
| #api.PrintSelfInfo() | |||||
| selfx=selfinfo.x | |||||
| selfy=selfinfo.y | |||||
| cur=gridtocell(selfx,selfy) | |||||
| # additional function_student | |||||
| class AI(IAI): | |||||
| map = None | |||||
| # tricker | |||||
| allgates_tr=[] | |||||
| allwindows_tr = [] | |||||
| alldoor3_tr = [] | |||||
| alldoor5_tr = [] | |||||
| alldoor6_tr = [] | |||||
| allclassrooms_tr = [] | |||||
| allboxes_tr = [] | |||||
| allhiddengates_tr = [] | |||||
| studentposition_previous_frame=None#precise_cel | |||||
| studentposition_this_frame=None#precise_cel | |||||
| my_previous_frame=None#precise_cel | |||||
| my_this_frame=None#precise_cel | |||||
| now_path=None#List[cel] | |||||
| move_along_path=None#bool | |||||
| def closest_chest(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allboxes_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxChestProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def closest_classroom(self,x:int,y:int,api:ITrickerAPI) ->cel: | |||||
| now = cel(x,y) | |||||
| dis = 1000000000000 | |||||
| the:cel | |||||
| for i in self.allclassrooms_tr: | |||||
| k = dist(now.x,now.y,i.x,i.y) | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if k<dis and jindu < Constants.maxClassroomProgress: | |||||
| dis = k | |||||
| the = i | |||||
| return the | |||||
| def wall_exist_precise(self,now:precise_cel,dest:precise_cel)->bool: | |||||
| k = (dest.y-now.y)/(dest.x-now.x) | |||||
| # y = k(x-now.x)+now.y | |||||
| long = dest.x-now.x | |||||
| for i in range(0,long,801): | |||||
| y1= k*i+now.y | |||||
| ok = cel(now.x+i,y1) | |||||
| if self.map[ok.x][ok.y].value in [2,4,5,6,11]: | |||||
| return True | |||||
| return False | |||||
| def near_chest(self,now:cel,api)->bool: | |||||
| eight = [cel(now.x,now.y+1),cel(now.x,now.y-1),cel(now.x+1,now.y),cel(now.x-1,now.y),cel(now.x+1,now.y+1),cel(now.x-1,now.y-1),cel(now.x+1,now.y-1),cel(now.x-1,now.y+1)] | |||||
| for i in eight: | |||||
| jindu = api.GetChestProgress(i.x,i.y) | |||||
| if self.map[i.x][i.y].name == 'Chest' and jindu < Constants.maxChestProgress: | |||||
| return True | |||||
| return False | |||||
| # student | |||||
| allgates=[] | |||||
| allwindows = [] | |||||
| alldoors3 = [] | |||||
| alldoors5 = [] | |||||
| alldoors6 = [] | |||||
| allclassrooms = [] | |||||
| allboxes = [] | |||||
| allhiddengates = [] | |||||
| last_cel:Cel | |||||
| cur_cel:Cel | |||||
| #movetime=10#移动时间是多少? | |||||
| #nowpath:deque[Cel]#存BFS得出的路径直到路径走�?? | |||||
| #move_along_path=0#直到走完路径,避免每帧调用一�?? | |||||
| def Todestin(self,api:IStudentAPI): | |||||
| return | |||||
| def Iscanreach(self,point:Cel,target:Cel)->bool:#判断两个点能否直接到 | |||||
| if(target.x<0 or target.x>50 | |||||
| or target.y<0 or target.y>50 | |||||
| or self.map[target.x][target.y].name=='Wall' | |||||
| or (target.x==point.x and target.y==point.y)):#或超出地图、墙壁、与初始点重�?? | |||||
| return False | |||||
| else:#上下左右�?? | |||||
| if(abs(point.x-target.x)+abs(point.y-target.y)==1): | |||||
| return True | |||||
| else:#斜对角线是门的话先不�?? | |||||
| name1=self.map[target.x][target.y].name | |||||
| if(name1=='Door3' or name1=='Door5' or name1=='Door6'): | |||||
| return False | |||||
| else: | |||||
| name2=self.map[target.x][point.y].name | |||||
| name3=self.map[point.x][target.y].name | |||||
| if name2=='Wall' or name3=='Wall': | |||||
| return False | |||||
| else: | |||||
| return True | |||||
| #对角线的两侧有墙壁的话也不行�??45度的时候走不过�?? | |||||
| #隐藏门和大校门? | |||||
| def getSurroundCell(self,info:THUAI6.Student,point:Cel)->deque[Cel]:#找到该点周围8个点中能到的 | |||||
| surroundCell=[]#存能到的 | |||||
| curprop=info.prop | |||||
| global next | |||||
| for xi in range(point.x-1,point.x+2): | |||||
| for yj in range(point.y-1,point.y+2): | |||||
| other=Cel(xi,yj) | |||||
| if(self.Iscanreach(point,other)): | |||||
| sname=self.map[other.x][other.y].name | |||||
| if(sname=='Door3'):#是门的话,判断有无对应钥匙? | |||||
| if 1 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door5'): | |||||
| if 2 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Door6'): | |||||
| if 3 in curprop: | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| elif(sname=='Window'):#是窗的话爬出�?? | |||||
| next=Cel(2*other.x-point.x,2*other.y-point.y) | |||||
| else: | |||||
| next=other | |||||
| surroundCell.append(next) | |||||
| return surroundCell | |||||
| #判断坐标是否在list里,searched用到,也是数字相同但不同对象 | |||||
| def InList(self,point:Cel,llist:List)->bool: | |||||
| for k in llist: | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #坐标是否在字典的key里,每一次初始化类的对象,尽管数字相同但还是不同对象,可以重�??=?还可以?(后面简化一�?? | |||||
| def InDic(self,point:Cel,DDix:dict)->bool: | |||||
| for k in DDix.keys(): | |||||
| if point.x==k.x and point.y==k.y: | |||||
| return True | |||||
| return False | |||||
| #广度搜索找路径,如果 | |||||
| #路径不存在的情况�?? | |||||
| def BFSpath(self,info:THUAI6.Student,point:Cel,target:Cel,api:IStudentAPI)->List[Cel]: | |||||
| #self_info=api.GetSelfInfo(self) | |||||
| searched=[]#已经搜过的点 | |||||
| q=deque() | |||||
| q.append(point)#初始点加�?? | |||||
| #定义搜索过的点的父节�?? | |||||
| parents=dict() | |||||
| path=[target] | |||||
| while q: | |||||
| cur_=q.popleft() | |||||
| #如果点没有被搜过 | |||||
| if not self.InList(cur_,searched): | |||||
| #api.Print('not serached'+'cur_ '+str(cur_.x)+' '+str(cur_.y)) | |||||
| if(cur_.x !=target.x or cur_.y!=target.y): | |||||
| #print('!=here') | |||||
| sur=self.getSurroundCell(info,cur_) | |||||
| for k in sur: | |||||
| if not self.InList(k,searched): | |||||
| q.append(k) | |||||
| if not self.InDic(k,parents): | |||||
| parents[k]=cur_ | |||||
| searched.append(cur_) | |||||
| else: | |||||
| #print('cur_=target') | |||||
| key=Cel(target.x,target.y) | |||||
| while key.x!=point.x or key.y!=point.y: | |||||
| for k in parents: | |||||
| if k.x==key.x and k.y==key.y: | |||||
| father=parents[k] | |||||
| path.append(father) | |||||
| key=father | |||||
| #往回找到路径后,将列表反转 | |||||
| path.reverse() | |||||
| break | |||||
| return path | |||||
| #�?7间教室学�? | |||||
| def Study(self,api:IStudentAPI)->None: | |||||
| leng=len(self.allclassrooms) | |||||
| ifdone=np.zeros((leng,1),dtype=bool) | |||||
| class_count=0#已经学的教室的间�? | |||||
| #找到距离self最近的教室并前往,途中捡道�? | |||||
| while class_count<7: | |||||
| my_filter=filter(lambda x:ifdone[self.allclassrooms.index(x)]==0,self.allclassrooms)#已经学过的教室就不要去了 | |||||
| result=list(my_filter) | |||||
| dis_to_class=[] | |||||
| for k in result: | |||||
| dis=distance(k,api,self) | |||||
| dis_to_class.append(dis) | |||||
| num=dis_to_class.index(min(dis_to_class))#result中距离最近的教室的编�? | |||||
| which_class=result[num] | |||||
| x1=which_class.x | |||||
| y1=which_class.y | |||||
| #print('nearest ',x1,y1) | |||||
| #在classroom列表中找回这个点的位置,并在ifdone中修改标�? | |||||
| index_of_class=next((i for i,xx in enumerate(self.allclassrooms) if xx.x==x1 and xx.y==y1),None) | |||||
| ifdone[index_of_class]=1 | |||||
| Goto(which_class,api,self) | |||||
| api.StartLearning()#开始学�? | |||||
| class_count=class_count+1 | |||||
| # function | |||||
| def __init__(self, pID: int): | |||||
| self.__playerID = pID | |||||
| def StudentPlay(self, api: IStudentAPI) -> None: | |||||
| # 鍏叡鎿嶄綔 | |||||
| if self.map==None: | |||||
| self.map=api.GetFullMap() | |||||
| xi=0 | |||||
| yj=0 | |||||
| for i in self.map: | |||||
| yj=0 | |||||
| for j in i: | |||||
| if j.name=='ClassRoom': | |||||
| self.allclassrooms.append(Cel(xi,yj)) | |||||
| if j.name=='Gate': | |||||
| self.allgates.append(Cel(xi,yj)) | |||||
| if j.name=='HiddenGate': | |||||
| self.allhiddengates.append(Cel(xi,yj)) | |||||
| if j.name=='Window':#��һ���������ĺ������� | |||||
| self.allwindows.append(Cel(xi,yj)) | |||||
| if j.name=='Door3': | |||||
| self.alldoors3.append(Cel(xi,yj)) | |||||
| if j.name=='Door5': | |||||
| self.alldoors5.append(Cel(xi,yj)) | |||||
| if j.name=='Door6': | |||||
| self.alldoors6.append(Cel(xi,yj)) | |||||
| if j.name=='Chest': | |||||
| self.allboxes.append(Cel(xi,yj)) | |||||
| yj=yj+1 | |||||
| xi=xi+1 | |||||
| print(self.map) | |||||
| if self.__playerID == 0: | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| #api.StartLearning() | |||||
| #api.PrintSelfInfo() | |||||
| #print('end') | |||||
| #self.Study(api) | |||||
| elif self.__playerID == 1: | |||||
| # 鐜╁�??1鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 2: | |||||
| # 鐜╁�??2鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| elif self.__playerID == 3: | |||||
| # 鐜╁�??3鎵ц鎿嶄�?? | |||||
| selfinfo=api.GetSelfInfo()#获取自身信息 | |||||
| Props=api.GetProps()#获取场上所有可见道�? | |||||
| students=api.GetStudents()#获取其他所有学生的信息 | |||||
| api.PrintSelfInfo() | |||||
| box1=self.allboxes[0] | |||||
| Goto(box1,api,self) | |||||
| return | |||||
| # 鍙互鍐欐垚if self.__playerID<2涔嬬被鐨勫啓娉� | |||||
| # 鍏叡鎿嶄綔 | |||||
| return | |||||
| def TrickerPlay(self, api: ITrickerAPI) -> None: | |||||
| #api.EndAllAction() | |||||
| if self.map is None: | |||||
| self.map = api.GetFullMap() | |||||
| yj = 0 | |||||
| xi = 0 | |||||
| for i in self.map: | |||||
| yj = 0 | |||||
| for j in i: | |||||
| if j.name == 'ClassRoom': | |||||
| self.allclassrooms_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Gate': | |||||
| self.allgates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'HiddenGate': | |||||
| self.allhiddengates_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Window': | |||||
| self.allwindows_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door3': | |||||
| self.alldoor3_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door5': | |||||
| self.alldoor5_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Door6': | |||||
| self.alldoor6_tr.append(cel(xi,yj)) | |||||
| elif j.name == 'Chest': | |||||
| self.allboxes_tr.append(cel(xi,yj)) | |||||
| yj = yj + 1 | |||||
| xi = xi + 1 | |||||
| if self.my_previous_frame is None: | |||||
| self.my_previous_frame = precise_cel(0,0) | |||||
| selfInfo = api.GetSelfInfo() | |||||
| self.my_this_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.trickerType.name == 'ANoisyPerson': | |||||
| student_see = api.GetStudents() | |||||
| now = cel(selfInfo.x,selfInfo.y,1) | |||||
| dest= cel(0,0) | |||||
| this_dest:cel | |||||
| if self.near_chest(now,api): | |||||
| api.StartOpenChest() | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| if len(student_see) == 0: | |||||
| self.move_along_path = True | |||||
| dest = cel(0,0) | |||||
| if selfInfo.trickDesire == 0: | |||||
| #找教室 | |||||
| dest = self.closest_classroom(now.x,now.y) | |||||
| else: | |||||
| dis = float(ANoisyPerson.alertnessRadius)/selfInfo.trickDesire | |||||
| a=1 | |||||
| else: | |||||
| self.move_along_path = False | |||||
| closestudent:THUAI6.Student | |||||
| closestudent = None | |||||
| cloest = 1000000000000 | |||||
| attacklong = Constants.basicAttackShortRange | |||||
| for i in student_see: | |||||
| dis = dist(i.x,i.y,selfInfo.x,selfInfo.y) | |||||
| if dis < cloest : | |||||
| cloest = dis | |||||
| if i.playerState.name != 'Addicted': | |||||
| closestudent = i | |||||
| if closestudent is not None: | |||||
| dest = cel(closestudent.x,closestudent.y,1) | |||||
| else: | |||||
| dest = self.closest_chest(now.x,now.y,api) | |||||
| #self.move_along_path = True | |||||
| if closestudent is not None: | |||||
| if closestudent.studentType.name == 'Athlete': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Teacher': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'StraightAStudent': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| elif closestudent.studentType.name == 'Sunshine': | |||||
| if cloest > StraightAStudent.viewRange: | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| #api.Attack() | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| else: | |||||
| if cloest <= attacklong: | |||||
| #if selfInfo.timeUntilSkillAvailable[1] == 0: | |||||
| #api.UseSkill(1) | |||||
| if closestudent.playerState.name != 'Addicted': | |||||
| attckangle = toangle(now.gridx,now.gridy,dest.gridx,dest.gridy) | |||||
| api.Attack(attckangle) | |||||
| if selfInfo.timeUntilSkillAvailable[0] == 0: | |||||
| api.UseSkill(0) | |||||
| else: | |||||
| a=1#if selfInfo.playerState = | |||||
| # if self.now_path is None and self.move_along_path == True: | |||||
| # self.now_path = findway_DFS(self.map,now,dest) | |||||
| # if self.move_along_path == True: | |||||
| # dest = self.now_path[1] | |||||
| this_dest = dest | |||||
| angle = toangle(selfInfo.x,selfInfo.y,this_dest.gridx,this_dest.gridy) | |||||
| if self.my_previous_frame.x == self.my_this_frame.x and self.my_previous_frame.y == self.my_this_frame.y: | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.MoveUp(10) | |||||
| return | |||||
| self.my_previous_frame = precise_cel(selfInfo.x,selfInfo.y) | |||||
| if selfInfo.playerState.name == 'Idle': | |||||
| api.Move(60,angle) | |||||
| # hide in grass | |||||
| # attack | |||||
| # trick around | |||||
| # find chest | |||||
| # find classroom | |||||
| elif selfInfo.trickerType.name == 'Idol': | |||||
| a=1 | |||||
| elif selfInfo.trickerType.name == 'Assassin': | |||||
| a=1 | |||||
| return | |||||
| # playerID=4, GUID=678, x=21500, y=25500 | |||||
| # speed=3960, view range=15600, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, 0.0, -1.0] | |||||
| # state=Idle, bullet=CommonAttackOfTricker, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=Assassin, trick desire=2.4405367634838275, class volume=0.0 | |||||
| # buff= | |||||
| # playerID=2, GUID=694, x=12500, y=26500 | |||||
| # speed=2880, view range=9000, place=Land, radius=400 | |||||
| # score=0, facing direction=0.0, skill time=[0.0, -1.0, -1.0] | |||||
| # state=Idle, bullet=NullBulletType, prop=NullPropType, NullPropType, NullPropType, | |||||
| # type=StraightAStudent, determination=3300000, addiction=0, danger alert=0.0 | |||||
| # learning speed=135, encourage speed=100, encourage progress=0, rouse progress=0 | |||||
| # buff= | |||||
| @@ -0,0 +1,977 @@ | |||||
| #include <vector> | |||||
| #include <list> | |||||
| #include <math.h> | |||||
| #include <string> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| constexpr int deltaTimems = 50; | |||||
| constexpr int LastTrickerNum = 10; | |||||
| #define deltaTime 0.05 | |||||
| #define pi 3.1415926 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Klee; | |||||
| /* | |||||
| * 全局常量: | |||||
| * | |||||
| * 全局变量: | |||||
| * | |||||
| * Debug相关 | |||||
| * 寻路相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * 学生变量: | |||||
| * 通信相关 | |||||
| * 状态相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * StraightAStudent变量: | |||||
| * 冥想相关 | |||||
| * Athlete变量: | |||||
| * | |||||
| * Teacher变量: | |||||
| * | |||||
| * Sunshine变量: | |||||
| * | |||||
| * | |||||
| * 捣蛋鬼变量: | |||||
| * | |||||
| * | |||||
| * Assas变量: | |||||
| * | |||||
| * 工具类 | |||||
| * A*类 | |||||
| * | |||||
| */ | |||||
| struct Node { | |||||
| int x, y; | |||||
| int F, G, H; | |||||
| Node* parent; | |||||
| Node(int _x, int _y):x(_x), y(_y), F(0), G(0), H(0), parent(NULL) {} | |||||
| }*node[50][50]; | |||||
| const std::list<Node*> emptylist; | |||||
| const int EscapeDist = 16000; | |||||
| std::list<Node*> PathToGo, PathGone; | |||||
| bool isFirstFrame = true; | |||||
| int PlayerId; | |||||
| int Gridx, Gridy; | |||||
| int Cellx, Celly; | |||||
| std::vector<std::vector<THUAI6::PlaceType>> Map; | |||||
| int map[50][50];//0为无阻碍,1为半阻碍,2为完全阻碍, 3为临时避障 | |||||
| struct ClassRoomInfo { | |||||
| int x, y; | |||||
| bool finished; | |||||
| int occupied;//为4说明无学生占用,为0-3说明相应编号学生正在占用 | |||||
| }Classroom[100]; | |||||
| int ClassroomCount, ClassroomFinished; | |||||
| struct ChestInfo { | |||||
| int x, y; | |||||
| bool opened; | |||||
| int occupied; | |||||
| }Chest[80]; | |||||
| int ChestCount; | |||||
| struct GateInfo { | |||||
| int x, y; | |||||
| bool opening; | |||||
| bool open; | |||||
| }Gate[20]; | |||||
| int GateCount; | |||||
| struct HiddenGateInfo { | |||||
| int x, y; | |||||
| bool refreshed; | |||||
| bool open; | |||||
| }HiddenGate[30]; | |||||
| int HiddenGateCount; | |||||
| struct GrassInfo { | |||||
| int x, y, occupied; | |||||
| }Grass[200]; | |||||
| int GrassCount; | |||||
| double faceDir = 0; | |||||
| bool map1 = false, map2 = false; | |||||
| std::pair<int, int> LastTarget; | |||||
| std::pair<int, std::string> msgrcv; | |||||
| std::string msg;//信息结构:'准备前往*号教室''1/0完成了作业''*号教室作业已完成''需要被营救'"捣蛋鬼x坐标""捣蛋鬼y坐标" | |||||
| std::shared_ptr<const THUAI6::Student> studentinfo; | |||||
| THUAI6::Tricker Trickers,LastTrickers[LastTrickerNum]; | |||||
| bool TrickerisInSight; | |||||
| bool CanGetTrickerInfo; | |||||
| enum class StudentState { | |||||
| MovingForClassroom, Learning, MovingForGrass, MovingToAttck, MovingForProp, MovingToRescue, MovingToGraduate, WaitToGraduate | |||||
| }studentstate; | |||||
| int CurrentClassroomToGo; | |||||
| int CurrentGateToGo; | |||||
| int CurrentStudentToRescue; | |||||
| int CurrentGrassToGo; | |||||
| bool ShouldChangeClassroomToGo; | |||||
| float Courage = 1.0; | |||||
| int TrickerStuckTime; | |||||
| bool rousing = false; | |||||
| int framecount; | |||||
| int MeditationProgress; | |||||
| double ChargeTimeLeft; | |||||
| std::shared_ptr<const THUAI6::Tricker> trickerinfo; | |||||
| THUAI6::Student Students[4]; | |||||
| bool StudentisInSight[4]; | |||||
| enum class TrickerState { | |||||
| MovingToPatrol, MovingToPursue, MovingToSearch, MovingForProp | |||||
| }trickerstate; | |||||
| struct _PatrolNode { | |||||
| int x, y; | |||||
| }; | |||||
| std::vector<_PatrolNode> PatrolNode; | |||||
| int CurrentPatrolNodeToGo; | |||||
| int CurrentStudentToPursue; | |||||
| int StuckTime; | |||||
| int WaitTrickerTime; | |||||
| bool bgmuseful; | |||||
| class Tools { | |||||
| public: | |||||
| double max(double a, double b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| double min(double a, double b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| double Dist(double x1, double y1, double x2, double y2) { | |||||
| return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); | |||||
| } | |||||
| int ToInt(std::string s) { | |||||
| int x = 0; | |||||
| for(int i = 0;i < 5;i++, x *= 10) | |||||
| x += s[i] - '0'; | |||||
| return x / 10; | |||||
| } | |||||
| std::string ToString(int x) { | |||||
| std::string s = "*****"; | |||||
| for(int i = 0;i < 5;i++, x /= 10) | |||||
| s[4 - i] = x % 10 + '0'; | |||||
| return s; | |||||
| } | |||||
| double StuDistValSet(double Dist) { | |||||
| if(Dist < 7500) | |||||
| return Dist; | |||||
| return (Dist - 7500) * 2 + 7500; | |||||
| } | |||||
| double TriDistValSet(double Dist) { | |||||
| int dist1 = 8500, dist2 = 10500; | |||||
| if(Dist < dist1) | |||||
| return Dist * 6; | |||||
| if(Dist >= dist1 && Dist <= dist2) | |||||
| return (Dist - dist1) * 3 + dist1 * 6; | |||||
| return (Dist - dist2) + dist1 * 4 + (dist2 - dist1) * 2; | |||||
| } | |||||
| }T; | |||||
| class A_Star { | |||||
| public: | |||||
| void init() { | |||||
| openlist.clear(); | |||||
| closelist.clear(); | |||||
| } | |||||
| std::list<Node*> GetPath(std::vector<int> st, std::vector<int> ed, bool Reach) { | |||||
| if(st == ed) { | |||||
| std::list<Node*> simplelist; | |||||
| simplelist.push_back(new Node(st[0], st[1])); | |||||
| return simplelist; | |||||
| } | |||||
| openlist.push_back(new Node(st[0], st[1])); | |||||
| while(!openlist.empty()) { | |||||
| Node* CurrentUpdateNode = GetLeastF(); | |||||
| closelist.push_back(CurrentUpdateNode); | |||||
| UpdateFValue(CurrentUpdateNode, ed); | |||||
| Node* CurPathNode; | |||||
| if(!Reach) { | |||||
| CurPathNode = GetDestSurroundNode(ed); | |||||
| if(CurPathNode) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } else { | |||||
| CurPathNode = isInList(openlist, ed[0], ed[1]); | |||||
| if(CurPathNode != NULL) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } | |||||
| openlist.remove(CurrentUpdateNode); | |||||
| } | |||||
| return emptylist; | |||||
| } | |||||
| private: | |||||
| void UpdateFValue(Node* CurNode, std::vector<int> ed) { | |||||
| std::list<std::vector<int>> UpdateList = GetSurroundNode(CurNode); | |||||
| for(auto Curnum = UpdateList.begin();Curnum != UpdateList.end();Curnum++) { | |||||
| int i = (*Curnum)[0], j = (*Curnum)[1]; | |||||
| if(isInList(openlist, i, j) == NULL) { | |||||
| Node* UpdateNode = new Node(i, j); | |||||
| UpdateNode->parent = CurNode; | |||||
| if(map[i][j] == 1) { | |||||
| if(PlayerId <= 3 && studentType[PlayerId] == THUAI6::StudentType::StraightAStudent) { | |||||
| if(MeditationProgress + CurNode->G / 28.8 * 40000 > 300000) | |||||
| UpdateNode->G = CurNode->G + 1; | |||||
| else | |||||
| UpdateNode->G = CurNode->G + 100; | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + 1 * (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->H = (abs(ed[0] - i) + abs(ed[1] - j)) * 10; | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| openlist.push_back(UpdateNode); | |||||
| } else { | |||||
| Node* UpdateNode = isInList(openlist, i, j); | |||||
| int newG = (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| if(CurNode->G + newG <= UpdateNode->G) { | |||||
| UpdateNode->parent = CurNode; | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| std::list<std::vector<int>> GetSurroundNode(Node* CurNode) { | |||||
| std::list<std::vector<int>> SurroundNodeList; | |||||
| bool up = false, down = false, left = false, right = false; | |||||
| if(CurNode->x > 0 && map[CurNode->x - 1][CurNode->y] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y}), up = true; | |||||
| if(CurNode->x < 49 && map[CurNode->x + 1][CurNode->y] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y}), down = true; | |||||
| if(CurNode->y > 0 && map[CurNode->x][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y - 1}), left = true; | |||||
| if(CurNode->y < 49 && map[CurNode->x][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y + 1}), right = true; | |||||
| if(up && left && map[CurNode->x - 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y - 1}); | |||||
| if(up && right && map[CurNode->x - 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y + 1}); | |||||
| if(down && left && map[CurNode->x + 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y - 1}); | |||||
| if(down && right && map[CurNode->x + 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y + 1}); | |||||
| return SurroundNodeList; | |||||
| } | |||||
| Node* isInList(std::list<Node*>& List, int x, int y) { | |||||
| for(auto i = List.begin();i != List.end();i++) | |||||
| if((*i)->x == x && (*i)->y == y) | |||||
| return (*i); | |||||
| return NULL; | |||||
| } | |||||
| Node* GetDestSurroundNode(std::vector<int> ed) { | |||||
| Node* retNode; | |||||
| retNode = isInList(openlist, ed[0] - 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0] + 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] - 1); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] + 1); | |||||
| if(retNode) return retNode; | |||||
| return NULL; | |||||
| } | |||||
| Node* GetLeastF() { | |||||
| if(!openlist.empty()) { | |||||
| Node* resPoint = openlist.front(); | |||||
| for(auto i = openlist.begin();i != openlist.end();i++) | |||||
| if(resPoint->F > (*i)->F) | |||||
| resPoint = *i; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| private: | |||||
| std::list<Node*> openlist; | |||||
| std::list<Node*> closelist; | |||||
| }AStar; | |||||
| int SearchBestClassroom(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < ClassroomCount;i++) { | |||||
| if(!Classroom[i].finished && Classroom[i].occupied == 4) { | |||||
| double val = T.Dist(api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int SearchBestGate(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GateCount;i++) { | |||||
| double val = T.Dist(api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int StudentSearchBestGrass(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| /*api.Print(fmt::format("Grid{},{}", Gridx, Gridy)); | |||||
| api.Print(fmt::format("Tri{},{}", Trickers.x, Trickers.y));*/ | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double val = T.StuDistValSet(T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy)); | |||||
| /*api.Print(fmt::format("Grass{},{}:val{}", Grass[i].x, Grass[i].y, val)); | |||||
| api.Print(fmt::format("Dist(stu,Grass)={}", T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy))); | |||||
| api.Print(fmt::format("Dist(tri,Grass)={}", T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y))));*/ | |||||
| if(val < valmin && !Grass[i].occupied) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int TrickerSearchPossibleGrass(IAPI& api, int DistMax) { | |||||
| double distmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double dist = T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy); | |||||
| if(dist < distmin) { | |||||
| distmin = dist; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| void FindPathTo(std::pair<int, int> Target, bool CanReach, bool ForceToFind = false) { | |||||
| if((PathToGo.empty() || (!PathToGo.empty() && LastTarget != Target)) || ForceToFind) { | |||||
| LastTarget = Target; | |||||
| AStar.init(); | |||||
| PathToGo = AStar.GetPath({Cellx,Celly}, {Target.first,Target.second}, CanReach); | |||||
| } | |||||
| } | |||||
| inline void StudentStart(IAPI& api) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| inline void TrickerStart(IAPI& api) { | |||||
| CurrentPatrolNodeToGo = 0; | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| inline void Start(IAPI& api) { | |||||
| isFirstFrame = false; | |||||
| Map = api.GetFullMap(); | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(Map[i][j] == THUAI6::PlaceType::Window) | |||||
| map[i][j] = 1; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Wall || Map[i][j] == THUAI6::PlaceType::Gate | |||||
| || Map[i][j] == THUAI6::PlaceType::Chest || Map[i][j] == THUAI6::PlaceType::ClassRoom | |||||
| || Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| map[i][j] = 2; | |||||
| if(Map[i][j] == THUAI6::PlaceType::ClassRoom) | |||||
| Classroom[ClassroomCount].x = i, Classroom[ClassroomCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Chest) | |||||
| Chest[ChestCount].x = i, Chest[ChestCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Gate) | |||||
| Gate[GateCount].x = i, Gate[GateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| HiddenGate[HiddenGateCount].x = i, HiddenGate[HiddenGateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Grass) { | |||||
| Grass[GrassCount].x = i; | |||||
| Grass[GrassCount++].y = j; | |||||
| //api.Print(fmt::format("Grass[{}]:{},{}", GrassCount - 1, Grass[GrassCount - 1].x, Grass[GrassCount - 1].y)); | |||||
| } | |||||
| } | |||||
| if(Gate[0].y > 25) { | |||||
| PatrolNode = {{18,18},{31,31},{42,31},{42,18},{31,7},{31,18},{18,31},{18,42},{7,31},{7,18}}; | |||||
| map2 = true; | |||||
| } else { | |||||
| PatrolNode = {{23,13},{18,7},{8,9},{8,33},{14,36},{23,29},{31,35},{42,41},{40,24}}; | |||||
| map1 = true; | |||||
| } | |||||
| if(PlayerId <= 3) { | |||||
| StudentStart(api); | |||||
| } else | |||||
| TrickerStart(api); | |||||
| } | |||||
| void UpdateStudentInfo(IStudentAPI& api) { | |||||
| msg = "*0*0"; | |||||
| if(studentinfo->x == Gridx && studentinfo->y == Gridy && studentinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| switch(studentType[PlayerId]) { | |||||
| case THUAI6::StudentType::StraightAStudent: | |||||
| if((int)studentinfo->playerState >= 1 && (int)studentinfo->playerState <= 10 && (int)studentinfo->playerState != 2) | |||||
| MeditationProgress += 40 * deltaTimems; | |||||
| else | |||||
| MeditationProgress = 0; | |||||
| break; | |||||
| } | |||||
| Gridx = studentinfo->x; | |||||
| Gridy = studentinfo->y; | |||||
| Cellx = api.GridToCell(studentinfo->x); | |||||
| Celly = api.GridToCell(studentinfo->y); | |||||
| for(int i = 0;i < 4;i++) | |||||
| Students[i] = *(api.GetStudents()[i]); | |||||
| auto TrickersInSight = api.GetTrickers(); | |||||
| TrickerisInSight = false; | |||||
| CanGetTrickerInfo = false; | |||||
| for(int i= LastTrickerNum - 1;i >= 1;i--) | |||||
| LastTrickers[i] = LastTrickers[i-1]; | |||||
| LastTrickers[0] = Trickers; | |||||
| if(!TrickersInSight.empty()) { | |||||
| //api.Print("TrickerisInSight!"); | |||||
| TrickerisInSight = true; | |||||
| CanGetTrickerInfo = true; | |||||
| Trickers = *TrickersInSight[0]; | |||||
| } else { | |||||
| //api.Print("TrickerisnotInSight!"); | |||||
| //if(Trickers.x != -1) | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(Trickers.x != -1 && api.HaveView(api.GridToCell(Trickers.x), api.GridToCell(Trickers.y))) { | |||||
| Trickers.x = -1; | |||||
| //api.Print("TrickerisnotInSightremove!"); | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("TrickerStuckTime {}", TrickerStuckTime)); | |||||
| } | |||||
| void UpdateTrickerInfo(ITrickerAPI& api) { | |||||
| if(trickerinfo->x == Gridx && trickerinfo->y == Gridy && trickerinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| Gridx = trickerinfo->x; | |||||
| Gridy = trickerinfo->y; | |||||
| Cellx = api.GridToCell(trickerinfo->x); | |||||
| Celly = api.GridToCell(trickerinfo->y); | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(int i = 0;i < 4;i++) | |||||
| StudentisInSight[i] = false; | |||||
| for(int i = 0;i < StudentsInSight.size();i++) { | |||||
| StudentisInSight[StudentsInSight[i]->playerID] = true; | |||||
| Students[StudentsInSight[i]->playerID] = *StudentsInSight[i]; | |||||
| } | |||||
| } | |||||
| void UpdateMapInfo(IAPI& api) { | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(map[i][j] == 3) | |||||
| map[i][j] = 0; | |||||
| if(map[i][j] % 5 == 3) | |||||
| map[i][j] -= 5; | |||||
| } | |||||
| } | |||||
| void MoveAlongPath(IAPI& api) { | |||||
| if(!PathToGo.empty() && ((!PathGone.empty() && PathToGo.front() != PathGone.front()) || PathGone.empty())) { | |||||
| bool Reach = false; | |||||
| if(PathToGo.size() == 1) { | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) < 100) { | |||||
| Reach = true; | |||||
| } | |||||
| } else { | |||||
| auto it = PathToGo.begin();it++; | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) | |||||
| + T.Dist(Gridx, Gridy, api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) < | |||||
| T.Dist(api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y), | |||||
| api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) * 1.15) { | |||||
| Reach = true; | |||||
| } | |||||
| } | |||||
| if(Reach) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| if(!PathToGo.empty()) { | |||||
| if(Map[PathToGo.front()->x][PathToGo.front()->y] == THUAI6::PlaceType::Window) { | |||||
| if(api.SkipWindow().get()) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| } else { | |||||
| faceDir = atan2(api.CellToGrid(PathToGo.front()->y) - Gridy, api.CellToGrid(PathToGo.front()->x) - Gridx); | |||||
| api.Move(500, faceDir); | |||||
| } | |||||
| } else { | |||||
| if(PlayerId <= 3) { | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::StraightAStudent && studentinfo->timeUntilSkillAvailable[0] == 0) | |||||
| api.UseSkill(0); | |||||
| studentstate = StudentState::Learning; | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| api.StartOpenGate(); | |||||
| studentstate = StudentState::WaitToGraduate; | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| break; | |||||
| } | |||||
| } else { | |||||
| int GrassId; | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| } else { | |||||
| trickerstate = TrickerState::MovingToSearch; | |||||
| GrassId = TrickerSearchPossibleGrass(api, 2000); | |||||
| if(GrassId != -1) { | |||||
| FindPathTo({Grass[GrassId].x,Grass[GrassId].y}, true); | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| bool OnlyTeacherLeft(ITrickerAPI& api) { | |||||
| if(framecount > 6000) | |||||
| return true; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| if(Students[i].studentType != THUAI6::StudentType::Teacher && Students[i].determination > 0) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| int FindStudentInSight(ITrickerAPI& api) { | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(auto i = 0;i < StudentsInSight.size();i++) { | |||||
| if(OnlyTeacherLeft(api)) { | |||||
| if(Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } else { | |||||
| if(Students[StudentsInSight[i]->playerID].studentType != THUAI6::StudentType::Teacher && Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| void ReadMessage(IStudentAPI& api) { | |||||
| for(int i = 0;i < 10;i++) | |||||
| Classroom[i].occupied = 4; | |||||
| if(map1) | |||||
| Classroom[5].occupied = 5; | |||||
| ShouldChangeClassroomToGo = false; | |||||
| while(api.HaveMessage()) { | |||||
| msgrcv = api.GetMessage(); | |||||
| Classroom[msgrcv.second[0] - '0'].occupied = msgrcv.first; | |||||
| if(msgrcv.second[0] - '0' == CurrentClassroomToGo) ShouldChangeClassroomToGo = true; | |||||
| if(msgrcv.second[1] == '1') Classroom[msgrcv.second[2] - '0'].finished = true, ClassroomFinished++; | |||||
| if(msgrcv.second[3] == '1' && studentstate != StudentState::MovingToRescue) { | |||||
| int dist = 1000000, id = 0; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| //api.Print(fmt::format("stu[{}].x = {} stu[{}].y = {}", i, Students[i].x, i, Students[i].y)); | |||||
| if(msgrcv.first != i) { | |||||
| if(dist > T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y)) { | |||||
| dist = T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y); | |||||
| id = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("id{},dist{}", id, dist)); | |||||
| if(id == PlayerId) { | |||||
| studentstate = StudentState::MovingToRescue; | |||||
| CurrentStudentToRescue = msgrcv.first; | |||||
| } | |||||
| } | |||||
| if(msgrcv.second[4] != '*' && !TrickerisInSight) { | |||||
| Trickers.x = T.ToInt(msgrcv.second.substr(4, 5)); | |||||
| Trickers.y = T.ToInt(msgrcv.second.substr(9, 5)); | |||||
| CanGetTrickerInfo = true; | |||||
| } | |||||
| } | |||||
| if(ShouldChangeClassroomToGo) { | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| if(studentstate == StudentState::MovingForClassroom) | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| } | |||||
| void SendMessage(IStudentAPI& api) { | |||||
| if(studentstate == StudentState::MovingForClassroom || studentstate == StudentState::Learning) | |||||
| msg[0] = CurrentClassroomToGo + '0'; | |||||
| if(studentinfo->playerState == THUAI6::PlayerState::Addicted && | |||||
| !((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) | |||||
| msg[3] = '1'; | |||||
| if(TrickerisInSight) | |||||
| msg = msg + T.ToString(Trickers.x) + T.ToString(Trickers.y); | |||||
| else | |||||
| msg = msg + "**********"; | |||||
| for(int i = 0;i < 4;i++) | |||||
| if(PlayerId != i) | |||||
| api.SendTextMessage(i, msg); | |||||
| } | |||||
| bool CheckTrickerStuck(){ | |||||
| int avex = 0,avey = 0; | |||||
| for(int i = 0;i < LastTrickerNum;i++){ | |||||
| avex += LastTrickers[i].x; | |||||
| avey += LastTrickers[i].y; | |||||
| } | |||||
| avex /= LastTrickerNum; | |||||
| avey /= LastTrickerNum; | |||||
| return (T.Dist(avex,avey,Trickers.x,Trickers.y) < 350); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) { | |||||
| studentinfo = api.GetSelfInfo(); | |||||
| //api.Print(fmt::format("studentinfo->dangerAlert {}", studentinfo->dangerAlert)); | |||||
| UpdateMapInfo(api); | |||||
| UpdateStudentInfo(api); | |||||
| if(studentstate != StudentState::Learning && !rousing) { | |||||
| api.EndAllAction(); | |||||
| //api.Print("endallaction"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| } | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| ReadMessage(api); | |||||
| if(CheckTrickerStuck() && CanGetTrickerInfo) { | |||||
| TrickerStuckTime += 50; | |||||
| } else | |||||
| TrickerStuckTime = 0; | |||||
| if(studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && abs(T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) - 15000 / studentinfo->dangerAlert) > 100) | |||||
| Trickers.x = -1; | |||||
| if(!CanGetTrickerInfo) | |||||
| Trickers.x = -1; | |||||
| //api.Print(fmt::format("Trickers{},{}", Trickers.x, Trickers.y)); | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(StuckTime >= 200) { | |||||
| if(PathToGo.size() >= 2) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| StuckTime = 0; | |||||
| } | |||||
| if(studentstate != StudentState::MovingForGrass) { | |||||
| if((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) { | |||||
| rousing = false; | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| //api.Print(fmt::format("grassfind{},{}", Grass[TargetGrass].x, Grass[TargetGrass].y)); | |||||
| } | |||||
| auto bullets = api.GetBullets(); | |||||
| bool FlyingKnifeInSight = false; | |||||
| for(auto i = 0;i < bullets.size();i++) | |||||
| if(bullets[i]->bulletType == THUAI6::BulletType::FlyingKnife){ | |||||
| FlyingKnifeInSight = true; | |||||
| //api.Print(fmt::format("FlyingKnifeInSight!!!")); | |||||
| } | |||||
| if(FlyingKnifeInSight){ | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| } | |||||
| } | |||||
| if(ClassroomFinished >= 7 && studentstate != StudentState::WaitToGraduate && studentstate != StudentState::MovingToGraduate && studentstate != StudentState::MovingForGrass) { | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| } | |||||
| //api.Print(fmt::format("Courage{}", Courage)); | |||||
| //api.Print(fmt::format("EscapeDist / Courage{}", EscapeDist / Courage)); | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01 && studentinfo->determination < 3200000) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| //api.Print(fmt::format("state:MovingForClassroom")); | |||||
| //api.Print(fmt::format("classroom{}:{},{}", CurrentClassroomToGo, Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y)); | |||||
| //CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| case StudentState::Learning: | |||||
| //api.Print(fmt::format("state:Learning")); | |||||
| //api.Print(fmt::format("classroom[{}] process = {}", CurrentClassroomToGo, api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y))); | |||||
| if(api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y) == 10000000) { | |||||
| //api.Print("ClassroomFinished"); | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| Classroom[CurrentClassroomToGo].finished = true; | |||||
| ClassroomFinished++; | |||||
| msg[1] = '1'; | |||||
| msg[2] = (char)(CurrentClassroomToGo + '0'); | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } else | |||||
| api.StartLearning(); | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| Courage += 0.002; | |||||
| //api.Print(fmt::format("state:MovingForGrass")); | |||||
| if(!((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage)))) { | |||||
| WaitTrickerTime += 50; | |||||
| if(Trickers.trickerType == THUAI6::TrickerType::ANoisyPerson){ | |||||
| if(WaitTrickerTime >= 10000) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| }else{ | |||||
| if(WaitTrickerTime >= 2500) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[2] < 0.01) { | |||||
| api.UseSkill(2); | |||||
| } | |||||
| if(PathToGo.empty()) { | |||||
| //api.Print("gototricker"); | |||||
| if(Map[Cellx - 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx - 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx - 1, Celly}, true); | |||||
| if(Map[Cellx + 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx + 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx + 1, Celly}, true); | |||||
| if(Map[Cellx][Celly - 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly - 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly - 1}, true); | |||||
| if(Map[Cellx][Celly + 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly + 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly + 1}, true); | |||||
| } | |||||
| /*int TargetGrass; | |||||
| TargetGrass = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[TargetGrass].x, Grass[TargetGrass].y}, true);*/ | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToRescue: | |||||
| //api.Print(fmt::format("state:MovingToRescue")); | |||||
| //api.Print(fmt::format("rescuestudent{}:{},{}", CurrentStudentToRescue, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| rousing = false; | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToRescue].x),api.GridToCell(Students[CurrentStudentToRescue].y)}, false); | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y) < 1200) { | |||||
| api.StartRouseMate(CurrentStudentToRescue); | |||||
| PathToGo.clear(); | |||||
| rousing = true; | |||||
| //api.Print("rousing!"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| //api.Print("rouseover!"); | |||||
| rousing = false; | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| api.StartOpenGate(); | |||||
| if(api.GetGateProgress(Gate[CurrentGateToGo].x, Gate[CurrentGateToGo].y) == 18000) { | |||||
| api.Graduate(); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| if(TrickerStuckTime >= 200) { | |||||
| Courage += 0.003; | |||||
| }else{ | |||||
| if(CanGetTrickerInfo) | |||||
| Courage = 1; | |||||
| } | |||||
| if(Courage > 1) | |||||
| Courage -= 0.0015; | |||||
| if(Courage > 2.3) | |||||
| Courage = 2.3; | |||||
| if(Courage < 1) | |||||
| Courage = 1; | |||||
| //api.Print(fmt::format("Courage = {}", Courage)); | |||||
| //api.Print(fmt::format("CriticalDist = {}", EscapeDist / Courage)); | |||||
| //api.Print(fmt::format("BGM = {}", studentinfo->dangerAlert)); | |||||
| //api.Print(fmt::format("BGM Dist = {}", 15000 / studentinfo->dangerAlert)); | |||||
| //for(auto i = PathToGo.begin();i != PathToGo.end();i++) | |||||
| // api.Print(fmt::format("{} {}", (*i)->x, (*i)->y)); | |||||
| MoveAlongPath(api); | |||||
| SendMessage(api); | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) { | |||||
| framecount++; | |||||
| if(trickerType == THUAI6::TrickerType::ANoisyPerson) { | |||||
| api.UseSkill(0); | |||||
| goto end; | |||||
| } | |||||
| api.EndAllAction(); | |||||
| trickerinfo = api.GetSelfInfo(); | |||||
| UpdateMapInfo(api); | |||||
| UpdateTrickerInfo(api); | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| int StudentsInSightId; | |||||
| if(StuckTime >= 200) { | |||||
| if(StuckTime >= 1000) { | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } else { | |||||
| if(trickerstate == TrickerState::MovingToSearch || trickerstate == TrickerState::MovingToPursue) { | |||||
| if(api.Attack(faceDir).get()) | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| } else { | |||||
| if(!PathToGo.empty()) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| StudentsInSightId = FindStudentInSight(api); | |||||
| if(StudentsInSightId != -1) { | |||||
| trickerstate = TrickerState::MovingToPursue; | |||||
| CurrentStudentToPursue = StudentsInSightId; | |||||
| } else { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].playerState == THUAI6::PlayerState::Addicted) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToPursue].x), api.GridToCell(Students[CurrentStudentToPursue].y)}, false); | |||||
| if(StudentisInSight[CurrentStudentToPursue]) { | |||||
| if(trickerinfo->timeUntilSkillAvailable[1] == 0) { | |||||
| if(trickerType == THUAI6::TrickerType::Klee && T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.UseSkill(0); | |||||
| } | |||||
| if(api.HaveView(Students[CurrentStudentToPursue].y + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)) | |||||
| if(trickerType == THUAI6::TrickerType::Assassin) | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(trickerinfo->bulletType) { | |||||
| case THUAI6::BulletType::CommonAttackOfTricker: | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| } | |||||
| break; | |||||
| case THUAI6::BulletType::FlyingKnife: | |||||
| api.Attack(atan2( | |||||
| Students[CurrentStudentToPursue].y - Gridy + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x - Gridx + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| case THUAI6::BulletType::BombBomb: | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| break; | |||||
| } | |||||
| MoveAlongPath(api); | |||||
| end:; | |||||
| } | |||||
| @@ -0,0 +1,977 @@ | |||||
| #include <vector> | |||||
| #include <list> | |||||
| #include <math.h> | |||||
| #include <string> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| constexpr int deltaTimems = 50; | |||||
| constexpr int LastTrickerNum = 10; | |||||
| #define deltaTime 0.05 | |||||
| #define pi 3.1415926 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Klee; | |||||
| /* | |||||
| * 全局常量: | |||||
| * | |||||
| * 全局变量: | |||||
| * | |||||
| * Debug相关 | |||||
| * 寻路相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * 学生变量: | |||||
| * 通信相关 | |||||
| * 状态相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * StraightAStudent变量: | |||||
| * 冥想相关 | |||||
| * Athlete变量: | |||||
| * | |||||
| * Teacher变量: | |||||
| * | |||||
| * Sunshine变量: | |||||
| * | |||||
| * | |||||
| * 捣蛋鬼变量: | |||||
| * | |||||
| * | |||||
| * Assas变量: | |||||
| * | |||||
| * 工具类 | |||||
| * A*类 | |||||
| * | |||||
| */ | |||||
| struct Node { | |||||
| int x, y; | |||||
| int F, G, H; | |||||
| Node* parent; | |||||
| Node(int _x, int _y):x(_x), y(_y), F(0), G(0), H(0), parent(NULL) {} | |||||
| }*node[50][50]; | |||||
| const std::list<Node*> emptylist; | |||||
| const int EscapeDist = 16000; | |||||
| std::list<Node*> PathToGo, PathGone; | |||||
| bool isFirstFrame = true; | |||||
| int PlayerId; | |||||
| int Gridx, Gridy; | |||||
| int Cellx, Celly; | |||||
| std::vector<std::vector<THUAI6::PlaceType>> Map; | |||||
| int map[50][50];//0为无阻碍,1为半阻碍,2为完全阻碍, 3为临时避障 | |||||
| struct ClassRoomInfo { | |||||
| int x, y; | |||||
| bool finished; | |||||
| int occupied;//为4说明无学生占用,为0-3说明相应编号学生正在占用 | |||||
| }Classroom[100]; | |||||
| int ClassroomCount, ClassroomFinished; | |||||
| struct ChestInfo { | |||||
| int x, y; | |||||
| bool opened; | |||||
| int occupied; | |||||
| }Chest[80]; | |||||
| int ChestCount; | |||||
| struct GateInfo { | |||||
| int x, y; | |||||
| bool opening; | |||||
| bool open; | |||||
| }Gate[20]; | |||||
| int GateCount; | |||||
| struct HiddenGateInfo { | |||||
| int x, y; | |||||
| bool refreshed; | |||||
| bool open; | |||||
| }HiddenGate[30]; | |||||
| int HiddenGateCount; | |||||
| struct GrassInfo { | |||||
| int x, y, occupied; | |||||
| }Grass[200]; | |||||
| int GrassCount; | |||||
| double faceDir = 0; | |||||
| bool map1 = false, map2 = false; | |||||
| std::pair<int, int> LastTarget; | |||||
| std::pair<int, std::string> msgrcv; | |||||
| std::string msg;//信息结构:'准备前往*号教室''1/0完成了作业''*号教室作业已完成''需要被营救'"捣蛋鬼x坐标""捣蛋鬼y坐标" | |||||
| std::shared_ptr<const THUAI6::Student> studentinfo; | |||||
| THUAI6::Tricker Trickers,LastTrickers[LastTrickerNum]; | |||||
| bool TrickerisInSight; | |||||
| bool CanGetTrickerInfo; | |||||
| enum class StudentState { | |||||
| MovingForClassroom, Learning, MovingForGrass, MovingToAttck, MovingForProp, MovingToRescue, MovingToGraduate, WaitToGraduate | |||||
| }studentstate; | |||||
| int CurrentClassroomToGo; | |||||
| int CurrentGateToGo; | |||||
| int CurrentStudentToRescue; | |||||
| int CurrentGrassToGo; | |||||
| bool ShouldChangeClassroomToGo; | |||||
| float Courage = 1.0; | |||||
| int TrickerStuckTime; | |||||
| bool rousing = false; | |||||
| int framecount; | |||||
| int MeditationProgress; | |||||
| double ChargeTimeLeft; | |||||
| std::shared_ptr<const THUAI6::Tricker> trickerinfo; | |||||
| THUAI6::Student Students[4]; | |||||
| bool StudentisInSight[4]; | |||||
| enum class TrickerState { | |||||
| MovingToPatrol, MovingToPursue, MovingToSearch, MovingForProp | |||||
| }trickerstate; | |||||
| struct _PatrolNode { | |||||
| int x, y; | |||||
| }; | |||||
| std::vector<_PatrolNode> PatrolNode; | |||||
| int CurrentPatrolNodeToGo; | |||||
| int CurrentStudentToPursue; | |||||
| int StuckTime; | |||||
| int WaitTrickerTime; | |||||
| bool bgmuseful; | |||||
| class Tools { | |||||
| public: | |||||
| double max(double a, double b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| double min(double a, double b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| double Dist(double x1, double y1, double x2, double y2) { | |||||
| return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); | |||||
| } | |||||
| int ToInt(std::string s) { | |||||
| int x = 0; | |||||
| for(int i = 0;i < 5;i++, x *= 10) | |||||
| x += s[i] - '0'; | |||||
| return x / 10; | |||||
| } | |||||
| std::string ToString(int x) { | |||||
| std::string s = "*****"; | |||||
| for(int i = 0;i < 5;i++, x /= 10) | |||||
| s[4 - i] = x % 10 + '0'; | |||||
| return s; | |||||
| } | |||||
| double StuDistValSet(double Dist) { | |||||
| if(Dist < 7500) | |||||
| return Dist; | |||||
| return (Dist - 7500) * 2 + 7500; | |||||
| } | |||||
| double TriDistValSet(double Dist) { | |||||
| int dist1 = 8500, dist2 = 10500; | |||||
| if(Dist < dist1) | |||||
| return Dist * 6; | |||||
| if(Dist >= dist1 && Dist <= dist2) | |||||
| return (Dist - dist1) * 3 + dist1 * 6; | |||||
| return (Dist - dist2) + dist1 * 4 + (dist2 - dist1) * 2; | |||||
| } | |||||
| }T; | |||||
| class A_Star { | |||||
| public: | |||||
| void init() { | |||||
| openlist.clear(); | |||||
| closelist.clear(); | |||||
| } | |||||
| std::list<Node*> GetPath(std::vector<int> st, std::vector<int> ed, bool Reach) { | |||||
| if(st == ed) { | |||||
| std::list<Node*> simplelist; | |||||
| simplelist.push_back(new Node(st[0], st[1])); | |||||
| return simplelist; | |||||
| } | |||||
| openlist.push_back(new Node(st[0], st[1])); | |||||
| while(!openlist.empty()) { | |||||
| Node* CurrentUpdateNode = GetLeastF(); | |||||
| closelist.push_back(CurrentUpdateNode); | |||||
| UpdateFValue(CurrentUpdateNode, ed); | |||||
| Node* CurPathNode; | |||||
| if(!Reach) { | |||||
| CurPathNode = GetDestSurroundNode(ed); | |||||
| if(CurPathNode) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } else { | |||||
| CurPathNode = isInList(openlist, ed[0], ed[1]); | |||||
| if(CurPathNode != NULL) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } | |||||
| openlist.remove(CurrentUpdateNode); | |||||
| } | |||||
| return emptylist; | |||||
| } | |||||
| private: | |||||
| void UpdateFValue(Node* CurNode, std::vector<int> ed) { | |||||
| std::list<std::vector<int>> UpdateList = GetSurroundNode(CurNode); | |||||
| for(auto Curnum = UpdateList.begin();Curnum != UpdateList.end();Curnum++) { | |||||
| int i = (*Curnum)[0], j = (*Curnum)[1]; | |||||
| if(isInList(openlist, i, j) == NULL) { | |||||
| Node* UpdateNode = new Node(i, j); | |||||
| UpdateNode->parent = CurNode; | |||||
| if(map[i][j] == 1) { | |||||
| if(PlayerId <= 3 && studentType[PlayerId] == THUAI6::StudentType::StraightAStudent) { | |||||
| if(MeditationProgress + CurNode->G / 28.8 * 40000 > 300000) | |||||
| UpdateNode->G = CurNode->G + 1; | |||||
| else | |||||
| UpdateNode->G = CurNode->G + 100; | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + 1 * (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->H = (abs(ed[0] - i) + abs(ed[1] - j)) * 10; | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| openlist.push_back(UpdateNode); | |||||
| } else { | |||||
| Node* UpdateNode = isInList(openlist, i, j); | |||||
| int newG = (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| if(CurNode->G + newG <= UpdateNode->G) { | |||||
| UpdateNode->parent = CurNode; | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| std::list<std::vector<int>> GetSurroundNode(Node* CurNode) { | |||||
| std::list<std::vector<int>> SurroundNodeList; | |||||
| bool up = false, down = false, left = false, right = false; | |||||
| if(CurNode->x > 0 && map[CurNode->x - 1][CurNode->y] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y}), up = true; | |||||
| if(CurNode->x < 49 && map[CurNode->x + 1][CurNode->y] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y}), down = true; | |||||
| if(CurNode->y > 0 && map[CurNode->x][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y - 1}), left = true; | |||||
| if(CurNode->y < 49 && map[CurNode->x][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y + 1}), right = true; | |||||
| if(up && left && map[CurNode->x - 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y - 1}); | |||||
| if(up && right && map[CurNode->x - 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y + 1}); | |||||
| if(down && left && map[CurNode->x + 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y - 1}); | |||||
| if(down && right && map[CurNode->x + 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y + 1}); | |||||
| return SurroundNodeList; | |||||
| } | |||||
| Node* isInList(std::list<Node*>& List, int x, int y) { | |||||
| for(auto i = List.begin();i != List.end();i++) | |||||
| if((*i)->x == x && (*i)->y == y) | |||||
| return (*i); | |||||
| return NULL; | |||||
| } | |||||
| Node* GetDestSurroundNode(std::vector<int> ed) { | |||||
| Node* retNode; | |||||
| retNode = isInList(openlist, ed[0] - 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0] + 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] - 1); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] + 1); | |||||
| if(retNode) return retNode; | |||||
| return NULL; | |||||
| } | |||||
| Node* GetLeastF() { | |||||
| if(!openlist.empty()) { | |||||
| Node* resPoint = openlist.front(); | |||||
| for(auto i = openlist.begin();i != openlist.end();i++) | |||||
| if(resPoint->F > (*i)->F) | |||||
| resPoint = *i; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| private: | |||||
| std::list<Node*> openlist; | |||||
| std::list<Node*> closelist; | |||||
| }AStar; | |||||
| int SearchBestClassroom(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < ClassroomCount;i++) { | |||||
| if(!Classroom[i].finished && Classroom[i].occupied == 4) { | |||||
| double val = T.Dist(api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int SearchBestGate(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GateCount;i++) { | |||||
| double val = T.Dist(api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int StudentSearchBestGrass(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| /*api.Print(fmt::format("Grid{},{}", Gridx, Gridy)); | |||||
| api.Print(fmt::format("Tri{},{}", Trickers.x, Trickers.y));*/ | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double val = T.StuDistValSet(T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy)); | |||||
| /*api.Print(fmt::format("Grass{},{}:val{}", Grass[i].x, Grass[i].y, val)); | |||||
| api.Print(fmt::format("Dist(stu,Grass)={}", T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy))); | |||||
| api.Print(fmt::format("Dist(tri,Grass)={}", T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y))));*/ | |||||
| if(val < valmin && !Grass[i].occupied) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int TrickerSearchPossibleGrass(IAPI& api, int DistMax) { | |||||
| double distmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double dist = T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy); | |||||
| if(dist < distmin) { | |||||
| distmin = dist; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| void FindPathTo(std::pair<int, int> Target, bool CanReach, bool ForceToFind = false) { | |||||
| if((PathToGo.empty() || (!PathToGo.empty() && LastTarget != Target)) || ForceToFind) { | |||||
| LastTarget = Target; | |||||
| AStar.init(); | |||||
| PathToGo = AStar.GetPath({Cellx,Celly}, {Target.first,Target.second}, CanReach); | |||||
| } | |||||
| } | |||||
| inline void StudentStart(IAPI& api) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| inline void TrickerStart(IAPI& api) { | |||||
| CurrentPatrolNodeToGo = 0; | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| inline void Start(IAPI& api) { | |||||
| isFirstFrame = false; | |||||
| Map = api.GetFullMap(); | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(Map[i][j] == THUAI6::PlaceType::Window) | |||||
| map[i][j] = 1; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Wall || Map[i][j] == THUAI6::PlaceType::Gate | |||||
| || Map[i][j] == THUAI6::PlaceType::Chest || Map[i][j] == THUAI6::PlaceType::ClassRoom | |||||
| || Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| map[i][j] = 2; | |||||
| if(Map[i][j] == THUAI6::PlaceType::ClassRoom) | |||||
| Classroom[ClassroomCount].x = i, Classroom[ClassroomCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Chest) | |||||
| Chest[ChestCount].x = i, Chest[ChestCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Gate) | |||||
| Gate[GateCount].x = i, Gate[GateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| HiddenGate[HiddenGateCount].x = i, HiddenGate[HiddenGateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Grass) { | |||||
| Grass[GrassCount].x = i; | |||||
| Grass[GrassCount++].y = j; | |||||
| //api.Print(fmt::format("Grass[{}]:{},{}", GrassCount - 1, Grass[GrassCount - 1].x, Grass[GrassCount - 1].y)); | |||||
| } | |||||
| } | |||||
| if(Gate[0].y > 25) { | |||||
| PatrolNode = {{18,18},{31,31},{42,31},{42,18},{31,7},{31,18},{18,31},{18,42},{7,31},{7,18}}; | |||||
| map2 = true; | |||||
| } else { | |||||
| PatrolNode = {{23,13},{18,7},{8,9},{8,33},{14,36},{23,29},{31,35},{42,41},{40,24}}; | |||||
| map1 = true; | |||||
| } | |||||
| if(PlayerId <= 3) { | |||||
| StudentStart(api); | |||||
| } else | |||||
| TrickerStart(api); | |||||
| } | |||||
| void UpdateStudentInfo(IStudentAPI& api) { | |||||
| msg = "*0*0"; | |||||
| if(studentinfo->x == Gridx && studentinfo->y == Gridy && studentinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| switch(studentType[PlayerId]) { | |||||
| case THUAI6::StudentType::StraightAStudent: | |||||
| if((int)studentinfo->playerState >= 1 && (int)studentinfo->playerState <= 10 && (int)studentinfo->playerState != 2) | |||||
| MeditationProgress += 40 * deltaTimems; | |||||
| else | |||||
| MeditationProgress = 0; | |||||
| break; | |||||
| } | |||||
| Gridx = studentinfo->x; | |||||
| Gridy = studentinfo->y; | |||||
| Cellx = api.GridToCell(studentinfo->x); | |||||
| Celly = api.GridToCell(studentinfo->y); | |||||
| for(int i = 0;i < 4;i++) | |||||
| Students[i] = *(api.GetStudents()[i]); | |||||
| auto TrickersInSight = api.GetTrickers(); | |||||
| TrickerisInSight = false; | |||||
| CanGetTrickerInfo = false; | |||||
| for(int i= LastTrickerNum - 1;i >= 1;i--) | |||||
| LastTrickers[i] = LastTrickers[i-1]; | |||||
| LastTrickers[0] = Trickers; | |||||
| if(!TrickersInSight.empty()) { | |||||
| //api.Print("TrickerisInSight!"); | |||||
| TrickerisInSight = true; | |||||
| CanGetTrickerInfo = true; | |||||
| Trickers = *TrickersInSight[0]; | |||||
| } else { | |||||
| //api.Print("TrickerisnotInSight!"); | |||||
| //if(Trickers.x != -1) | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(Trickers.x != -1 && api.HaveView(api.GridToCell(Trickers.x), api.GridToCell(Trickers.y))) { | |||||
| Trickers.x = -1; | |||||
| //api.Print("TrickerisnotInSightremove!"); | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("TrickerStuckTime {}", TrickerStuckTime)); | |||||
| } | |||||
| void UpdateTrickerInfo(ITrickerAPI& api) { | |||||
| if(trickerinfo->x == Gridx && trickerinfo->y == Gridy && trickerinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| Gridx = trickerinfo->x; | |||||
| Gridy = trickerinfo->y; | |||||
| Cellx = api.GridToCell(trickerinfo->x); | |||||
| Celly = api.GridToCell(trickerinfo->y); | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(int i = 0;i < 4;i++) | |||||
| StudentisInSight[i] = false; | |||||
| for(int i = 0;i < StudentsInSight.size();i++) { | |||||
| StudentisInSight[StudentsInSight[i]->playerID] = true; | |||||
| Students[StudentsInSight[i]->playerID] = *StudentsInSight[i]; | |||||
| } | |||||
| } | |||||
| void UpdateMapInfo(IAPI& api) { | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(map[i][j] == 3) | |||||
| map[i][j] = 0; | |||||
| if(map[i][j] % 5 == 3) | |||||
| map[i][j] -= 5; | |||||
| } | |||||
| } | |||||
| void MoveAlongPath(IAPI& api) { | |||||
| if(!PathToGo.empty() && ((!PathGone.empty() && PathToGo.front() != PathGone.front()) || PathGone.empty())) { | |||||
| bool Reach = false; | |||||
| if(PathToGo.size() == 1) { | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) < 100) { | |||||
| Reach = true; | |||||
| } | |||||
| } else { | |||||
| auto it = PathToGo.begin();it++; | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) | |||||
| + T.Dist(Gridx, Gridy, api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) < | |||||
| T.Dist(api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y), | |||||
| api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) * 1.15) { | |||||
| Reach = true; | |||||
| } | |||||
| } | |||||
| if(Reach) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| if(!PathToGo.empty()) { | |||||
| if(Map[PathToGo.front()->x][PathToGo.front()->y] == THUAI6::PlaceType::Window) { | |||||
| if(api.SkipWindow().get()) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| } else { | |||||
| faceDir = atan2(api.CellToGrid(PathToGo.front()->y) - Gridy, api.CellToGrid(PathToGo.front()->x) - Gridx); | |||||
| api.Move(500, faceDir); | |||||
| } | |||||
| } else { | |||||
| if(PlayerId <= 3) { | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::StraightAStudent && studentinfo->timeUntilSkillAvailable[0] == 0) | |||||
| api.UseSkill(0); | |||||
| studentstate = StudentState::Learning; | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| api.StartOpenGate(); | |||||
| studentstate = StudentState::WaitToGraduate; | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| break; | |||||
| } | |||||
| } else { | |||||
| int GrassId; | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| } else { | |||||
| trickerstate = TrickerState::MovingToSearch; | |||||
| GrassId = TrickerSearchPossibleGrass(api, 2000); | |||||
| if(GrassId != -1) { | |||||
| FindPathTo({Grass[GrassId].x,Grass[GrassId].y}, true); | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| bool OnlyTeacherLeft(ITrickerAPI& api) { | |||||
| if(framecount > 6000) | |||||
| return true; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| if(Students[i].studentType != THUAI6::StudentType::Teacher && Students[i].determination > 0) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| int FindStudentInSight(ITrickerAPI& api) { | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(auto i = 0;i < StudentsInSight.size();i++) { | |||||
| if(OnlyTeacherLeft(api)) { | |||||
| if(Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } else { | |||||
| if(Students[StudentsInSight[i]->playerID].studentType != THUAI6::StudentType::Teacher && Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| void ReadMessage(IStudentAPI& api) { | |||||
| for(int i = 0;i < 10;i++) | |||||
| Classroom[i].occupied = 4; | |||||
| if(map1) | |||||
| Classroom[5].occupied = 5; | |||||
| ShouldChangeClassroomToGo = false; | |||||
| while(api.HaveMessage()) { | |||||
| msgrcv = api.GetMessage(); | |||||
| Classroom[msgrcv.second[0] - '0'].occupied = msgrcv.first; | |||||
| if(msgrcv.second[0] - '0' == CurrentClassroomToGo) ShouldChangeClassroomToGo = true; | |||||
| if(msgrcv.second[1] == '1') Classroom[msgrcv.second[2] - '0'].finished = true, ClassroomFinished++; | |||||
| if(msgrcv.second[3] == '1' && studentstate != StudentState::MovingToRescue) { | |||||
| int dist = 1000000, id = 0; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| //api.Print(fmt::format("stu[{}].x = {} stu[{}].y = {}", i, Students[i].x, i, Students[i].y)); | |||||
| if(msgrcv.first != i) { | |||||
| if(dist > T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y)) { | |||||
| dist = T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y); | |||||
| id = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("id{},dist{}", id, dist)); | |||||
| if(id == PlayerId) { | |||||
| studentstate = StudentState::MovingToRescue; | |||||
| CurrentStudentToRescue = msgrcv.first; | |||||
| } | |||||
| } | |||||
| if(msgrcv.second[4] != '*' && !TrickerisInSight) { | |||||
| Trickers.x = T.ToInt(msgrcv.second.substr(4, 5)); | |||||
| Trickers.y = T.ToInt(msgrcv.second.substr(9, 5)); | |||||
| CanGetTrickerInfo = true; | |||||
| } | |||||
| } | |||||
| if(ShouldChangeClassroomToGo) { | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| if(studentstate == StudentState::MovingForClassroom) | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| } | |||||
| void SendMessage(IStudentAPI& api) { | |||||
| if(studentstate == StudentState::MovingForClassroom || studentstate == StudentState::Learning) | |||||
| msg[0] = CurrentClassroomToGo + '0'; | |||||
| if(studentinfo->playerState == THUAI6::PlayerState::Addicted && | |||||
| !((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) | |||||
| msg[3] = '1'; | |||||
| if(TrickerisInSight) | |||||
| msg = msg + T.ToString(Trickers.x) + T.ToString(Trickers.y); | |||||
| else | |||||
| msg = msg + "**********"; | |||||
| for(int i = 0;i < 4;i++) | |||||
| if(PlayerId != i) | |||||
| api.SendTextMessage(i, msg); | |||||
| } | |||||
| bool CheckTrickerStuck(){ | |||||
| int avex = 0,avey = 0; | |||||
| for(int i = 0;i < LastTrickerNum;i++){ | |||||
| avex += LastTrickers[i].x; | |||||
| avey += LastTrickers[i].y; | |||||
| } | |||||
| avex /= LastTrickerNum; | |||||
| avey /= LastTrickerNum; | |||||
| return (T.Dist(avex,avey,Trickers.x,Trickers.y) < 350); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) { | |||||
| studentinfo = api.GetSelfInfo(); | |||||
| //api.Print(fmt::format("studentinfo->dangerAlert {}", studentinfo->dangerAlert)); | |||||
| UpdateMapInfo(api); | |||||
| UpdateStudentInfo(api); | |||||
| if(studentstate != StudentState::Learning && !rousing) { | |||||
| api.EndAllAction(); | |||||
| //api.Print("endallaction"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| } | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| ReadMessage(api); | |||||
| if(CheckTrickerStuck() && CanGetTrickerInfo) { | |||||
| TrickerStuckTime += 50; | |||||
| } else | |||||
| TrickerStuckTime = 0; | |||||
| if(studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && abs(T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) - 15000 / studentinfo->dangerAlert) > 100) | |||||
| Trickers.x = -1; | |||||
| if(!CanGetTrickerInfo) | |||||
| Trickers.x = -1; | |||||
| //api.Print(fmt::format("Trickers{},{}", Trickers.x, Trickers.y)); | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(StuckTime >= 200) { | |||||
| if(PathToGo.size() >= 2) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| StuckTime = 0; | |||||
| } | |||||
| if(studentstate != StudentState::MovingForGrass) { | |||||
| if((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) { | |||||
| rousing = false; | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| //api.Print(fmt::format("grassfind{},{}", Grass[TargetGrass].x, Grass[TargetGrass].y)); | |||||
| } | |||||
| auto bullets = api.GetBullets(); | |||||
| bool FlyingKnifeInSight = false; | |||||
| for(auto i = 0;i < bullets.size();i++) | |||||
| if(bullets[i]->bulletType == THUAI6::BulletType::FlyingKnife){ | |||||
| FlyingKnifeInSight = true; | |||||
| //api.Print(fmt::format("FlyingKnifeInSight!!!")); | |||||
| } | |||||
| if(FlyingKnifeInSight){ | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| } | |||||
| } | |||||
| if(ClassroomFinished >= 7 && studentstate != StudentState::WaitToGraduate && studentstate != StudentState::MovingToGraduate && studentstate != StudentState::MovingForGrass) { | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| } | |||||
| //api.Print(fmt::format("Courage{}", Courage)); | |||||
| //api.Print(fmt::format("EscapeDist / Courage{}", EscapeDist / Courage)); | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01 && studentinfo->determination < 3200000) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| //api.Print(fmt::format("state:MovingForClassroom")); | |||||
| //api.Print(fmt::format("classroom{}:{},{}", CurrentClassroomToGo, Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y)); | |||||
| //CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| case StudentState::Learning: | |||||
| //api.Print(fmt::format("state:Learning")); | |||||
| //api.Print(fmt::format("classroom[{}] process = {}", CurrentClassroomToGo, api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y))); | |||||
| if(api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y) == 10000000) { | |||||
| //api.Print("ClassroomFinished"); | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| Classroom[CurrentClassroomToGo].finished = true; | |||||
| ClassroomFinished++; | |||||
| msg[1] = '1'; | |||||
| msg[2] = (char)(CurrentClassroomToGo + '0'); | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } else | |||||
| api.StartLearning(); | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| Courage += 0.002; | |||||
| //api.Print(fmt::format("state:MovingForGrass")); | |||||
| if(!((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage)))) { | |||||
| WaitTrickerTime += 50; | |||||
| if(Trickers.trickerType == THUAI6::TrickerType::ANoisyPerson){ | |||||
| if(WaitTrickerTime >= 10000) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| }else{ | |||||
| if(WaitTrickerTime >= 2500) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[2] < 0.01) { | |||||
| api.UseSkill(2); | |||||
| } | |||||
| if(PathToGo.empty()) { | |||||
| //api.Print("gototricker"); | |||||
| if(Map[Cellx - 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx - 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx - 1, Celly}, true); | |||||
| if(Map[Cellx + 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx + 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx + 1, Celly}, true); | |||||
| if(Map[Cellx][Celly - 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly - 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly - 1}, true); | |||||
| if(Map[Cellx][Celly + 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly + 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly + 1}, true); | |||||
| } | |||||
| /*int TargetGrass; | |||||
| TargetGrass = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[TargetGrass].x, Grass[TargetGrass].y}, true);*/ | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToRescue: | |||||
| //api.Print(fmt::format("state:MovingToRescue")); | |||||
| //api.Print(fmt::format("rescuestudent{}:{},{}", CurrentStudentToRescue, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| rousing = false; | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToRescue].x),api.GridToCell(Students[CurrentStudentToRescue].y)}, false); | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y) < 1200) { | |||||
| api.StartRouseMate(CurrentStudentToRescue); | |||||
| PathToGo.clear(); | |||||
| rousing = true; | |||||
| //api.Print("rousing!"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| //api.Print("rouseover!"); | |||||
| rousing = false; | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| api.StartOpenGate(); | |||||
| if(api.GetGateProgress(Gate[CurrentGateToGo].x, Gate[CurrentGateToGo].y) == 18000) { | |||||
| api.Graduate(); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| if(TrickerStuckTime >= 200) { | |||||
| Courage += 0.003; | |||||
| }else{ | |||||
| if(CanGetTrickerInfo) | |||||
| Courage = 1; | |||||
| } | |||||
| if(Courage > 1) | |||||
| Courage -= 0.0015; | |||||
| if(Courage > 2.3) | |||||
| Courage = 2.3; | |||||
| if(Courage < 1) | |||||
| Courage = 1; | |||||
| //api.Print(fmt::format("Courage = {}", Courage)); | |||||
| //api.Print(fmt::format("CriticalDist = {}", EscapeDist / Courage)); | |||||
| //api.Print(fmt::format("BGM = {}", studentinfo->dangerAlert)); | |||||
| //api.Print(fmt::format("BGM Dist = {}", 15000 / studentinfo->dangerAlert)); | |||||
| //for(auto i = PathToGo.begin();i != PathToGo.end();i++) | |||||
| // api.Print(fmt::format("{} {}", (*i)->x, (*i)->y)); | |||||
| MoveAlongPath(api); | |||||
| SendMessage(api); | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) { | |||||
| framecount++; | |||||
| if(trickerType == THUAI6::TrickerType::ANoisyPerson) { | |||||
| api.UseSkill(0); | |||||
| goto end; | |||||
| } | |||||
| api.EndAllAction(); | |||||
| trickerinfo = api.GetSelfInfo(); | |||||
| UpdateMapInfo(api); | |||||
| UpdateTrickerInfo(api); | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| int StudentsInSightId; | |||||
| if(StuckTime >= 200) { | |||||
| if(StuckTime >= 1000) { | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } else { | |||||
| if(trickerstate == TrickerState::MovingToSearch || trickerstate == TrickerState::MovingToPursue) { | |||||
| if(api.Attack(faceDir).get()) | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| } else { | |||||
| if(!PathToGo.empty()) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| StudentsInSightId = FindStudentInSight(api); | |||||
| if(StudentsInSightId != -1) { | |||||
| trickerstate = TrickerState::MovingToPursue; | |||||
| CurrentStudentToPursue = StudentsInSightId; | |||||
| } else { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].playerState == THUAI6::PlayerState::Addicted) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToPursue].x), api.GridToCell(Students[CurrentStudentToPursue].y)}, false); | |||||
| if(StudentisInSight[CurrentStudentToPursue]) { | |||||
| if(trickerinfo->timeUntilSkillAvailable[1] == 0) { | |||||
| if(trickerType == THUAI6::TrickerType::Klee && T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.UseSkill(0); | |||||
| } | |||||
| if(api.HaveView(Students[CurrentStudentToPursue].y + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)) | |||||
| if(trickerType == THUAI6::TrickerType::Assassin) | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(trickerinfo->bulletType) { | |||||
| case THUAI6::BulletType::CommonAttackOfTricker: | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| } | |||||
| break; | |||||
| case THUAI6::BulletType::FlyingKnife: | |||||
| api.Attack(atan2( | |||||
| Students[CurrentStudentToPursue].y - Gridy + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x - Gridx + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| case THUAI6::BulletType::BombBomb: | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| break; | |||||
| } | |||||
| MoveAlongPath(api); | |||||
| end:; | |||||
| } | |||||
| @@ -0,0 +1,977 @@ | |||||
| #include <vector> | |||||
| #include <list> | |||||
| #include <math.h> | |||||
| #include <string> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| constexpr int deltaTimems = 50; | |||||
| constexpr int LastTrickerNum = 10; | |||||
| #define deltaTime 0.05 | |||||
| #define pi 3.1415926 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Klee; | |||||
| /* | |||||
| * 全局常量: | |||||
| * | |||||
| * 全局变量: | |||||
| * | |||||
| * Debug相关 | |||||
| * 寻路相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * 学生变量: | |||||
| * 通信相关 | |||||
| * 状态相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * StraightAStudent变量: | |||||
| * 冥想相关 | |||||
| * Athlete变量: | |||||
| * | |||||
| * Teacher变量: | |||||
| * | |||||
| * Sunshine变量: | |||||
| * | |||||
| * | |||||
| * 捣蛋鬼变量: | |||||
| * | |||||
| * | |||||
| * Assas变量: | |||||
| * | |||||
| * 工具类 | |||||
| * A*类 | |||||
| * | |||||
| */ | |||||
| struct Node { | |||||
| int x, y; | |||||
| int F, G, H; | |||||
| Node* parent; | |||||
| Node(int _x, int _y):x(_x), y(_y), F(0), G(0), H(0), parent(NULL) {} | |||||
| }*node[50][50]; | |||||
| const std::list<Node*> emptylist; | |||||
| const int EscapeDist = 16000; | |||||
| std::list<Node*> PathToGo, PathGone; | |||||
| bool isFirstFrame = true; | |||||
| int PlayerId; | |||||
| int Gridx, Gridy; | |||||
| int Cellx, Celly; | |||||
| std::vector<std::vector<THUAI6::PlaceType>> Map; | |||||
| int map[50][50];//0为无阻碍,1为半阻碍,2为完全阻碍, 3为临时避障 | |||||
| struct ClassRoomInfo { | |||||
| int x, y; | |||||
| bool finished; | |||||
| int occupied;//为4说明无学生占用,为0-3说明相应编号学生正在占用 | |||||
| }Classroom[100]; | |||||
| int ClassroomCount, ClassroomFinished; | |||||
| struct ChestInfo { | |||||
| int x, y; | |||||
| bool opened; | |||||
| int occupied; | |||||
| }Chest[80]; | |||||
| int ChestCount; | |||||
| struct GateInfo { | |||||
| int x, y; | |||||
| bool opening; | |||||
| bool open; | |||||
| }Gate[20]; | |||||
| int GateCount; | |||||
| struct HiddenGateInfo { | |||||
| int x, y; | |||||
| bool refreshed; | |||||
| bool open; | |||||
| }HiddenGate[30]; | |||||
| int HiddenGateCount; | |||||
| struct GrassInfo { | |||||
| int x, y, occupied; | |||||
| }Grass[200]; | |||||
| int GrassCount; | |||||
| double faceDir = 0; | |||||
| bool map1 = false, map2 = false; | |||||
| std::pair<int, int> LastTarget; | |||||
| std::pair<int, std::string> msgrcv; | |||||
| std::string msg;//信息结构:'准备前往*号教室''1/0完成了作业''*号教室作业已完成''需要被营救'"捣蛋鬼x坐标""捣蛋鬼y坐标" | |||||
| std::shared_ptr<const THUAI6::Student> studentinfo; | |||||
| THUAI6::Tricker Trickers,LastTrickers[LastTrickerNum]; | |||||
| bool TrickerisInSight; | |||||
| bool CanGetTrickerInfo; | |||||
| enum class StudentState { | |||||
| MovingForClassroom, Learning, MovingForGrass, MovingToAttck, MovingForProp, MovingToRescue, MovingToGraduate, WaitToGraduate | |||||
| }studentstate; | |||||
| int CurrentClassroomToGo; | |||||
| int CurrentGateToGo; | |||||
| int CurrentStudentToRescue; | |||||
| int CurrentGrassToGo; | |||||
| bool ShouldChangeClassroomToGo; | |||||
| float Courage = 1.0; | |||||
| int TrickerStuckTime; | |||||
| bool rousing = false; | |||||
| int framecount; | |||||
| int MeditationProgress; | |||||
| double ChargeTimeLeft; | |||||
| std::shared_ptr<const THUAI6::Tricker> trickerinfo; | |||||
| THUAI6::Student Students[4]; | |||||
| bool StudentisInSight[4]; | |||||
| enum class TrickerState { | |||||
| MovingToPatrol, MovingToPursue, MovingToSearch, MovingForProp | |||||
| }trickerstate; | |||||
| struct _PatrolNode { | |||||
| int x, y; | |||||
| }; | |||||
| std::vector<_PatrolNode> PatrolNode; | |||||
| int CurrentPatrolNodeToGo; | |||||
| int CurrentStudentToPursue; | |||||
| int StuckTime; | |||||
| int WaitTrickerTime; | |||||
| bool bgmuseful; | |||||
| class Tools { | |||||
| public: | |||||
| double max(double a, double b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| double min(double a, double b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| double Dist(double x1, double y1, double x2, double y2) { | |||||
| return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); | |||||
| } | |||||
| int ToInt(std::string s) { | |||||
| int x = 0; | |||||
| for(int i = 0;i < 5;i++, x *= 10) | |||||
| x += s[i] - '0'; | |||||
| return x / 10; | |||||
| } | |||||
| std::string ToString(int x) { | |||||
| std::string s = "*****"; | |||||
| for(int i = 0;i < 5;i++, x /= 10) | |||||
| s[4 - i] = x % 10 + '0'; | |||||
| return s; | |||||
| } | |||||
| double StuDistValSet(double Dist) { | |||||
| if(Dist < 7500) | |||||
| return Dist; | |||||
| return (Dist - 7500) * 2 + 7500; | |||||
| } | |||||
| double TriDistValSet(double Dist) { | |||||
| int dist1 = 8500, dist2 = 10500; | |||||
| if(Dist < dist1) | |||||
| return Dist * 6; | |||||
| if(Dist >= dist1 && Dist <= dist2) | |||||
| return (Dist - dist1) * 3 + dist1 * 6; | |||||
| return (Dist - dist2) + dist1 * 4 + (dist2 - dist1) * 2; | |||||
| } | |||||
| }T; | |||||
| class A_Star { | |||||
| public: | |||||
| void init() { | |||||
| openlist.clear(); | |||||
| closelist.clear(); | |||||
| } | |||||
| std::list<Node*> GetPath(std::vector<int> st, std::vector<int> ed, bool Reach) { | |||||
| if(st == ed) { | |||||
| std::list<Node*> simplelist; | |||||
| simplelist.push_back(new Node(st[0], st[1])); | |||||
| return simplelist; | |||||
| } | |||||
| openlist.push_back(new Node(st[0], st[1])); | |||||
| while(!openlist.empty()) { | |||||
| Node* CurrentUpdateNode = GetLeastF(); | |||||
| closelist.push_back(CurrentUpdateNode); | |||||
| UpdateFValue(CurrentUpdateNode, ed); | |||||
| Node* CurPathNode; | |||||
| if(!Reach) { | |||||
| CurPathNode = GetDestSurroundNode(ed); | |||||
| if(CurPathNode) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } else { | |||||
| CurPathNode = isInList(openlist, ed[0], ed[1]); | |||||
| if(CurPathNode != NULL) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } | |||||
| openlist.remove(CurrentUpdateNode); | |||||
| } | |||||
| return emptylist; | |||||
| } | |||||
| private: | |||||
| void UpdateFValue(Node* CurNode, std::vector<int> ed) { | |||||
| std::list<std::vector<int>> UpdateList = GetSurroundNode(CurNode); | |||||
| for(auto Curnum = UpdateList.begin();Curnum != UpdateList.end();Curnum++) { | |||||
| int i = (*Curnum)[0], j = (*Curnum)[1]; | |||||
| if(isInList(openlist, i, j) == NULL) { | |||||
| Node* UpdateNode = new Node(i, j); | |||||
| UpdateNode->parent = CurNode; | |||||
| if(map[i][j] == 1) { | |||||
| if(PlayerId <= 3 && studentType[PlayerId] == THUAI6::StudentType::StraightAStudent) { | |||||
| if(MeditationProgress + CurNode->G / 28.8 * 40000 > 300000) | |||||
| UpdateNode->G = CurNode->G + 1; | |||||
| else | |||||
| UpdateNode->G = CurNode->G + 100; | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + 1 * (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->H = (abs(ed[0] - i) + abs(ed[1] - j)) * 10; | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| openlist.push_back(UpdateNode); | |||||
| } else { | |||||
| Node* UpdateNode = isInList(openlist, i, j); | |||||
| int newG = (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| if(CurNode->G + newG <= UpdateNode->G) { | |||||
| UpdateNode->parent = CurNode; | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| std::list<std::vector<int>> GetSurroundNode(Node* CurNode) { | |||||
| std::list<std::vector<int>> SurroundNodeList; | |||||
| bool up = false, down = false, left = false, right = false; | |||||
| if(CurNode->x > 0 && map[CurNode->x - 1][CurNode->y] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y}), up = true; | |||||
| if(CurNode->x < 49 && map[CurNode->x + 1][CurNode->y] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y}), down = true; | |||||
| if(CurNode->y > 0 && map[CurNode->x][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y - 1}), left = true; | |||||
| if(CurNode->y < 49 && map[CurNode->x][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y + 1}), right = true; | |||||
| if(up && left && map[CurNode->x - 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y - 1}); | |||||
| if(up && right && map[CurNode->x - 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y + 1}); | |||||
| if(down && left && map[CurNode->x + 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y - 1}); | |||||
| if(down && right && map[CurNode->x + 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y + 1}); | |||||
| return SurroundNodeList; | |||||
| } | |||||
| Node* isInList(std::list<Node*>& List, int x, int y) { | |||||
| for(auto i = List.begin();i != List.end();i++) | |||||
| if((*i)->x == x && (*i)->y == y) | |||||
| return (*i); | |||||
| return NULL; | |||||
| } | |||||
| Node* GetDestSurroundNode(std::vector<int> ed) { | |||||
| Node* retNode; | |||||
| retNode = isInList(openlist, ed[0] - 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0] + 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] - 1); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] + 1); | |||||
| if(retNode) return retNode; | |||||
| return NULL; | |||||
| } | |||||
| Node* GetLeastF() { | |||||
| if(!openlist.empty()) { | |||||
| Node* resPoint = openlist.front(); | |||||
| for(auto i = openlist.begin();i != openlist.end();i++) | |||||
| if(resPoint->F > (*i)->F) | |||||
| resPoint = *i; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| private: | |||||
| std::list<Node*> openlist; | |||||
| std::list<Node*> closelist; | |||||
| }AStar; | |||||
| int SearchBestClassroom(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < ClassroomCount;i++) { | |||||
| if(!Classroom[i].finished && Classroom[i].occupied == 4) { | |||||
| double val = T.Dist(api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int SearchBestGate(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GateCount;i++) { | |||||
| double val = T.Dist(api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int StudentSearchBestGrass(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| /*api.Print(fmt::format("Grid{},{}", Gridx, Gridy)); | |||||
| api.Print(fmt::format("Tri{},{}", Trickers.x, Trickers.y));*/ | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double val = T.StuDistValSet(T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy)); | |||||
| /*api.Print(fmt::format("Grass{},{}:val{}", Grass[i].x, Grass[i].y, val)); | |||||
| api.Print(fmt::format("Dist(stu,Grass)={}", T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy))); | |||||
| api.Print(fmt::format("Dist(tri,Grass)={}", T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y))));*/ | |||||
| if(val < valmin && !Grass[i].occupied) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int TrickerSearchPossibleGrass(IAPI& api, int DistMax) { | |||||
| double distmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double dist = T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy); | |||||
| if(dist < distmin) { | |||||
| distmin = dist; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| void FindPathTo(std::pair<int, int> Target, bool CanReach, bool ForceToFind = false) { | |||||
| if((PathToGo.empty() || (!PathToGo.empty() && LastTarget != Target)) || ForceToFind) { | |||||
| LastTarget = Target; | |||||
| AStar.init(); | |||||
| PathToGo = AStar.GetPath({Cellx,Celly}, {Target.first,Target.second}, CanReach); | |||||
| } | |||||
| } | |||||
| inline void StudentStart(IAPI& api) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| inline void TrickerStart(IAPI& api) { | |||||
| CurrentPatrolNodeToGo = 0; | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| inline void Start(IAPI& api) { | |||||
| isFirstFrame = false; | |||||
| Map = api.GetFullMap(); | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(Map[i][j] == THUAI6::PlaceType::Window) | |||||
| map[i][j] = 1; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Wall || Map[i][j] == THUAI6::PlaceType::Gate | |||||
| || Map[i][j] == THUAI6::PlaceType::Chest || Map[i][j] == THUAI6::PlaceType::ClassRoom | |||||
| || Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| map[i][j] = 2; | |||||
| if(Map[i][j] == THUAI6::PlaceType::ClassRoom) | |||||
| Classroom[ClassroomCount].x = i, Classroom[ClassroomCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Chest) | |||||
| Chest[ChestCount].x = i, Chest[ChestCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Gate) | |||||
| Gate[GateCount].x = i, Gate[GateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| HiddenGate[HiddenGateCount].x = i, HiddenGate[HiddenGateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Grass) { | |||||
| Grass[GrassCount].x = i; | |||||
| Grass[GrassCount++].y = j; | |||||
| //api.Print(fmt::format("Grass[{}]:{},{}", GrassCount - 1, Grass[GrassCount - 1].x, Grass[GrassCount - 1].y)); | |||||
| } | |||||
| } | |||||
| if(Gate[0].y > 25) { | |||||
| PatrolNode = {{18,18},{31,31},{42,31},{42,18},{31,7},{31,18},{18,31},{18,42},{7,31},{7,18}}; | |||||
| map2 = true; | |||||
| } else { | |||||
| PatrolNode = {{23,13},{18,7},{8,9},{8,33},{14,36},{23,29},{31,35},{42,41},{40,24}}; | |||||
| map1 = true; | |||||
| } | |||||
| if(PlayerId <= 3) { | |||||
| StudentStart(api); | |||||
| } else | |||||
| TrickerStart(api); | |||||
| } | |||||
| void UpdateStudentInfo(IStudentAPI& api) { | |||||
| msg = "*0*0"; | |||||
| if(studentinfo->x == Gridx && studentinfo->y == Gridy && studentinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| switch(studentType[PlayerId]) { | |||||
| case THUAI6::StudentType::StraightAStudent: | |||||
| if((int)studentinfo->playerState >= 1 && (int)studentinfo->playerState <= 10 && (int)studentinfo->playerState != 2) | |||||
| MeditationProgress += 40 * deltaTimems; | |||||
| else | |||||
| MeditationProgress = 0; | |||||
| break; | |||||
| } | |||||
| Gridx = studentinfo->x; | |||||
| Gridy = studentinfo->y; | |||||
| Cellx = api.GridToCell(studentinfo->x); | |||||
| Celly = api.GridToCell(studentinfo->y); | |||||
| for(int i = 0;i < 4;i++) | |||||
| Students[i] = *(api.GetStudents()[i]); | |||||
| auto TrickersInSight = api.GetTrickers(); | |||||
| TrickerisInSight = false; | |||||
| CanGetTrickerInfo = false; | |||||
| for(int i= LastTrickerNum - 1;i >= 1;i--) | |||||
| LastTrickers[i] = LastTrickers[i-1]; | |||||
| LastTrickers[0] = Trickers; | |||||
| if(!TrickersInSight.empty()) { | |||||
| //api.Print("TrickerisInSight!"); | |||||
| TrickerisInSight = true; | |||||
| CanGetTrickerInfo = true; | |||||
| Trickers = *TrickersInSight[0]; | |||||
| } else { | |||||
| //api.Print("TrickerisnotInSight!"); | |||||
| //if(Trickers.x != -1) | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(Trickers.x != -1 && api.HaveView(api.GridToCell(Trickers.x), api.GridToCell(Trickers.y))) { | |||||
| Trickers.x = -1; | |||||
| //api.Print("TrickerisnotInSightremove!"); | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("TrickerStuckTime {}", TrickerStuckTime)); | |||||
| } | |||||
| void UpdateTrickerInfo(ITrickerAPI& api) { | |||||
| if(trickerinfo->x == Gridx && trickerinfo->y == Gridy && trickerinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| Gridx = trickerinfo->x; | |||||
| Gridy = trickerinfo->y; | |||||
| Cellx = api.GridToCell(trickerinfo->x); | |||||
| Celly = api.GridToCell(trickerinfo->y); | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(int i = 0;i < 4;i++) | |||||
| StudentisInSight[i] = false; | |||||
| for(int i = 0;i < StudentsInSight.size();i++) { | |||||
| StudentisInSight[StudentsInSight[i]->playerID] = true; | |||||
| Students[StudentsInSight[i]->playerID] = *StudentsInSight[i]; | |||||
| } | |||||
| } | |||||
| void UpdateMapInfo(IAPI& api) { | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(map[i][j] == 3) | |||||
| map[i][j] = 0; | |||||
| if(map[i][j] % 5 == 3) | |||||
| map[i][j] -= 5; | |||||
| } | |||||
| } | |||||
| void MoveAlongPath(IAPI& api) { | |||||
| if(!PathToGo.empty() && ((!PathGone.empty() && PathToGo.front() != PathGone.front()) || PathGone.empty())) { | |||||
| bool Reach = false; | |||||
| if(PathToGo.size() == 1) { | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) < 100) { | |||||
| Reach = true; | |||||
| } | |||||
| } else { | |||||
| auto it = PathToGo.begin();it++; | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) | |||||
| + T.Dist(Gridx, Gridy, api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) < | |||||
| T.Dist(api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y), | |||||
| api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) * 1.15) { | |||||
| Reach = true; | |||||
| } | |||||
| } | |||||
| if(Reach) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| if(!PathToGo.empty()) { | |||||
| if(Map[PathToGo.front()->x][PathToGo.front()->y] == THUAI6::PlaceType::Window) { | |||||
| if(api.SkipWindow().get()) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| } else { | |||||
| faceDir = atan2(api.CellToGrid(PathToGo.front()->y) - Gridy, api.CellToGrid(PathToGo.front()->x) - Gridx); | |||||
| api.Move(500, faceDir); | |||||
| } | |||||
| } else { | |||||
| if(PlayerId <= 3) { | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::StraightAStudent && studentinfo->timeUntilSkillAvailable[0] == 0) | |||||
| api.UseSkill(0); | |||||
| studentstate = StudentState::Learning; | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| api.StartOpenGate(); | |||||
| studentstate = StudentState::WaitToGraduate; | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| break; | |||||
| } | |||||
| } else { | |||||
| int GrassId; | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| } else { | |||||
| trickerstate = TrickerState::MovingToSearch; | |||||
| GrassId = TrickerSearchPossibleGrass(api, 2000); | |||||
| if(GrassId != -1) { | |||||
| FindPathTo({Grass[GrassId].x,Grass[GrassId].y}, true); | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| bool OnlyTeacherLeft(ITrickerAPI& api) { | |||||
| if(framecount > 6000) | |||||
| return true; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| if(Students[i].studentType != THUAI6::StudentType::Teacher && Students[i].determination > 0) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| int FindStudentInSight(ITrickerAPI& api) { | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(auto i = 0;i < StudentsInSight.size();i++) { | |||||
| if(OnlyTeacherLeft(api)) { | |||||
| if(Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } else { | |||||
| if(Students[StudentsInSight[i]->playerID].studentType != THUAI6::StudentType::Teacher && Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| void ReadMessage(IStudentAPI& api) { | |||||
| for(int i = 0;i < 10;i++) | |||||
| Classroom[i].occupied = 4; | |||||
| if(map1) | |||||
| Classroom[5].occupied = 5; | |||||
| ShouldChangeClassroomToGo = false; | |||||
| while(api.HaveMessage()) { | |||||
| msgrcv = api.GetMessage(); | |||||
| Classroom[msgrcv.second[0] - '0'].occupied = msgrcv.first; | |||||
| if(msgrcv.second[0] - '0' == CurrentClassroomToGo) ShouldChangeClassroomToGo = true; | |||||
| if(msgrcv.second[1] == '1') Classroom[msgrcv.second[2] - '0'].finished = true, ClassroomFinished++; | |||||
| if(msgrcv.second[3] == '1' && studentstate != StudentState::MovingToRescue) { | |||||
| int dist = 1000000, id = 0; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| //api.Print(fmt::format("stu[{}].x = {} stu[{}].y = {}", i, Students[i].x, i, Students[i].y)); | |||||
| if(msgrcv.first != i) { | |||||
| if(dist > T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y)) { | |||||
| dist = T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y); | |||||
| id = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("id{},dist{}", id, dist)); | |||||
| if(id == PlayerId) { | |||||
| studentstate = StudentState::MovingToRescue; | |||||
| CurrentStudentToRescue = msgrcv.first; | |||||
| } | |||||
| } | |||||
| if(msgrcv.second[4] != '*' && !TrickerisInSight) { | |||||
| Trickers.x = T.ToInt(msgrcv.second.substr(4, 5)); | |||||
| Trickers.y = T.ToInt(msgrcv.second.substr(9, 5)); | |||||
| CanGetTrickerInfo = true; | |||||
| } | |||||
| } | |||||
| if(ShouldChangeClassroomToGo) { | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| if(studentstate == StudentState::MovingForClassroom) | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| } | |||||
| void SendMessage(IStudentAPI& api) { | |||||
| if(studentstate == StudentState::MovingForClassroom || studentstate == StudentState::Learning) | |||||
| msg[0] = CurrentClassroomToGo + '0'; | |||||
| if(studentinfo->playerState == THUAI6::PlayerState::Addicted && | |||||
| !((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) | |||||
| msg[3] = '1'; | |||||
| if(TrickerisInSight) | |||||
| msg = msg + T.ToString(Trickers.x) + T.ToString(Trickers.y); | |||||
| else | |||||
| msg = msg + "**********"; | |||||
| for(int i = 0;i < 4;i++) | |||||
| if(PlayerId != i) | |||||
| api.SendTextMessage(i, msg); | |||||
| } | |||||
| bool CheckTrickerStuck(){ | |||||
| int avex = 0,avey = 0; | |||||
| for(int i = 0;i < LastTrickerNum;i++){ | |||||
| avex += LastTrickers[i].x; | |||||
| avey += LastTrickers[i].y; | |||||
| } | |||||
| avex /= LastTrickerNum; | |||||
| avey /= LastTrickerNum; | |||||
| return (T.Dist(avex,avey,Trickers.x,Trickers.y) < 350); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) { | |||||
| studentinfo = api.GetSelfInfo(); | |||||
| //api.Print(fmt::format("studentinfo->dangerAlert {}", studentinfo->dangerAlert)); | |||||
| UpdateMapInfo(api); | |||||
| UpdateStudentInfo(api); | |||||
| if(studentstate != StudentState::Learning && !rousing) { | |||||
| api.EndAllAction(); | |||||
| //api.Print("endallaction"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| } | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| ReadMessage(api); | |||||
| if(CheckTrickerStuck() && CanGetTrickerInfo) { | |||||
| TrickerStuckTime += 50; | |||||
| } else | |||||
| TrickerStuckTime = 0; | |||||
| if(studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && abs(T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) - 15000 / studentinfo->dangerAlert) > 100) | |||||
| Trickers.x = -1; | |||||
| if(!CanGetTrickerInfo) | |||||
| Trickers.x = -1; | |||||
| //api.Print(fmt::format("Trickers{},{}", Trickers.x, Trickers.y)); | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(StuckTime >= 200) { | |||||
| if(PathToGo.size() >= 2) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| StuckTime = 0; | |||||
| } | |||||
| if(studentstate != StudentState::MovingForGrass) { | |||||
| if((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) { | |||||
| rousing = false; | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| //api.Print(fmt::format("grassfind{},{}", Grass[TargetGrass].x, Grass[TargetGrass].y)); | |||||
| } | |||||
| auto bullets = api.GetBullets(); | |||||
| bool FlyingKnifeInSight = false; | |||||
| for(auto i = 0;i < bullets.size();i++) | |||||
| if(bullets[i]->bulletType == THUAI6::BulletType::FlyingKnife){ | |||||
| FlyingKnifeInSight = true; | |||||
| //api.Print(fmt::format("FlyingKnifeInSight!!!")); | |||||
| } | |||||
| if(FlyingKnifeInSight){ | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| } | |||||
| } | |||||
| if(ClassroomFinished >= 7 && studentstate != StudentState::WaitToGraduate && studentstate != StudentState::MovingToGraduate && studentstate != StudentState::MovingForGrass) { | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| } | |||||
| //api.Print(fmt::format("Courage{}", Courage)); | |||||
| //api.Print(fmt::format("EscapeDist / Courage{}", EscapeDist / Courage)); | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01 && studentinfo->determination < 3200000) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| //api.Print(fmt::format("state:MovingForClassroom")); | |||||
| //api.Print(fmt::format("classroom{}:{},{}", CurrentClassroomToGo, Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y)); | |||||
| //CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| case StudentState::Learning: | |||||
| //api.Print(fmt::format("state:Learning")); | |||||
| //api.Print(fmt::format("classroom[{}] process = {}", CurrentClassroomToGo, api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y))); | |||||
| if(api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y) == 10000000) { | |||||
| //api.Print("ClassroomFinished"); | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| Classroom[CurrentClassroomToGo].finished = true; | |||||
| ClassroomFinished++; | |||||
| msg[1] = '1'; | |||||
| msg[2] = (char)(CurrentClassroomToGo + '0'); | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } else | |||||
| api.StartLearning(); | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| Courage += 0.002; | |||||
| //api.Print(fmt::format("state:MovingForGrass")); | |||||
| if(!((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage)))) { | |||||
| WaitTrickerTime += 50; | |||||
| if(Trickers.trickerType == THUAI6::TrickerType::ANoisyPerson){ | |||||
| if(WaitTrickerTime >= 10000) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| }else{ | |||||
| if(WaitTrickerTime >= 2500) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[2] < 0.01) { | |||||
| api.UseSkill(2); | |||||
| } | |||||
| if(PathToGo.empty()) { | |||||
| //api.Print("gototricker"); | |||||
| if(Map[Cellx - 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx - 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx - 1, Celly}, true); | |||||
| if(Map[Cellx + 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx + 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx + 1, Celly}, true); | |||||
| if(Map[Cellx][Celly - 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly - 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly - 1}, true); | |||||
| if(Map[Cellx][Celly + 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly + 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly + 1}, true); | |||||
| } | |||||
| /*int TargetGrass; | |||||
| TargetGrass = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[TargetGrass].x, Grass[TargetGrass].y}, true);*/ | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToRescue: | |||||
| //api.Print(fmt::format("state:MovingToRescue")); | |||||
| //api.Print(fmt::format("rescuestudent{}:{},{}", CurrentStudentToRescue, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| rousing = false; | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToRescue].x),api.GridToCell(Students[CurrentStudentToRescue].y)}, false); | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y) < 1200) { | |||||
| api.StartRouseMate(CurrentStudentToRescue); | |||||
| PathToGo.clear(); | |||||
| rousing = true; | |||||
| //api.Print("rousing!"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| //api.Print("rouseover!"); | |||||
| rousing = false; | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| api.StartOpenGate(); | |||||
| if(api.GetGateProgress(Gate[CurrentGateToGo].x, Gate[CurrentGateToGo].y) == 18000) { | |||||
| api.Graduate(); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| if(TrickerStuckTime >= 200) { | |||||
| Courage += 0.003; | |||||
| }else{ | |||||
| if(CanGetTrickerInfo) | |||||
| Courage = 1; | |||||
| } | |||||
| if(Courage > 1) | |||||
| Courage -= 0.0015; | |||||
| if(Courage > 2.3) | |||||
| Courage = 2.3; | |||||
| if(Courage < 1) | |||||
| Courage = 1; | |||||
| //api.Print(fmt::format("Courage = {}", Courage)); | |||||
| //api.Print(fmt::format("CriticalDist = {}", EscapeDist / Courage)); | |||||
| //api.Print(fmt::format("BGM = {}", studentinfo->dangerAlert)); | |||||
| //api.Print(fmt::format("BGM Dist = {}", 15000 / studentinfo->dangerAlert)); | |||||
| //for(auto i = PathToGo.begin();i != PathToGo.end();i++) | |||||
| // api.Print(fmt::format("{} {}", (*i)->x, (*i)->y)); | |||||
| MoveAlongPath(api); | |||||
| SendMessage(api); | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) { | |||||
| framecount++; | |||||
| if(trickerType == THUAI6::TrickerType::ANoisyPerson) { | |||||
| api.UseSkill(0); | |||||
| goto end; | |||||
| } | |||||
| api.EndAllAction(); | |||||
| trickerinfo = api.GetSelfInfo(); | |||||
| UpdateMapInfo(api); | |||||
| UpdateTrickerInfo(api); | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| int StudentsInSightId; | |||||
| if(StuckTime >= 200) { | |||||
| if(StuckTime >= 1000) { | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } else { | |||||
| if(trickerstate == TrickerState::MovingToSearch || trickerstate == TrickerState::MovingToPursue) { | |||||
| if(api.Attack(faceDir).get()) | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| } else { | |||||
| if(!PathToGo.empty()) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| StudentsInSightId = FindStudentInSight(api); | |||||
| if(StudentsInSightId != -1) { | |||||
| trickerstate = TrickerState::MovingToPursue; | |||||
| CurrentStudentToPursue = StudentsInSightId; | |||||
| } else { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].playerState == THUAI6::PlayerState::Addicted) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToPursue].x), api.GridToCell(Students[CurrentStudentToPursue].y)}, false); | |||||
| if(StudentisInSight[CurrentStudentToPursue]) { | |||||
| if(trickerinfo->timeUntilSkillAvailable[1] == 0) { | |||||
| if(trickerType == THUAI6::TrickerType::Klee && T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.UseSkill(0); | |||||
| } | |||||
| if(api.HaveView(Students[CurrentStudentToPursue].y + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)) | |||||
| if(trickerType == THUAI6::TrickerType::Assassin) | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(trickerinfo->bulletType) { | |||||
| case THUAI6::BulletType::CommonAttackOfTricker: | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| } | |||||
| break; | |||||
| case THUAI6::BulletType::FlyingKnife: | |||||
| api.Attack(atan2( | |||||
| Students[CurrentStudentToPursue].y - Gridy + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x - Gridx + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| case THUAI6::BulletType::BombBomb: | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| break; | |||||
| } | |||||
| MoveAlongPath(api); | |||||
| end:; | |||||
| } | |||||
| @@ -0,0 +1,977 @@ | |||||
| #include <vector> | |||||
| #include <list> | |||||
| #include <math.h> | |||||
| #include <string> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| constexpr int deltaTimems = 50; | |||||
| constexpr int LastTrickerNum = 10; | |||||
| #define deltaTime 0.05 | |||||
| #define pi 3.1415926 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Klee; | |||||
| /* | |||||
| * 全局常量: | |||||
| * | |||||
| * 全局变量: | |||||
| * | |||||
| * Debug相关 | |||||
| * 寻路相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * 学生变量: | |||||
| * 通信相关 | |||||
| * 状态相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * StraightAStudent变量: | |||||
| * 冥想相关 | |||||
| * Athlete变量: | |||||
| * | |||||
| * Teacher变量: | |||||
| * | |||||
| * Sunshine变量: | |||||
| * | |||||
| * | |||||
| * 捣蛋鬼变量: | |||||
| * | |||||
| * | |||||
| * Assas变量: | |||||
| * | |||||
| * 工具类 | |||||
| * A*类 | |||||
| * | |||||
| */ | |||||
| struct Node { | |||||
| int x, y; | |||||
| int F, G, H; | |||||
| Node* parent; | |||||
| Node(int _x, int _y):x(_x), y(_y), F(0), G(0), H(0), parent(NULL) {} | |||||
| }*node[50][50]; | |||||
| const std::list<Node*> emptylist; | |||||
| const int EscapeDist = 16000; | |||||
| std::list<Node*> PathToGo, PathGone; | |||||
| bool isFirstFrame = true; | |||||
| int PlayerId; | |||||
| int Gridx, Gridy; | |||||
| int Cellx, Celly; | |||||
| std::vector<std::vector<THUAI6::PlaceType>> Map; | |||||
| int map[50][50];//0为无阻碍,1为半阻碍,2为完全阻碍, 3为临时避障 | |||||
| struct ClassRoomInfo { | |||||
| int x, y; | |||||
| bool finished; | |||||
| int occupied;//为4说明无学生占用,为0-3说明相应编号学生正在占用 | |||||
| }Classroom[100]; | |||||
| int ClassroomCount, ClassroomFinished; | |||||
| struct ChestInfo { | |||||
| int x, y; | |||||
| bool opened; | |||||
| int occupied; | |||||
| }Chest[80]; | |||||
| int ChestCount; | |||||
| struct GateInfo { | |||||
| int x, y; | |||||
| bool opening; | |||||
| bool open; | |||||
| }Gate[20]; | |||||
| int GateCount; | |||||
| struct HiddenGateInfo { | |||||
| int x, y; | |||||
| bool refreshed; | |||||
| bool open; | |||||
| }HiddenGate[30]; | |||||
| int HiddenGateCount; | |||||
| struct GrassInfo { | |||||
| int x, y, occupied; | |||||
| }Grass[200]; | |||||
| int GrassCount; | |||||
| double faceDir = 0; | |||||
| bool map1 = false, map2 = false; | |||||
| std::pair<int, int> LastTarget; | |||||
| std::pair<int, std::string> msgrcv; | |||||
| std::string msg;//信息结构:'准备前往*号教室''1/0完成了作业''*号教室作业已完成''需要被营救'"捣蛋鬼x坐标""捣蛋鬼y坐标" | |||||
| std::shared_ptr<const THUAI6::Student> studentinfo; | |||||
| THUAI6::Tricker Trickers,LastTrickers[LastTrickerNum]; | |||||
| bool TrickerisInSight; | |||||
| bool CanGetTrickerInfo; | |||||
| enum class StudentState { | |||||
| MovingForClassroom, Learning, MovingForGrass, MovingToAttck, MovingForProp, MovingToRescue, MovingToGraduate, WaitToGraduate | |||||
| }studentstate; | |||||
| int CurrentClassroomToGo; | |||||
| int CurrentGateToGo; | |||||
| int CurrentStudentToRescue; | |||||
| int CurrentGrassToGo; | |||||
| bool ShouldChangeClassroomToGo; | |||||
| float Courage = 1.0; | |||||
| int TrickerStuckTime; | |||||
| bool rousing = false; | |||||
| int framecount; | |||||
| int MeditationProgress; | |||||
| double ChargeTimeLeft; | |||||
| std::shared_ptr<const THUAI6::Tricker> trickerinfo; | |||||
| THUAI6::Student Students[4]; | |||||
| bool StudentisInSight[4]; | |||||
| enum class TrickerState { | |||||
| MovingToPatrol, MovingToPursue, MovingToSearch, MovingForProp | |||||
| }trickerstate; | |||||
| struct _PatrolNode { | |||||
| int x, y; | |||||
| }; | |||||
| std::vector<_PatrolNode> PatrolNode; | |||||
| int CurrentPatrolNodeToGo; | |||||
| int CurrentStudentToPursue; | |||||
| int StuckTime; | |||||
| int WaitTrickerTime; | |||||
| bool bgmuseful; | |||||
| class Tools { | |||||
| public: | |||||
| double max(double a, double b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| double min(double a, double b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| double Dist(double x1, double y1, double x2, double y2) { | |||||
| return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); | |||||
| } | |||||
| int ToInt(std::string s) { | |||||
| int x = 0; | |||||
| for(int i = 0;i < 5;i++, x *= 10) | |||||
| x += s[i] - '0'; | |||||
| return x / 10; | |||||
| } | |||||
| std::string ToString(int x) { | |||||
| std::string s = "*****"; | |||||
| for(int i = 0;i < 5;i++, x /= 10) | |||||
| s[4 - i] = x % 10 + '0'; | |||||
| return s; | |||||
| } | |||||
| double StuDistValSet(double Dist) { | |||||
| if(Dist < 7500) | |||||
| return Dist; | |||||
| return (Dist - 7500) * 2 + 7500; | |||||
| } | |||||
| double TriDistValSet(double Dist) { | |||||
| int dist1 = 8500, dist2 = 10500; | |||||
| if(Dist < dist1) | |||||
| return Dist * 6; | |||||
| if(Dist >= dist1 && Dist <= dist2) | |||||
| return (Dist - dist1) * 3 + dist1 * 6; | |||||
| return (Dist - dist2) + dist1 * 4 + (dist2 - dist1) * 2; | |||||
| } | |||||
| }T; | |||||
| class A_Star { | |||||
| public: | |||||
| void init() { | |||||
| openlist.clear(); | |||||
| closelist.clear(); | |||||
| } | |||||
| std::list<Node*> GetPath(std::vector<int> st, std::vector<int> ed, bool Reach) { | |||||
| if(st == ed) { | |||||
| std::list<Node*> simplelist; | |||||
| simplelist.push_back(new Node(st[0], st[1])); | |||||
| return simplelist; | |||||
| } | |||||
| openlist.push_back(new Node(st[0], st[1])); | |||||
| while(!openlist.empty()) { | |||||
| Node* CurrentUpdateNode = GetLeastF(); | |||||
| closelist.push_back(CurrentUpdateNode); | |||||
| UpdateFValue(CurrentUpdateNode, ed); | |||||
| Node* CurPathNode; | |||||
| if(!Reach) { | |||||
| CurPathNode = GetDestSurroundNode(ed); | |||||
| if(CurPathNode) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } else { | |||||
| CurPathNode = isInList(openlist, ed[0], ed[1]); | |||||
| if(CurPathNode != NULL) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } | |||||
| openlist.remove(CurrentUpdateNode); | |||||
| } | |||||
| return emptylist; | |||||
| } | |||||
| private: | |||||
| void UpdateFValue(Node* CurNode, std::vector<int> ed) { | |||||
| std::list<std::vector<int>> UpdateList = GetSurroundNode(CurNode); | |||||
| for(auto Curnum = UpdateList.begin();Curnum != UpdateList.end();Curnum++) { | |||||
| int i = (*Curnum)[0], j = (*Curnum)[1]; | |||||
| if(isInList(openlist, i, j) == NULL) { | |||||
| Node* UpdateNode = new Node(i, j); | |||||
| UpdateNode->parent = CurNode; | |||||
| if(map[i][j] == 1) { | |||||
| if(PlayerId <= 3 && studentType[PlayerId] == THUAI6::StudentType::StraightAStudent) { | |||||
| if(MeditationProgress + CurNode->G / 28.8 * 40000 > 300000) | |||||
| UpdateNode->G = CurNode->G + 1; | |||||
| else | |||||
| UpdateNode->G = CurNode->G + 100; | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + 1 * (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->H = (abs(ed[0] - i) + abs(ed[1] - j)) * 10; | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| openlist.push_back(UpdateNode); | |||||
| } else { | |||||
| Node* UpdateNode = isInList(openlist, i, j); | |||||
| int newG = (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| if(CurNode->G + newG <= UpdateNode->G) { | |||||
| UpdateNode->parent = CurNode; | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| std::list<std::vector<int>> GetSurroundNode(Node* CurNode) { | |||||
| std::list<std::vector<int>> SurroundNodeList; | |||||
| bool up = false, down = false, left = false, right = false; | |||||
| if(CurNode->x > 0 && map[CurNode->x - 1][CurNode->y] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y}), up = true; | |||||
| if(CurNode->x < 49 && map[CurNode->x + 1][CurNode->y] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y}), down = true; | |||||
| if(CurNode->y > 0 && map[CurNode->x][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y - 1}), left = true; | |||||
| if(CurNode->y < 49 && map[CurNode->x][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y + 1}), right = true; | |||||
| if(up && left && map[CurNode->x - 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y - 1}); | |||||
| if(up && right && map[CurNode->x - 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y + 1}); | |||||
| if(down && left && map[CurNode->x + 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y - 1}); | |||||
| if(down && right && map[CurNode->x + 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y + 1}); | |||||
| return SurroundNodeList; | |||||
| } | |||||
| Node* isInList(std::list<Node*>& List, int x, int y) { | |||||
| for(auto i = List.begin();i != List.end();i++) | |||||
| if((*i)->x == x && (*i)->y == y) | |||||
| return (*i); | |||||
| return NULL; | |||||
| } | |||||
| Node* GetDestSurroundNode(std::vector<int> ed) { | |||||
| Node* retNode; | |||||
| retNode = isInList(openlist, ed[0] - 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0] + 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] - 1); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] + 1); | |||||
| if(retNode) return retNode; | |||||
| return NULL; | |||||
| } | |||||
| Node* GetLeastF() { | |||||
| if(!openlist.empty()) { | |||||
| Node* resPoint = openlist.front(); | |||||
| for(auto i = openlist.begin();i != openlist.end();i++) | |||||
| if(resPoint->F > (*i)->F) | |||||
| resPoint = *i; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| private: | |||||
| std::list<Node*> openlist; | |||||
| std::list<Node*> closelist; | |||||
| }AStar; | |||||
| int SearchBestClassroom(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < ClassroomCount;i++) { | |||||
| if(!Classroom[i].finished && Classroom[i].occupied == 4) { | |||||
| double val = T.Dist(api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int SearchBestGate(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GateCount;i++) { | |||||
| double val = T.Dist(api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int StudentSearchBestGrass(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| /*api.Print(fmt::format("Grid{},{}", Gridx, Gridy)); | |||||
| api.Print(fmt::format("Tri{},{}", Trickers.x, Trickers.y));*/ | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double val = T.StuDistValSet(T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy)); | |||||
| /*api.Print(fmt::format("Grass{},{}:val{}", Grass[i].x, Grass[i].y, val)); | |||||
| api.Print(fmt::format("Dist(stu,Grass)={}", T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy))); | |||||
| api.Print(fmt::format("Dist(tri,Grass)={}", T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y))));*/ | |||||
| if(val < valmin && !Grass[i].occupied) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int TrickerSearchPossibleGrass(IAPI& api, int DistMax) { | |||||
| double distmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double dist = T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy); | |||||
| if(dist < distmin) { | |||||
| distmin = dist; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| void FindPathTo(std::pair<int, int> Target, bool CanReach, bool ForceToFind = false) { | |||||
| if((PathToGo.empty() || (!PathToGo.empty() && LastTarget != Target)) || ForceToFind) { | |||||
| LastTarget = Target; | |||||
| AStar.init(); | |||||
| PathToGo = AStar.GetPath({Cellx,Celly}, {Target.first,Target.second}, CanReach); | |||||
| } | |||||
| } | |||||
| inline void StudentStart(IAPI& api) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| inline void TrickerStart(IAPI& api) { | |||||
| CurrentPatrolNodeToGo = 0; | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| inline void Start(IAPI& api) { | |||||
| isFirstFrame = false; | |||||
| Map = api.GetFullMap(); | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(Map[i][j] == THUAI6::PlaceType::Window) | |||||
| map[i][j] = 1; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Wall || Map[i][j] == THUAI6::PlaceType::Gate | |||||
| || Map[i][j] == THUAI6::PlaceType::Chest || Map[i][j] == THUAI6::PlaceType::ClassRoom | |||||
| || Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| map[i][j] = 2; | |||||
| if(Map[i][j] == THUAI6::PlaceType::ClassRoom) | |||||
| Classroom[ClassroomCount].x = i, Classroom[ClassroomCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Chest) | |||||
| Chest[ChestCount].x = i, Chest[ChestCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Gate) | |||||
| Gate[GateCount].x = i, Gate[GateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| HiddenGate[HiddenGateCount].x = i, HiddenGate[HiddenGateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Grass) { | |||||
| Grass[GrassCount].x = i; | |||||
| Grass[GrassCount++].y = j; | |||||
| //api.Print(fmt::format("Grass[{}]:{},{}", GrassCount - 1, Grass[GrassCount - 1].x, Grass[GrassCount - 1].y)); | |||||
| } | |||||
| } | |||||
| if(Gate[0].y > 25) { | |||||
| PatrolNode = {{18,18},{31,31},{42,31},{42,18},{31,7},{31,18},{18,31},{18,42},{7,31},{7,18}}; | |||||
| map2 = true; | |||||
| } else { | |||||
| PatrolNode = {{23,13},{18,7},{8,9},{8,33},{14,36},{23,29},{31,35},{42,41},{40,24}}; | |||||
| map1 = true; | |||||
| } | |||||
| if(PlayerId <= 3) { | |||||
| StudentStart(api); | |||||
| } else | |||||
| TrickerStart(api); | |||||
| } | |||||
| void UpdateStudentInfo(IStudentAPI& api) { | |||||
| msg = "*0*0"; | |||||
| if(studentinfo->x == Gridx && studentinfo->y == Gridy && studentinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| switch(studentType[PlayerId]) { | |||||
| case THUAI6::StudentType::StraightAStudent: | |||||
| if((int)studentinfo->playerState >= 1 && (int)studentinfo->playerState <= 10 && (int)studentinfo->playerState != 2) | |||||
| MeditationProgress += 40 * deltaTimems; | |||||
| else | |||||
| MeditationProgress = 0; | |||||
| break; | |||||
| } | |||||
| Gridx = studentinfo->x; | |||||
| Gridy = studentinfo->y; | |||||
| Cellx = api.GridToCell(studentinfo->x); | |||||
| Celly = api.GridToCell(studentinfo->y); | |||||
| for(int i = 0;i < 4;i++) | |||||
| Students[i] = *(api.GetStudents()[i]); | |||||
| auto TrickersInSight = api.GetTrickers(); | |||||
| TrickerisInSight = false; | |||||
| CanGetTrickerInfo = false; | |||||
| for(int i= LastTrickerNum - 1;i >= 1;i--) | |||||
| LastTrickers[i] = LastTrickers[i-1]; | |||||
| LastTrickers[0] = Trickers; | |||||
| if(!TrickersInSight.empty()) { | |||||
| //api.Print("TrickerisInSight!"); | |||||
| TrickerisInSight = true; | |||||
| CanGetTrickerInfo = true; | |||||
| Trickers = *TrickersInSight[0]; | |||||
| } else { | |||||
| //api.Print("TrickerisnotInSight!"); | |||||
| //if(Trickers.x != -1) | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(Trickers.x != -1 && api.HaveView(api.GridToCell(Trickers.x), api.GridToCell(Trickers.y))) { | |||||
| Trickers.x = -1; | |||||
| //api.Print("TrickerisnotInSightremove!"); | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("TrickerStuckTime {}", TrickerStuckTime)); | |||||
| } | |||||
| void UpdateTrickerInfo(ITrickerAPI& api) { | |||||
| if(trickerinfo->x == Gridx && trickerinfo->y == Gridy && trickerinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| Gridx = trickerinfo->x; | |||||
| Gridy = trickerinfo->y; | |||||
| Cellx = api.GridToCell(trickerinfo->x); | |||||
| Celly = api.GridToCell(trickerinfo->y); | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(int i = 0;i < 4;i++) | |||||
| StudentisInSight[i] = false; | |||||
| for(int i = 0;i < StudentsInSight.size();i++) { | |||||
| StudentisInSight[StudentsInSight[i]->playerID] = true; | |||||
| Students[StudentsInSight[i]->playerID] = *StudentsInSight[i]; | |||||
| } | |||||
| } | |||||
| void UpdateMapInfo(IAPI& api) { | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(map[i][j] == 3) | |||||
| map[i][j] = 0; | |||||
| if(map[i][j] % 5 == 3) | |||||
| map[i][j] -= 5; | |||||
| } | |||||
| } | |||||
| void MoveAlongPath(IAPI& api) { | |||||
| if(!PathToGo.empty() && ((!PathGone.empty() && PathToGo.front() != PathGone.front()) || PathGone.empty())) { | |||||
| bool Reach = false; | |||||
| if(PathToGo.size() == 1) { | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) < 100) { | |||||
| Reach = true; | |||||
| } | |||||
| } else { | |||||
| auto it = PathToGo.begin();it++; | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) | |||||
| + T.Dist(Gridx, Gridy, api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) < | |||||
| T.Dist(api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y), | |||||
| api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) * 1.15) { | |||||
| Reach = true; | |||||
| } | |||||
| } | |||||
| if(Reach) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| if(!PathToGo.empty()) { | |||||
| if(Map[PathToGo.front()->x][PathToGo.front()->y] == THUAI6::PlaceType::Window) { | |||||
| if(api.SkipWindow().get()) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| } else { | |||||
| faceDir = atan2(api.CellToGrid(PathToGo.front()->y) - Gridy, api.CellToGrid(PathToGo.front()->x) - Gridx); | |||||
| api.Move(500, faceDir); | |||||
| } | |||||
| } else { | |||||
| if(PlayerId <= 3) { | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::StraightAStudent && studentinfo->timeUntilSkillAvailable[0] == 0) | |||||
| api.UseSkill(0); | |||||
| studentstate = StudentState::Learning; | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| api.StartOpenGate(); | |||||
| studentstate = StudentState::WaitToGraduate; | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| break; | |||||
| } | |||||
| } else { | |||||
| int GrassId; | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| } else { | |||||
| trickerstate = TrickerState::MovingToSearch; | |||||
| GrassId = TrickerSearchPossibleGrass(api, 2000); | |||||
| if(GrassId != -1) { | |||||
| FindPathTo({Grass[GrassId].x,Grass[GrassId].y}, true); | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| bool OnlyTeacherLeft(ITrickerAPI& api) { | |||||
| if(framecount > 6000) | |||||
| return true; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| if(Students[i].studentType != THUAI6::StudentType::Teacher && Students[i].determination > 0) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| int FindStudentInSight(ITrickerAPI& api) { | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(auto i = 0;i < StudentsInSight.size();i++) { | |||||
| if(OnlyTeacherLeft(api)) { | |||||
| if(Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } else { | |||||
| if(Students[StudentsInSight[i]->playerID].studentType != THUAI6::StudentType::Teacher && Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| void ReadMessage(IStudentAPI& api) { | |||||
| for(int i = 0;i < 10;i++) | |||||
| Classroom[i].occupied = 4; | |||||
| if(map1) | |||||
| Classroom[5].occupied = 5; | |||||
| ShouldChangeClassroomToGo = false; | |||||
| while(api.HaveMessage()) { | |||||
| msgrcv = api.GetMessage(); | |||||
| Classroom[msgrcv.second[0] - '0'].occupied = msgrcv.first; | |||||
| if(msgrcv.second[0] - '0' == CurrentClassroomToGo) ShouldChangeClassroomToGo = true; | |||||
| if(msgrcv.second[1] == '1') Classroom[msgrcv.second[2] - '0'].finished = true, ClassroomFinished++; | |||||
| if(msgrcv.second[3] == '1' && studentstate != StudentState::MovingToRescue) { | |||||
| int dist = 1000000, id = 0; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| //api.Print(fmt::format("stu[{}].x = {} stu[{}].y = {}", i, Students[i].x, i, Students[i].y)); | |||||
| if(msgrcv.first != i) { | |||||
| if(dist > T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y)) { | |||||
| dist = T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y); | |||||
| id = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("id{},dist{}", id, dist)); | |||||
| if(id == PlayerId) { | |||||
| studentstate = StudentState::MovingToRescue; | |||||
| CurrentStudentToRescue = msgrcv.first; | |||||
| } | |||||
| } | |||||
| if(msgrcv.second[4] != '*' && !TrickerisInSight) { | |||||
| Trickers.x = T.ToInt(msgrcv.second.substr(4, 5)); | |||||
| Trickers.y = T.ToInt(msgrcv.second.substr(9, 5)); | |||||
| CanGetTrickerInfo = true; | |||||
| } | |||||
| } | |||||
| if(ShouldChangeClassroomToGo) { | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| if(studentstate == StudentState::MovingForClassroom) | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| } | |||||
| void SendMessage(IStudentAPI& api) { | |||||
| if(studentstate == StudentState::MovingForClassroom || studentstate == StudentState::Learning) | |||||
| msg[0] = CurrentClassroomToGo + '0'; | |||||
| if(studentinfo->playerState == THUAI6::PlayerState::Addicted && | |||||
| !((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) | |||||
| msg[3] = '1'; | |||||
| if(TrickerisInSight) | |||||
| msg = msg + T.ToString(Trickers.x) + T.ToString(Trickers.y); | |||||
| else | |||||
| msg = msg + "**********"; | |||||
| for(int i = 0;i < 4;i++) | |||||
| if(PlayerId != i) | |||||
| api.SendTextMessage(i, msg); | |||||
| } | |||||
| bool CheckTrickerStuck(){ | |||||
| int avex = 0,avey = 0; | |||||
| for(int i = 0;i < LastTrickerNum;i++){ | |||||
| avex += LastTrickers[i].x; | |||||
| avey += LastTrickers[i].y; | |||||
| } | |||||
| avex /= LastTrickerNum; | |||||
| avey /= LastTrickerNum; | |||||
| return (T.Dist(avex,avey,Trickers.x,Trickers.y) < 350); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) { | |||||
| studentinfo = api.GetSelfInfo(); | |||||
| //api.Print(fmt::format("studentinfo->dangerAlert {}", studentinfo->dangerAlert)); | |||||
| UpdateMapInfo(api); | |||||
| UpdateStudentInfo(api); | |||||
| if(studentstate != StudentState::Learning && !rousing) { | |||||
| api.EndAllAction(); | |||||
| //api.Print("endallaction"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| } | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| ReadMessage(api); | |||||
| if(CheckTrickerStuck() && CanGetTrickerInfo) { | |||||
| TrickerStuckTime += 50; | |||||
| } else | |||||
| TrickerStuckTime = 0; | |||||
| if(studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && abs(T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) - 15000 / studentinfo->dangerAlert) > 100) | |||||
| Trickers.x = -1; | |||||
| if(!CanGetTrickerInfo) | |||||
| Trickers.x = -1; | |||||
| //api.Print(fmt::format("Trickers{},{}", Trickers.x, Trickers.y)); | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(StuckTime >= 200) { | |||||
| if(PathToGo.size() >= 2) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| StuckTime = 0; | |||||
| } | |||||
| if(studentstate != StudentState::MovingForGrass) { | |||||
| if((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) { | |||||
| rousing = false; | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| //api.Print(fmt::format("grassfind{},{}", Grass[TargetGrass].x, Grass[TargetGrass].y)); | |||||
| } | |||||
| auto bullets = api.GetBullets(); | |||||
| bool FlyingKnifeInSight = false; | |||||
| for(auto i = 0;i < bullets.size();i++) | |||||
| if(bullets[i]->bulletType == THUAI6::BulletType::FlyingKnife){ | |||||
| FlyingKnifeInSight = true; | |||||
| //api.Print(fmt::format("FlyingKnifeInSight!!!")); | |||||
| } | |||||
| if(FlyingKnifeInSight){ | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| } | |||||
| } | |||||
| if(ClassroomFinished >= 7 && studentstate != StudentState::WaitToGraduate && studentstate != StudentState::MovingToGraduate && studentstate != StudentState::MovingForGrass) { | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| } | |||||
| //api.Print(fmt::format("Courage{}", Courage)); | |||||
| //api.Print(fmt::format("EscapeDist / Courage{}", EscapeDist / Courage)); | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01 && studentinfo->determination < 3200000) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| //api.Print(fmt::format("state:MovingForClassroom")); | |||||
| //api.Print(fmt::format("classroom{}:{},{}", CurrentClassroomToGo, Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y)); | |||||
| //CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| case StudentState::Learning: | |||||
| //api.Print(fmt::format("state:Learning")); | |||||
| //api.Print(fmt::format("classroom[{}] process = {}", CurrentClassroomToGo, api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y))); | |||||
| if(api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y) == 10000000) { | |||||
| //api.Print("ClassroomFinished"); | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| Classroom[CurrentClassroomToGo].finished = true; | |||||
| ClassroomFinished++; | |||||
| msg[1] = '1'; | |||||
| msg[2] = (char)(CurrentClassroomToGo + '0'); | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } else | |||||
| api.StartLearning(); | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| Courage += 0.002; | |||||
| //api.Print(fmt::format("state:MovingForGrass")); | |||||
| if(!((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage)))) { | |||||
| WaitTrickerTime += 50; | |||||
| if(Trickers.trickerType == THUAI6::TrickerType::ANoisyPerson){ | |||||
| if(WaitTrickerTime >= 10000) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| }else{ | |||||
| if(WaitTrickerTime >= 2500) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[2] < 0.01) { | |||||
| api.UseSkill(2); | |||||
| } | |||||
| if(PathToGo.empty()) { | |||||
| //api.Print("gototricker"); | |||||
| if(Map[Cellx - 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx - 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx - 1, Celly}, true); | |||||
| if(Map[Cellx + 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx + 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx + 1, Celly}, true); | |||||
| if(Map[Cellx][Celly - 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly - 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly - 1}, true); | |||||
| if(Map[Cellx][Celly + 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly + 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly + 1}, true); | |||||
| } | |||||
| /*int TargetGrass; | |||||
| TargetGrass = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[TargetGrass].x, Grass[TargetGrass].y}, true);*/ | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToRescue: | |||||
| //api.Print(fmt::format("state:MovingToRescue")); | |||||
| //api.Print(fmt::format("rescuestudent{}:{},{}", CurrentStudentToRescue, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| rousing = false; | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToRescue].x),api.GridToCell(Students[CurrentStudentToRescue].y)}, false); | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y) < 1200) { | |||||
| api.StartRouseMate(CurrentStudentToRescue); | |||||
| PathToGo.clear(); | |||||
| rousing = true; | |||||
| //api.Print("rousing!"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| //api.Print("rouseover!"); | |||||
| rousing = false; | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| api.StartOpenGate(); | |||||
| if(api.GetGateProgress(Gate[CurrentGateToGo].x, Gate[CurrentGateToGo].y) == 18000) { | |||||
| api.Graduate(); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| if(TrickerStuckTime >= 200) { | |||||
| Courage += 0.003; | |||||
| }else{ | |||||
| if(CanGetTrickerInfo) | |||||
| Courage = 1; | |||||
| } | |||||
| if(Courage > 1) | |||||
| Courage -= 0.0015; | |||||
| if(Courage > 2.3) | |||||
| Courage = 2.3; | |||||
| if(Courage < 1) | |||||
| Courage = 1; | |||||
| //api.Print(fmt::format("Courage = {}", Courage)); | |||||
| //api.Print(fmt::format("CriticalDist = {}", EscapeDist / Courage)); | |||||
| //api.Print(fmt::format("BGM = {}", studentinfo->dangerAlert)); | |||||
| //api.Print(fmt::format("BGM Dist = {}", 15000 / studentinfo->dangerAlert)); | |||||
| //for(auto i = PathToGo.begin();i != PathToGo.end();i++) | |||||
| // api.Print(fmt::format("{} {}", (*i)->x, (*i)->y)); | |||||
| MoveAlongPath(api); | |||||
| SendMessage(api); | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) { | |||||
| framecount++; | |||||
| if(trickerType == THUAI6::TrickerType::ANoisyPerson) { | |||||
| api.UseSkill(0); | |||||
| goto end; | |||||
| } | |||||
| api.EndAllAction(); | |||||
| trickerinfo = api.GetSelfInfo(); | |||||
| UpdateMapInfo(api); | |||||
| UpdateTrickerInfo(api); | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| int StudentsInSightId; | |||||
| if(StuckTime >= 200) { | |||||
| if(StuckTime >= 1000) { | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } else { | |||||
| if(trickerstate == TrickerState::MovingToSearch || trickerstate == TrickerState::MovingToPursue) { | |||||
| if(api.Attack(faceDir).get()) | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| } else { | |||||
| if(!PathToGo.empty()) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| StudentsInSightId = FindStudentInSight(api); | |||||
| if(StudentsInSightId != -1) { | |||||
| trickerstate = TrickerState::MovingToPursue; | |||||
| CurrentStudentToPursue = StudentsInSightId; | |||||
| } else { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].playerState == THUAI6::PlayerState::Addicted) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToPursue].x), api.GridToCell(Students[CurrentStudentToPursue].y)}, false); | |||||
| if(StudentisInSight[CurrentStudentToPursue]) { | |||||
| if(trickerinfo->timeUntilSkillAvailable[1] == 0) { | |||||
| if(trickerType == THUAI6::TrickerType::Klee && T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.UseSkill(0); | |||||
| } | |||||
| if(api.HaveView(Students[CurrentStudentToPursue].y + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)) | |||||
| if(trickerType == THUAI6::TrickerType::Assassin) | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(trickerinfo->bulletType) { | |||||
| case THUAI6::BulletType::CommonAttackOfTricker: | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| } | |||||
| break; | |||||
| case THUAI6::BulletType::FlyingKnife: | |||||
| api.Attack(atan2( | |||||
| Students[CurrentStudentToPursue].y - Gridy + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x - Gridx + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| case THUAI6::BulletType::BombBomb: | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| break; | |||||
| } | |||||
| MoveAlongPath(api); | |||||
| end:; | |||||
| } | |||||
| @@ -0,0 +1,977 @@ | |||||
| #include <vector> | |||||
| #include <list> | |||||
| #include <math.h> | |||||
| #include <string> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| constexpr int deltaTimems = 50; | |||||
| constexpr int LastTrickerNum = 10; | |||||
| #define deltaTime 0.05 | |||||
| #define pi 3.1415926 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Klee; | |||||
| /* | |||||
| * 全局常量: | |||||
| * | |||||
| * 全局变量: | |||||
| * | |||||
| * Debug相关 | |||||
| * 寻路相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * 学生变量: | |||||
| * 通信相关 | |||||
| * 状态相关 | |||||
| * 游戏相关 | |||||
| * | |||||
| * StraightAStudent变量: | |||||
| * 冥想相关 | |||||
| * Athlete变量: | |||||
| * | |||||
| * Teacher变量: | |||||
| * | |||||
| * Sunshine变量: | |||||
| * | |||||
| * | |||||
| * 捣蛋鬼变量: | |||||
| * | |||||
| * | |||||
| * Assas变量: | |||||
| * | |||||
| * 工具类 | |||||
| * A*类 | |||||
| * | |||||
| */ | |||||
| struct Node { | |||||
| int x, y; | |||||
| int F, G, H; | |||||
| Node* parent; | |||||
| Node(int _x, int _y):x(_x), y(_y), F(0), G(0), H(0), parent(NULL) {} | |||||
| }*node[50][50]; | |||||
| const std::list<Node*> emptylist; | |||||
| const int EscapeDist = 16000; | |||||
| std::list<Node*> PathToGo, PathGone; | |||||
| bool isFirstFrame = true; | |||||
| int PlayerId; | |||||
| int Gridx, Gridy; | |||||
| int Cellx, Celly; | |||||
| std::vector<std::vector<THUAI6::PlaceType>> Map; | |||||
| int map[50][50];//0为无阻碍,1为半阻碍,2为完全阻碍, 3为临时避障 | |||||
| struct ClassRoomInfo { | |||||
| int x, y; | |||||
| bool finished; | |||||
| int occupied;//为4说明无学生占用,为0-3说明相应编号学生正在占用 | |||||
| }Classroom[100]; | |||||
| int ClassroomCount, ClassroomFinished; | |||||
| struct ChestInfo { | |||||
| int x, y; | |||||
| bool opened; | |||||
| int occupied; | |||||
| }Chest[80]; | |||||
| int ChestCount; | |||||
| struct GateInfo { | |||||
| int x, y; | |||||
| bool opening; | |||||
| bool open; | |||||
| }Gate[20]; | |||||
| int GateCount; | |||||
| struct HiddenGateInfo { | |||||
| int x, y; | |||||
| bool refreshed; | |||||
| bool open; | |||||
| }HiddenGate[30]; | |||||
| int HiddenGateCount; | |||||
| struct GrassInfo { | |||||
| int x, y, occupied; | |||||
| }Grass[200]; | |||||
| int GrassCount; | |||||
| double faceDir = 0; | |||||
| bool map1 = false, map2 = false; | |||||
| std::pair<int, int> LastTarget; | |||||
| std::pair<int, std::string> msgrcv; | |||||
| std::string msg;//信息结构:'准备前往*号教室''1/0完成了作业''*号教室作业已完成''需要被营救'"捣蛋鬼x坐标""捣蛋鬼y坐标" | |||||
| std::shared_ptr<const THUAI6::Student> studentinfo; | |||||
| THUAI6::Tricker Trickers,LastTrickers[LastTrickerNum]; | |||||
| bool TrickerisInSight; | |||||
| bool CanGetTrickerInfo; | |||||
| enum class StudentState { | |||||
| MovingForClassroom, Learning, MovingForGrass, MovingToAttck, MovingForProp, MovingToRescue, MovingToGraduate, WaitToGraduate | |||||
| }studentstate; | |||||
| int CurrentClassroomToGo; | |||||
| int CurrentGateToGo; | |||||
| int CurrentStudentToRescue; | |||||
| int CurrentGrassToGo; | |||||
| bool ShouldChangeClassroomToGo; | |||||
| float Courage = 1.0; | |||||
| int TrickerStuckTime; | |||||
| bool rousing = false; | |||||
| int framecount; | |||||
| int MeditationProgress; | |||||
| double ChargeTimeLeft; | |||||
| std::shared_ptr<const THUAI6::Tricker> trickerinfo; | |||||
| THUAI6::Student Students[4]; | |||||
| bool StudentisInSight[4]; | |||||
| enum class TrickerState { | |||||
| MovingToPatrol, MovingToPursue, MovingToSearch, MovingForProp | |||||
| }trickerstate; | |||||
| struct _PatrolNode { | |||||
| int x, y; | |||||
| }; | |||||
| std::vector<_PatrolNode> PatrolNode; | |||||
| int CurrentPatrolNodeToGo; | |||||
| int CurrentStudentToPursue; | |||||
| int StuckTime; | |||||
| int WaitTrickerTime; | |||||
| bool bgmuseful; | |||||
| class Tools { | |||||
| public: | |||||
| double max(double a, double b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| double min(double a, double b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| double Dist(double x1, double y1, double x2, double y2) { | |||||
| return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); | |||||
| } | |||||
| int ToInt(std::string s) { | |||||
| int x = 0; | |||||
| for(int i = 0;i < 5;i++, x *= 10) | |||||
| x += s[i] - '0'; | |||||
| return x / 10; | |||||
| } | |||||
| std::string ToString(int x) { | |||||
| std::string s = "*****"; | |||||
| for(int i = 0;i < 5;i++, x /= 10) | |||||
| s[4 - i] = x % 10 + '0'; | |||||
| return s; | |||||
| } | |||||
| double StuDistValSet(double Dist) { | |||||
| if(Dist < 7500) | |||||
| return Dist; | |||||
| return (Dist - 7500) * 2 + 7500; | |||||
| } | |||||
| double TriDistValSet(double Dist) { | |||||
| int dist1 = 8500, dist2 = 10500; | |||||
| if(Dist < dist1) | |||||
| return Dist * 6; | |||||
| if(Dist >= dist1 && Dist <= dist2) | |||||
| return (Dist - dist1) * 3 + dist1 * 6; | |||||
| return (Dist - dist2) + dist1 * 4 + (dist2 - dist1) * 2; | |||||
| } | |||||
| }T; | |||||
| class A_Star { | |||||
| public: | |||||
| void init() { | |||||
| openlist.clear(); | |||||
| closelist.clear(); | |||||
| } | |||||
| std::list<Node*> GetPath(std::vector<int> st, std::vector<int> ed, bool Reach) { | |||||
| if(st == ed) { | |||||
| std::list<Node*> simplelist; | |||||
| simplelist.push_back(new Node(st[0], st[1])); | |||||
| return simplelist; | |||||
| } | |||||
| openlist.push_back(new Node(st[0], st[1])); | |||||
| while(!openlist.empty()) { | |||||
| Node* CurrentUpdateNode = GetLeastF(); | |||||
| closelist.push_back(CurrentUpdateNode); | |||||
| UpdateFValue(CurrentUpdateNode, ed); | |||||
| Node* CurPathNode; | |||||
| if(!Reach) { | |||||
| CurPathNode = GetDestSurroundNode(ed); | |||||
| if(CurPathNode) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } else { | |||||
| CurPathNode = isInList(openlist, ed[0], ed[1]); | |||||
| if(CurPathNode != NULL) { | |||||
| std::list<Node*> path; | |||||
| while(CurPathNode != NULL) { | |||||
| path.push_front(CurPathNode); | |||||
| CurPathNode = CurPathNode->parent; | |||||
| } | |||||
| return path; | |||||
| } | |||||
| } | |||||
| openlist.remove(CurrentUpdateNode); | |||||
| } | |||||
| return emptylist; | |||||
| } | |||||
| private: | |||||
| void UpdateFValue(Node* CurNode, std::vector<int> ed) { | |||||
| std::list<std::vector<int>> UpdateList = GetSurroundNode(CurNode); | |||||
| for(auto Curnum = UpdateList.begin();Curnum != UpdateList.end();Curnum++) { | |||||
| int i = (*Curnum)[0], j = (*Curnum)[1]; | |||||
| if(isInList(openlist, i, j) == NULL) { | |||||
| Node* UpdateNode = new Node(i, j); | |||||
| UpdateNode->parent = CurNode; | |||||
| if(map[i][j] == 1) { | |||||
| if(PlayerId <= 3 && studentType[PlayerId] == THUAI6::StudentType::StraightAStudent) { | |||||
| if(MeditationProgress + CurNode->G / 28.8 * 40000 > 300000) | |||||
| UpdateNode->G = CurNode->G + 1; | |||||
| else | |||||
| UpdateNode->G = CurNode->G + 100; | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + 1 * (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| } else | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->H = (abs(ed[0] - i) + abs(ed[1] - j)) * 10; | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| openlist.push_back(UpdateNode); | |||||
| } else { | |||||
| Node* UpdateNode = isInList(openlist, i, j); | |||||
| int newG = (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| if(CurNode->G + newG <= UpdateNode->G) { | |||||
| UpdateNode->parent = CurNode; | |||||
| UpdateNode->G = CurNode->G + (int)sqrt(100 * ((i - CurNode->x) * (i - CurNode->x) + (j - CurNode->y) * (j - CurNode->y))); | |||||
| UpdateNode->F = UpdateNode->G + UpdateNode->H; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| std::list<std::vector<int>> GetSurroundNode(Node* CurNode) { | |||||
| std::list<std::vector<int>> SurroundNodeList; | |||||
| bool up = false, down = false, left = false, right = false; | |||||
| if(CurNode->x > 0 && map[CurNode->x - 1][CurNode->y] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y}), up = true; | |||||
| if(CurNode->x < 49 && map[CurNode->x + 1][CurNode->y] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y}), down = true; | |||||
| if(CurNode->y > 0 && map[CurNode->x][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y - 1}), left = true; | |||||
| if(CurNode->y < 49 && map[CurNode->x][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x,CurNode->y + 1}), right = true; | |||||
| if(up && left && map[CurNode->x - 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y - 1}); | |||||
| if(up && right && map[CurNode->x - 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x - 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x - 1,CurNode->y + 1}); | |||||
| if(down && left && map[CurNode->x + 1][CurNode->y - 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y - 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y - 1}); | |||||
| if(down && right && map[CurNode->x + 1][CurNode->y + 1] < 2 && isInList(closelist, CurNode->x + 1, CurNode->y + 1) == NULL) | |||||
| SurroundNodeList.push_back({CurNode->x + 1,CurNode->y + 1}); | |||||
| return SurroundNodeList; | |||||
| } | |||||
| Node* isInList(std::list<Node*>& List, int x, int y) { | |||||
| for(auto i = List.begin();i != List.end();i++) | |||||
| if((*i)->x == x && (*i)->y == y) | |||||
| return (*i); | |||||
| return NULL; | |||||
| } | |||||
| Node* GetDestSurroundNode(std::vector<int> ed) { | |||||
| Node* retNode; | |||||
| retNode = isInList(openlist, ed[0] - 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0] + 1, ed[1]); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] - 1); | |||||
| if(retNode) return retNode; | |||||
| retNode = isInList(openlist, ed[0], ed[1] + 1); | |||||
| if(retNode) return retNode; | |||||
| return NULL; | |||||
| } | |||||
| Node* GetLeastF() { | |||||
| if(!openlist.empty()) { | |||||
| Node* resPoint = openlist.front(); | |||||
| for(auto i = openlist.begin();i != openlist.end();i++) | |||||
| if(resPoint->F > (*i)->F) | |||||
| resPoint = *i; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| private: | |||||
| std::list<Node*> openlist; | |||||
| std::list<Node*> closelist; | |||||
| }AStar; | |||||
| int SearchBestClassroom(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < ClassroomCount;i++) { | |||||
| if(!Classroom[i].finished && Classroom[i].occupied == 4) { | |||||
| double val = T.Dist(api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Classroom[i].x), api.CellToGrid(Classroom[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int SearchBestGate(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GateCount;i++) { | |||||
| double val = T.Dist(api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y), Gridx, Gridy) | |||||
| - 0.5 * T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Gate[i].x), api.CellToGrid(Gate[i].y)); | |||||
| if(val < valmin) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int StudentSearchBestGrass(IAPI& api) { | |||||
| double valmin = 10000000; | |||||
| int nearestId = -1; | |||||
| /*api.Print(fmt::format("Grid{},{}", Gridx, Gridy)); | |||||
| api.Print(fmt::format("Tri{},{}", Trickers.x, Trickers.y));*/ | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double val = T.StuDistValSet(T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy)); | |||||
| /*api.Print(fmt::format("Grass{},{}:val{}", Grass[i].x, Grass[i].y, val)); | |||||
| api.Print(fmt::format("Dist(stu,Grass)={}", T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy))); | |||||
| api.Print(fmt::format("Dist(tri,Grass)={}", T.Dist(Trickers.x, Trickers.y, api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y))));*/ | |||||
| if(val < valmin && !Grass[i].occupied) { | |||||
| valmin = val; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| int TrickerSearchPossibleGrass(IAPI& api, int DistMax) { | |||||
| double distmin = 10000000; | |||||
| int nearestId = -1; | |||||
| for(int i = 0;i < GrassCount;i++) { | |||||
| double dist = T.Dist(api.CellToGrid(Grass[i].x), api.CellToGrid(Grass[i].y), Gridx, Gridy); | |||||
| if(dist < distmin) { | |||||
| distmin = dist; | |||||
| nearestId = i; | |||||
| } | |||||
| } | |||||
| return nearestId; | |||||
| } | |||||
| void FindPathTo(std::pair<int, int> Target, bool CanReach, bool ForceToFind = false) { | |||||
| if((PathToGo.empty() || (!PathToGo.empty() && LastTarget != Target)) || ForceToFind) { | |||||
| LastTarget = Target; | |||||
| AStar.init(); | |||||
| PathToGo = AStar.GetPath({Cellx,Celly}, {Target.first,Target.second}, CanReach); | |||||
| } | |||||
| } | |||||
| inline void StudentStart(IAPI& api) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| inline void TrickerStart(IAPI& api) { | |||||
| CurrentPatrolNodeToGo = 0; | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| inline void Start(IAPI& api) { | |||||
| isFirstFrame = false; | |||||
| Map = api.GetFullMap(); | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(Map[i][j] == THUAI6::PlaceType::Window) | |||||
| map[i][j] = 1; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Wall || Map[i][j] == THUAI6::PlaceType::Gate | |||||
| || Map[i][j] == THUAI6::PlaceType::Chest || Map[i][j] == THUAI6::PlaceType::ClassRoom | |||||
| || Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| map[i][j] = 2; | |||||
| if(Map[i][j] == THUAI6::PlaceType::ClassRoom) | |||||
| Classroom[ClassroomCount].x = i, Classroom[ClassroomCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Chest) | |||||
| Chest[ChestCount].x = i, Chest[ChestCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Gate) | |||||
| Gate[GateCount].x = i, Gate[GateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::HiddenGate) | |||||
| HiddenGate[HiddenGateCount].x = i, HiddenGate[HiddenGateCount++].y = j; | |||||
| if(Map[i][j] == THUAI6::PlaceType::Grass) { | |||||
| Grass[GrassCount].x = i; | |||||
| Grass[GrassCount++].y = j; | |||||
| //api.Print(fmt::format("Grass[{}]:{},{}", GrassCount - 1, Grass[GrassCount - 1].x, Grass[GrassCount - 1].y)); | |||||
| } | |||||
| } | |||||
| if(Gate[0].y > 25) { | |||||
| PatrolNode = {{18,18},{31,31},{42,31},{42,18},{31,7},{31,18},{18,31},{18,42},{7,31},{7,18}}; | |||||
| map2 = true; | |||||
| } else { | |||||
| PatrolNode = {{23,13},{18,7},{8,9},{8,33},{14,36},{23,29},{31,35},{42,41},{40,24}}; | |||||
| map1 = true; | |||||
| } | |||||
| if(PlayerId <= 3) { | |||||
| StudentStart(api); | |||||
| } else | |||||
| TrickerStart(api); | |||||
| } | |||||
| void UpdateStudentInfo(IStudentAPI& api) { | |||||
| msg = "*0*0"; | |||||
| if(studentinfo->x == Gridx && studentinfo->y == Gridy && studentinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| switch(studentType[PlayerId]) { | |||||
| case THUAI6::StudentType::StraightAStudent: | |||||
| if((int)studentinfo->playerState >= 1 && (int)studentinfo->playerState <= 10 && (int)studentinfo->playerState != 2) | |||||
| MeditationProgress += 40 * deltaTimems; | |||||
| else | |||||
| MeditationProgress = 0; | |||||
| break; | |||||
| } | |||||
| Gridx = studentinfo->x; | |||||
| Gridy = studentinfo->y; | |||||
| Cellx = api.GridToCell(studentinfo->x); | |||||
| Celly = api.GridToCell(studentinfo->y); | |||||
| for(int i = 0;i < 4;i++) | |||||
| Students[i] = *(api.GetStudents()[i]); | |||||
| auto TrickersInSight = api.GetTrickers(); | |||||
| TrickerisInSight = false; | |||||
| CanGetTrickerInfo = false; | |||||
| for(int i= LastTrickerNum - 1;i >= 1;i--) | |||||
| LastTrickers[i] = LastTrickers[i-1]; | |||||
| LastTrickers[0] = Trickers; | |||||
| if(!TrickersInSight.empty()) { | |||||
| //api.Print("TrickerisInSight!"); | |||||
| TrickerisInSight = true; | |||||
| CanGetTrickerInfo = true; | |||||
| Trickers = *TrickersInSight[0]; | |||||
| } else { | |||||
| //api.Print("TrickerisnotInSight!"); | |||||
| //if(Trickers.x != -1) | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(Trickers.x != -1 && api.HaveView(api.GridToCell(Trickers.x), api.GridToCell(Trickers.y))) { | |||||
| Trickers.x = -1; | |||||
| //api.Print("TrickerisnotInSightremove!"); | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("TrickerStuckTime {}", TrickerStuckTime)); | |||||
| } | |||||
| void UpdateTrickerInfo(ITrickerAPI& api) { | |||||
| if(trickerinfo->x == Gridx && trickerinfo->y == Gridy && trickerinfo->playerState == THUAI6::PlayerState::Idle) | |||||
| StuckTime += 50; | |||||
| else | |||||
| StuckTime = 0; | |||||
| Gridx = trickerinfo->x; | |||||
| Gridy = trickerinfo->y; | |||||
| Cellx = api.GridToCell(trickerinfo->x); | |||||
| Celly = api.GridToCell(trickerinfo->y); | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(int i = 0;i < 4;i++) | |||||
| StudentisInSight[i] = false; | |||||
| for(int i = 0;i < StudentsInSight.size();i++) { | |||||
| StudentisInSight[StudentsInSight[i]->playerID] = true; | |||||
| Students[StudentsInSight[i]->playerID] = *StudentsInSight[i]; | |||||
| } | |||||
| } | |||||
| void UpdateMapInfo(IAPI& api) { | |||||
| for(int i = 0;i < 50;i++) | |||||
| for(int j = 0;j < 50;j++) { | |||||
| if(map[i][j] == 3) | |||||
| map[i][j] = 0; | |||||
| if(map[i][j] % 5 == 3) | |||||
| map[i][j] -= 5; | |||||
| } | |||||
| } | |||||
| void MoveAlongPath(IAPI& api) { | |||||
| if(!PathToGo.empty() && ((!PathGone.empty() && PathToGo.front() != PathGone.front()) || PathGone.empty())) { | |||||
| bool Reach = false; | |||||
| if(PathToGo.size() == 1) { | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) < 100) { | |||||
| Reach = true; | |||||
| } | |||||
| } else { | |||||
| auto it = PathToGo.begin();it++; | |||||
| if(T.Dist(Gridx, Gridy, api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y)) | |||||
| + T.Dist(Gridx, Gridy, api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) < | |||||
| T.Dist(api.CellToGrid(PathToGo.front()->x), api.CellToGrid(PathToGo.front()->y), | |||||
| api.CellToGrid((*it)->x), api.CellToGrid((*it)->y)) * 1.15) { | |||||
| Reach = true; | |||||
| } | |||||
| } | |||||
| if(Reach) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| if(!PathToGo.empty()) { | |||||
| if(Map[PathToGo.front()->x][PathToGo.front()->y] == THUAI6::PlaceType::Window) { | |||||
| if(api.SkipWindow().get()) { | |||||
| PathGone.push_front(PathToGo.front()); | |||||
| PathToGo.pop_front(); | |||||
| } | |||||
| } else { | |||||
| faceDir = atan2(api.CellToGrid(PathToGo.front()->y) - Gridy, api.CellToGrid(PathToGo.front()->x) - Gridx); | |||||
| api.Move(500, faceDir); | |||||
| } | |||||
| } else { | |||||
| if(PlayerId <= 3) { | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::StraightAStudent && studentinfo->timeUntilSkillAvailable[0] == 0) | |||||
| api.UseSkill(0); | |||||
| studentstate = StudentState::Learning; | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| api.StartOpenGate(); | |||||
| studentstate = StudentState::WaitToGraduate; | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| break; | |||||
| } | |||||
| } else { | |||||
| int GrassId; | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| } else { | |||||
| trickerstate = TrickerState::MovingToSearch; | |||||
| GrassId = TrickerSearchPossibleGrass(api, 2000); | |||||
| if(GrassId != -1) { | |||||
| FindPathTo({Grass[GrassId].x,Grass[GrassId].y}, true); | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| bool OnlyTeacherLeft(ITrickerAPI& api) { | |||||
| if(framecount > 6000) | |||||
| return true; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| if(Students[i].studentType != THUAI6::StudentType::Teacher && Students[i].determination > 0) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| int FindStudentInSight(ITrickerAPI& api) { | |||||
| auto StudentsInSight = api.GetStudents(); | |||||
| for(auto i = 0;i < StudentsInSight.size();i++) { | |||||
| if(OnlyTeacherLeft(api)) { | |||||
| if(Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } else { | |||||
| if(Students[StudentsInSight[i]->playerID].studentType != THUAI6::StudentType::Teacher && Students[StudentsInSight[i]->playerID].determination > 0) | |||||
| return StudentsInSight[i]->playerID; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| void ReadMessage(IStudentAPI& api) { | |||||
| for(int i = 0;i < 10;i++) | |||||
| Classroom[i].occupied = 4; | |||||
| if(map1) | |||||
| Classroom[5].occupied = 5; | |||||
| ShouldChangeClassroomToGo = false; | |||||
| while(api.HaveMessage()) { | |||||
| msgrcv = api.GetMessage(); | |||||
| Classroom[msgrcv.second[0] - '0'].occupied = msgrcv.first; | |||||
| if(msgrcv.second[0] - '0' == CurrentClassroomToGo) ShouldChangeClassroomToGo = true; | |||||
| if(msgrcv.second[1] == '1') Classroom[msgrcv.second[2] - '0'].finished = true, ClassroomFinished++; | |||||
| if(msgrcv.second[3] == '1' && studentstate != StudentState::MovingToRescue) { | |||||
| int dist = 1000000, id = 0; | |||||
| for(int i = 0;i < 4;i++) { | |||||
| //api.Print(fmt::format("stu[{}].x = {} stu[{}].y = {}", i, Students[i].x, i, Students[i].y)); | |||||
| if(msgrcv.first != i) { | |||||
| if(dist > T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y)) { | |||||
| dist = T.Dist(Students[i].x, Students[i].y, Students[msgrcv.first].x, Students[msgrcv.first].y); | |||||
| id = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| //api.Print(fmt::format("id{},dist{}", id, dist)); | |||||
| if(id == PlayerId) { | |||||
| studentstate = StudentState::MovingToRescue; | |||||
| CurrentStudentToRescue = msgrcv.first; | |||||
| } | |||||
| } | |||||
| if(msgrcv.second[4] != '*' && !TrickerisInSight) { | |||||
| Trickers.x = T.ToInt(msgrcv.second.substr(4, 5)); | |||||
| Trickers.y = T.ToInt(msgrcv.second.substr(9, 5)); | |||||
| CanGetTrickerInfo = true; | |||||
| } | |||||
| } | |||||
| if(ShouldChangeClassroomToGo) { | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| if(studentstate == StudentState::MovingForClassroom) | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } | |||||
| } | |||||
| void SendMessage(IStudentAPI& api) { | |||||
| if(studentstate == StudentState::MovingForClassroom || studentstate == StudentState::Learning) | |||||
| msg[0] = CurrentClassroomToGo + '0'; | |||||
| if(studentinfo->playerState == THUAI6::PlayerState::Addicted && | |||||
| !((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) | |||||
| msg[3] = '1'; | |||||
| if(TrickerisInSight) | |||||
| msg = msg + T.ToString(Trickers.x) + T.ToString(Trickers.y); | |||||
| else | |||||
| msg = msg + "**********"; | |||||
| for(int i = 0;i < 4;i++) | |||||
| if(PlayerId != i) | |||||
| api.SendTextMessage(i, msg); | |||||
| } | |||||
| bool CheckTrickerStuck(){ | |||||
| int avex = 0,avey = 0; | |||||
| for(int i = 0;i < LastTrickerNum;i++){ | |||||
| avex += LastTrickers[i].x; | |||||
| avey += LastTrickers[i].y; | |||||
| } | |||||
| avex /= LastTrickerNum; | |||||
| avey /= LastTrickerNum; | |||||
| return (T.Dist(avex,avey,Trickers.x,Trickers.y) < 350); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) { | |||||
| studentinfo = api.GetSelfInfo(); | |||||
| //api.Print(fmt::format("studentinfo->dangerAlert {}", studentinfo->dangerAlert)); | |||||
| UpdateMapInfo(api); | |||||
| UpdateStudentInfo(api); | |||||
| if(studentstate != StudentState::Learning && !rousing) { | |||||
| api.EndAllAction(); | |||||
| //api.Print("endallaction"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| } | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| ReadMessage(api); | |||||
| if(CheckTrickerStuck() && CanGetTrickerInfo) { | |||||
| TrickerStuckTime += 50; | |||||
| } else | |||||
| TrickerStuckTime = 0; | |||||
| if(studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && abs(T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) - 15000 / studentinfo->dangerAlert) > 100) | |||||
| Trickers.x = -1; | |||||
| if(!CanGetTrickerInfo) | |||||
| Trickers.x = -1; | |||||
| //api.Print(fmt::format("Trickers{},{}", Trickers.x, Trickers.y)); | |||||
| //api.Print(fmt::format("tricker{},{}", Trickers.x, Trickers.y)); | |||||
| if(StuckTime >= 200) { | |||||
| if(PathToGo.size() >= 2) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| StuckTime = 0; | |||||
| } | |||||
| if(studentstate != StudentState::MovingForGrass) { | |||||
| if((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage))) { | |||||
| rousing = false; | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| //api.Print(fmt::format("grassfind{},{}", Grass[TargetGrass].x, Grass[TargetGrass].y)); | |||||
| } | |||||
| auto bullets = api.GetBullets(); | |||||
| bool FlyingKnifeInSight = false; | |||||
| for(auto i = 0;i < bullets.size();i++) | |||||
| if(bullets[i]->bulletType == THUAI6::BulletType::FlyingKnife){ | |||||
| FlyingKnifeInSight = true; | |||||
| //api.Print(fmt::format("FlyingKnifeInSight!!!")); | |||||
| } | |||||
| if(FlyingKnifeInSight){ | |||||
| CurrentGrassToGo = -1; | |||||
| if(map1) { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 0) CurrentGrassToGo = 15; | |||||
| if(CurrentClassroomToGo == 1) CurrentGrassToGo = 30; | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 37; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 65; | |||||
| if(CurrentClassroomToGo == 7) CurrentGrassToGo = 90; | |||||
| } | |||||
| } else { | |||||
| if(studentstate == StudentState::Learning) { | |||||
| if(CurrentClassroomToGo == 3) CurrentGrassToGo = 19; | |||||
| if(CurrentClassroomToGo == 4) CurrentGrassToGo = 45; | |||||
| if(CurrentClassroomToGo == 5) CurrentGrassToGo = 78; | |||||
| if(CurrentClassroomToGo == 6) CurrentGrassToGo = 104; | |||||
| } | |||||
| } | |||||
| if(CurrentGrassToGo == -1) | |||||
| CurrentGrassToGo = StudentSearchBestGrass(api); | |||||
| Grass[CurrentGrassToGo].occupied = true; | |||||
| FindPathTo({Grass[CurrentGrassToGo].x, Grass[CurrentGrassToGo].y}, true); | |||||
| studentstate = StudentState::MovingForGrass; | |||||
| } | |||||
| } | |||||
| if(ClassroomFinished >= 7 && studentstate != StudentState::WaitToGraduate && studentstate != StudentState::MovingToGraduate && studentstate != StudentState::MovingForGrass) { | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| } | |||||
| //api.Print(fmt::format("Courage{}", Courage)); | |||||
| //api.Print(fmt::format("EscapeDist / Courage{}", EscapeDist / Courage)); | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01 && studentinfo->determination < 3200000) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(studentstate) { | |||||
| case StudentState::MovingForClassroom: | |||||
| //api.Print(fmt::format("state:MovingForClassroom")); | |||||
| //api.Print(fmt::format("classroom{}:{},{}", CurrentClassroomToGo, Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y)); | |||||
| //CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| case StudentState::Learning: | |||||
| //api.Print(fmt::format("state:Learning")); | |||||
| //api.Print(fmt::format("classroom[{}] process = {}", CurrentClassroomToGo, api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y))); | |||||
| if(api.GetClassroomProgress(Classroom[CurrentClassroomToGo].x, Classroom[CurrentClassroomToGo].y) == 10000000) { | |||||
| //api.Print("ClassroomFinished"); | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| Classroom[CurrentClassroomToGo].finished = true; | |||||
| ClassroomFinished++; | |||||
| msg[1] = '1'; | |||||
| msg[2] = (char)(CurrentClassroomToGo + '0'); | |||||
| CurrentClassroomToGo = SearchBestClassroom(api); | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| } else | |||||
| api.StartLearning(); | |||||
| break; | |||||
| case StudentState::MovingForGrass: | |||||
| Courage += 0.002; | |||||
| //api.Print(fmt::format("state:MovingForGrass")); | |||||
| if(!((studentinfo->dangerAlert < 1000 && studentinfo->dangerAlert > 0.01 && 15000 / studentinfo->dangerAlert < EscapeDist / Courage) | |||||
| || (Trickers.x != -1 && (T.Dist(Gridx, Gridy, Trickers.x, Trickers.y) < EscapeDist / Courage)))) { | |||||
| WaitTrickerTime += 50; | |||||
| if(Trickers.trickerType == THUAI6::TrickerType::ANoisyPerson){ | |||||
| if(WaitTrickerTime >= 10000) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| }else{ | |||||
| if(WaitTrickerTime >= 2500) { | |||||
| Grass[CurrentGrassToGo].occupied = false; | |||||
| if(ClassroomFinished >= 7) | |||||
| studentstate = StudentState::MovingToGraduate; | |||||
| else | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| WaitTrickerTime = 0; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[2] < 0.01) { | |||||
| api.UseSkill(2); | |||||
| } | |||||
| if(PathToGo.empty()) { | |||||
| //api.Print("gototricker"); | |||||
| if(Map[Cellx - 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx - 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx - 1, Celly}, true); | |||||
| if(Map[Cellx + 1][Celly] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx + 1), api.CellToGrid(Celly), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx + 1, Celly}, true); | |||||
| if(Map[Cellx][Celly - 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly - 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly - 1}, true); | |||||
| if(Map[Cellx][Celly + 1] == THUAI6::PlaceType::Grass | |||||
| && T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly + 1), Trickers.x, Trickers.y) < T.Dist(api.CellToGrid(Cellx), api.CellToGrid(Celly), Trickers.x, Trickers.y)) | |||||
| FindPathTo({Cellx, Celly + 1}, true); | |||||
| } | |||||
| /*int TargetGrass; | |||||
| TargetGrass = StudentSearchBestGrass(api); | |||||
| FindPathTo({Grass[TargetGrass].x, Grass[TargetGrass].y}, true);*/ | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToRescue: | |||||
| //api.Print(fmt::format("state:MovingToRescue")); | |||||
| //api.Print(fmt::format("rescuestudent{}:{},{}", CurrentStudentToRescue, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| rousing = false; | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToRescue].x),api.GridToCell(Students[CurrentStudentToRescue].y)}, false); | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToRescue].x, Students[CurrentStudentToRescue].y) < 1200) { | |||||
| api.StartRouseMate(CurrentStudentToRescue); | |||||
| PathToGo.clear(); | |||||
| rousing = true; | |||||
| //api.Print("rousing!"); | |||||
| //api.Print(fmt::format("state {}", (int)studentinfo->playerState)); | |||||
| if(Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Addicted && Students[CurrentStudentToRescue].playerState != THUAI6::PlayerState::Roused) { | |||||
| studentstate = StudentState::MovingForClassroom; | |||||
| FindPathTo({Classroom[CurrentClassroomToGo].x,Classroom[CurrentClassroomToGo].y}, false); | |||||
| //api.Print("rouseover!"); | |||||
| rousing = false; | |||||
| if(studentType[PlayerId] == THUAI6::StudentType::Sunshine && studentinfo->timeUntilSkillAvailable[1] < 0.01) { | |||||
| api.UseSkill(1); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| case StudentState::MovingToGraduate: | |||||
| CurrentGateToGo = SearchBestGate(api); | |||||
| FindPathTo({Gate[CurrentGateToGo].x,Gate[CurrentGateToGo].y}, false); | |||||
| break; | |||||
| case StudentState::WaitToGraduate: | |||||
| api.StartOpenGate(); | |||||
| if(api.GetGateProgress(Gate[CurrentGateToGo].x, Gate[CurrentGateToGo].y) == 18000) { | |||||
| api.Graduate(); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| if(TrickerStuckTime >= 200) { | |||||
| Courage += 0.003; | |||||
| }else{ | |||||
| if(CanGetTrickerInfo) | |||||
| Courage = 1; | |||||
| } | |||||
| if(Courage > 1) | |||||
| Courage -= 0.0015; | |||||
| if(Courage > 2.3) | |||||
| Courage = 2.3; | |||||
| if(Courage < 1) | |||||
| Courage = 1; | |||||
| //api.Print(fmt::format("Courage = {}", Courage)); | |||||
| //api.Print(fmt::format("CriticalDist = {}", EscapeDist / Courage)); | |||||
| //api.Print(fmt::format("BGM = {}", studentinfo->dangerAlert)); | |||||
| //api.Print(fmt::format("BGM Dist = {}", 15000 / studentinfo->dangerAlert)); | |||||
| //for(auto i = PathToGo.begin();i != PathToGo.end();i++) | |||||
| // api.Print(fmt::format("{} {}", (*i)->x, (*i)->y)); | |||||
| MoveAlongPath(api); | |||||
| SendMessage(api); | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) { | |||||
| framecount++; | |||||
| if(trickerType == THUAI6::TrickerType::ANoisyPerson) { | |||||
| api.UseSkill(0); | |||||
| goto end; | |||||
| } | |||||
| api.EndAllAction(); | |||||
| trickerinfo = api.GetSelfInfo(); | |||||
| UpdateMapInfo(api); | |||||
| UpdateTrickerInfo(api); | |||||
| if(isFirstFrame) { | |||||
| PlayerId = this->playerID; | |||||
| Start(api); | |||||
| } | |||||
| int StudentsInSightId; | |||||
| if(StuckTime >= 200) { | |||||
| if(StuckTime >= 1000) { | |||||
| CurrentPatrolNodeToGo = (CurrentPatrolNodeToGo + 1) % PatrolNode.size(); | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } else { | |||||
| if(trickerstate == TrickerState::MovingToSearch || trickerstate == TrickerState::MovingToPursue) { | |||||
| if(api.Attack(faceDir).get()) | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| if(Students[CurrentStudentToPursue].determination <= 0) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| CurrentStudentToPursue = -1; | |||||
| } | |||||
| } else { | |||||
| if(!PathToGo.empty()) { | |||||
| map[PathToGo.front()->x][PathToGo.front()->y] = 203; | |||||
| FindPathTo({PathToGo.back()->x, PathToGo.back()->y}, true, true); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| switch(trickerstate) { | |||||
| case TrickerState::MovingToPatrol: | |||||
| StudentsInSightId = FindStudentInSight(api); | |||||
| if(StudentsInSightId != -1) { | |||||
| trickerstate = TrickerState::MovingToPursue; | |||||
| CurrentStudentToPursue = StudentsInSightId; | |||||
| } else { | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToPursue: | |||||
| if(Students[CurrentStudentToPursue].playerState == THUAI6::PlayerState::Addicted) { | |||||
| trickerstate = TrickerState::MovingToPatrol; | |||||
| FindPathTo({PatrolNode[CurrentPatrolNodeToGo].x,PatrolNode[CurrentPatrolNodeToGo].y}, true); | |||||
| break; | |||||
| } | |||||
| FindPathTo({api.GridToCell(Students[CurrentStudentToPursue].x), api.GridToCell(Students[CurrentStudentToPursue].y)}, false); | |||||
| if(StudentisInSight[CurrentStudentToPursue]) { | |||||
| if(trickerinfo->timeUntilSkillAvailable[1] == 0) { | |||||
| if(trickerType == THUAI6::TrickerType::Klee && T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.UseSkill(0); | |||||
| } | |||||
| if(api.HaveView(Students[CurrentStudentToPursue].y + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)) | |||||
| if(trickerType == THUAI6::TrickerType::Assassin) | |||||
| api.UseSkill(1); | |||||
| } | |||||
| switch(trickerinfo->bulletType) { | |||||
| case THUAI6::BulletType::CommonAttackOfTricker: | |||||
| if(T.Dist(Gridx, Gridy, Students[CurrentStudentToPursue].x, Students[CurrentStudentToPursue].y) <= 2000) { | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1500000; | |||||
| } | |||||
| break; | |||||
| case THUAI6::BulletType::FlyingKnife: | |||||
| api.Attack(atan2( | |||||
| Students[CurrentStudentToPursue].y - Gridy + sin(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01, | |||||
| Students[CurrentStudentToPursue].x - Gridx + cos(Students[CurrentStudentToPursue].facingDirection) * Students[CurrentStudentToPursue].speed * 0.01)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| case THUAI6::BulletType::BombBomb: | |||||
| api.Attack(atan2(Students[CurrentStudentToPursue].y - Gridy, Students[CurrentStudentToPursue].x - Gridx)); | |||||
| Students[CurrentStudentToPursue].determination -= 1200000; | |||||
| break; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case TrickerState::MovingToSearch: | |||||
| break; | |||||
| } | |||||
| MoveAlongPath(api); | |||||
| end:; | |||||
| } | |||||
| @@ -0,0 +1,772 @@ | |||||
| //四学霸策略 | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #include "API.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = | |||||
| { | |||||
| THUAI6::StudentType::StraightAStudent , | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| //全局变量 | |||||
| int32_t HomeworkLocations[10][2]{ {8,31},{18,5},{10,38},{19,41},{22,18},{28,26},{30,7},{33,40},{40,12},{44,32}}; | |||||
| int32_t GrassLocations[13][2]{ {8,2},{8,16},{5,25},{4,29},{10,46},{18,46},{20,36},{15,29},{20,1},{30,25},{40,24},{39,35},{46,8} }; | |||||
| bool IfResetDestination = true; | |||||
| std::vector<bool> Condition1 = { false, true, false, true, false, false, false, false, true ,true ,true, false };//门都可以,草地,land可以 | |||||
| namespace MoveFunc | |||||
| { | |||||
| int32_t Distance(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return int32_t(sqrt((gridX0 - gridX1) * (gridX0 - gridX1) + (gridY0 - gridY1) * (gridY0 - gridY1))); | |||||
| } | |||||
| //角度为0指向1 | |||||
| double Angle(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return atan2(gridY1 - gridY0, gridX1 - gridX0); | |||||
| } | |||||
| namespace FindWay | |||||
| { | |||||
| int IfAchievable(THUAI6::PlaceType ThisPlace, std::vector<bool> Condition) | |||||
| { | |||||
| //是否可以通过的条件可以进一步完善 | |||||
| if ((ThisPlace == THUAI6::PlaceType::NullPlaceType && Condition[0]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Land && Condition[1]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Wall && Condition[2]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Grass && Condition[3]) | |||||
| || (ThisPlace == THUAI6::PlaceType::ClassRoom && Condition[4]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Gate && Condition[5]) | |||||
| || (ThisPlace == THUAI6::PlaceType::HiddenGate && Condition[6]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Window && Condition[7]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door3 && Condition[8]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door5 && Condition[9]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door6 && Condition[10]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Chest && Condition[11])) | |||||
| return 0; | |||||
| else | |||||
| return 1;//1不可达 | |||||
| } | |||||
| const int kCost1 = 10; //直移一格消耗 | |||||
| const int kCost2 = 14; //斜移一格消耗 | |||||
| struct Point | |||||
| { | |||||
| int x, y; //点坐标,这里为了方便按照C++的数组来计算,x代表横排,y代表竖列 | |||||
| int F, G, H; //F=G+H | |||||
| Point* parent; //parent的坐标,这里没有用指针,从而简化代码 | |||||
| Point(int _x, int _y) :x(_x), y(_y), F(0), G(0), H(0), parent(NULL) //变量初始化 | |||||
| { | |||||
| } | |||||
| }; | |||||
| class Astar | |||||
| { | |||||
| public: | |||||
| void InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& api); | |||||
| std::list<Point*> GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<bool> ThisCondition; | |||||
| Astar(IStudentAPI& _api) : | |||||
| maze({ {} }), api(_api), openList({}), closeList({}) {} | |||||
| private: | |||||
| Point* findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<Point*> getSurroundPoints(const Point* point, bool isIgnoreCorner) const; | |||||
| bool isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const; //判断某点是否可以用于下一步判断 | |||||
| Point* isInList(const std::list<Point*>& list, const Point* point) const; //判断开启/关闭列表中是否包含某点 | |||||
| Point* getLeastFpoint(); //从开启列表中返回F值最小的节点 | |||||
| //计算FGH值 | |||||
| int calcG(Point* temp_start, Point* point); | |||||
| int calcH(Point* point, Point* end); | |||||
| int calcF(Point* point); | |||||
| private: | |||||
| std::vector<std::vector<THUAI6::PlaceType>> maze; | |||||
| std::list<Point*> openList; //开启列表 | |||||
| std::list<Point*> closeList; //关闭列表 | |||||
| IStudentAPI& api; | |||||
| }; | |||||
| void Astar::InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& _api) | |||||
| { | |||||
| maze = _maze; | |||||
| ThisCondition = Condition; | |||||
| api = _api; | |||||
| } | |||||
| int Astar::calcG(Point* temp_start, Point* point) | |||||
| { | |||||
| int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2; | |||||
| int parentG = point->parent == NULL ? 0 : point->parent->G; //如果是初始节点,则其父节点是空 | |||||
| return parentG + extraG; | |||||
| } | |||||
| int Astar::calcH(Point* point, Point* end) | |||||
| { | |||||
| //用简单的欧几里得距离计算H,这个H的计算是关键,还有很多算法,没深入研究^_^ | |||||
| return sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1; | |||||
| } | |||||
| int Astar::calcF(Point* point) | |||||
| { | |||||
| return point->G + point->H; | |||||
| } | |||||
| Point* Astar::getLeastFpoint() | |||||
| { | |||||
| if (!openList.empty()) | |||||
| { | |||||
| auto resPoint = openList.front(); | |||||
| for (auto& point : openList) | |||||
| if (point->F < resPoint->F) | |||||
| resPoint = point; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| openList.push_back(new Point(startPoint.x, startPoint.y)); //置入起点,拷贝开辟一个节点,内外隔离 | |||||
| while (!openList.empty()) | |||||
| { | |||||
| auto curPoint = getLeastFpoint(); //找到F值最小的点 | |||||
| openList.remove(curPoint); //从开启列表中删除 | |||||
| closeList.push_back(curPoint); //放到关闭列表 | |||||
| //1,找到当前周围八个格中可以通过的格子 | |||||
| auto surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner); | |||||
| for (auto& target : surroundPoints) | |||||
| { | |||||
| //2,对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H | |||||
| if (!isInList(openList, target)) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = calcG(curPoint, target); | |||||
| target->H = calcH(target, &endPoint); | |||||
| target->F = calcF(target); | |||||
| openList.push_back(target); | |||||
| } | |||||
| //3,对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F | |||||
| else | |||||
| { | |||||
| int tempG = calcG(curPoint, target); | |||||
| if (tempG < target->G) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = tempG; | |||||
| target->F = calcF(target); | |||||
| } | |||||
| } | |||||
| Point* resPoint = isInList(openList, &endPoint); | |||||
| if (resPoint) | |||||
| return resPoint; //返回列表里的节点指针,不要用原来传入的endpoint指针,因为发生了深拷贝 | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| std::list<Point*> Astar::GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| Point* result = findPath(startPoint, endPoint, isIgnoreCorner); | |||||
| std::list<Point*> path; | |||||
| //返回路径,如果没找到路径,返回空链表 | |||||
| while (result) | |||||
| { | |||||
| path.push_front(result); | |||||
| result = result->parent; | |||||
| } | |||||
| // 清空临时开闭列表,防止重复执行GetPath导致结果异常 | |||||
| openList.clear(); | |||||
| closeList.clear(); | |||||
| return path; | |||||
| } | |||||
| Point* Astar::isInList(const std::list<Point*>& list, const Point* point) const | |||||
| { | |||||
| //判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标 | |||||
| for (auto p : list) | |||||
| if (p->x == point->x && p->y == point->y) | |||||
| return p; | |||||
| return NULL; | |||||
| } | |||||
| bool Astar::isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const | |||||
| { | |||||
| if ((target->x==api.GridToCell(api.GetSelfInfo()->x)&& target->y == api.GridToCell(api.GetSelfInfo()->y)) | |||||
| ||target->x<0 || target->x>maze.size() - 1 | |||||
| || target->y<0 || target->y>maze[0].size() - 1 | |||||
| || IfAchievable(maze[target->x][target->y], ThisCondition) == 1 | |||||
| || target->x == point->x && target->y == point->y | |||||
| || isInList(closeList, target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false | |||||
| return false; | |||||
| else | |||||
| { | |||||
| if (abs(point->x - target->x) + abs(point->y - target->y) == 1) //非斜角可以 | |||||
| return true; | |||||
| else | |||||
| { | |||||
| //斜对角要判断是否绊住 | |||||
| if (IfAchievable(maze[point->x][target->y], ThisCondition) == 0 && IfAchievable(maze[target->x][point->y], ThisCondition) == 0) | |||||
| return true; | |||||
| else | |||||
| return isIgnoreCorner; | |||||
| } | |||||
| } | |||||
| } | |||||
| std::vector<Point*> Astar::getSurroundPoints(const Point* point, bool isIgnoreCorner) const | |||||
| { | |||||
| std::vector<Point*> surroundPoints; | |||||
| for (int x = point->x - 1; x <= point->x + 1; x++) | |||||
| for (int y = point->y - 1; y <= point->y + 1; y++) | |||||
| if (isCanreach(point, new Point(x, y), isIgnoreCorner)) | |||||
| surroundPoints.push_back(new Point(x, y)); | |||||
| return surroundPoints; | |||||
| } | |||||
| bool InPath(const int& row, const int& col, const std::list<Point*>& path) { | |||||
| for (const auto& p : path) { | |||||
| if (row == p->x && col == p->y) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| std::vector<std::vector<int32_t>> findPath(const std::vector<std::vector<THUAI6::PlaceType>>& map, int32_t cellX0, int32_t cellY0, int32_t cellX1, int32_t cellY1, std::vector<bool> Condition, IStudentAPI& api) | |||||
| { | |||||
| Astar astar(api); | |||||
| astar.InitAstar(map, Condition,api); | |||||
| Point start(cellX0, cellY0); | |||||
| Point end(cellX1, cellY1); | |||||
| std::list<Point*> path = astar.GetPath(start, end, false); | |||||
| std::vector<std::vector<int>> result{}; | |||||
| for (auto P : path) | |||||
| { | |||||
| std::vector<int> Loc{ P->x,P->y }; | |||||
| result.push_back(Loc); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| namespace GoToSomePlace | |||||
| { | |||||
| std::vector<bool> MoveToNextBlock(IStudentAPI& api, int32_t cellX, int32_t cellY) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (Distance(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY)) < 50)//可以调整 | |||||
| return { false,true }; | |||||
| auto p = api.Move(50, Angle(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY))); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| if (!p.get()) | |||||
| return { false,false }; | |||||
| return { true,true }; | |||||
| }//可以修改移动时间 | |||||
| //不检查是否可达,谨慎使用 | |||||
| class GoToAPlace | |||||
| { | |||||
| protected: | |||||
| bool Going = false;//是否正在走 | |||||
| int32_t DestinationX = -1;//cell | |||||
| int32_t DestinationY = -1; | |||||
| std::vector<std::vector<int32_t>> Way{ {} };//没吃到 | |||||
| int32_t StepNumber = 0; | |||||
| int32_t ThisStep = 0; | |||||
| IStudentAPI& api; | |||||
| std::vector<bool> ThisCondition; | |||||
| int32_t LastGridX=-1; | |||||
| int32_t LastGridY=-1; | |||||
| public: | |||||
| GoToAPlace(IStudentAPI& _api) : | |||||
| Going(false), DestinationX(-1), DestinationY(-1), LastGridX(-1), LastGridY(-1), api(_api), StepNumber(0), ThisStep(0), Way({}), ThisCondition{ {} } {} | |||||
| GoToAPlace& operator =(GoToAPlace P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way={ {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api=P.api; | |||||
| ThisCondition=P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return (*this); | |||||
| } | |||||
| bool SetDestination(int32_t cellX, int32_t cellY, std::vector<bool> Condition) | |||||
| { | |||||
| if (api.GridToCell(api.GetSelfInfo()->x) == cellX && api.GridToCell(api.GetSelfInfo()->y) == cellY) | |||||
| Way = { {} }; | |||||
| else | |||||
| Way = FindWay::findPath(api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), cellX, cellY, Condition, api); | |||||
| if (Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| Going = true; | |||||
| DestinationX = cellX; | |||||
| DestinationY = cellY; | |||||
| StepNumber = Way.size();//size-1是最大可访问下表 | |||||
| ThisStep = 0;//从0到size-1 | |||||
| ThisCondition = Condition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return true; | |||||
| } | |||||
| void Unset() | |||||
| { | |||||
| Going = false; | |||||
| DestinationX = -1; | |||||
| DestinationY = -1; | |||||
| Way = { {} }; | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| } | |||||
| void Update() | |||||
| { | |||||
| if (!Going) | |||||
| return; | |||||
| if (ThisStep >= Way.size() - 1) | |||||
| { | |||||
| Unset(); | |||||
| return; | |||||
| } | |||||
| return; | |||||
| } | |||||
| bool MoveAStep() | |||||
| { | |||||
| Update(); | |||||
| if (!Going||Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| if ((LastGridX != -1 && Distance(LastGridX, LastGridY, api.GetSelfInfo()->x, api.GetSelfInfo()->y) < 20) | |||||
| || abs(Way[ThisStep + 1][0] - api.GridToCell(api.GetSelfInfo()->x)) + abs(Way[ThisStep + 1][1] - api.GridToCell(api.GetSelfInfo()->y)) > 5) | |||||
| { | |||||
| SetDestination(DestinationX, DestinationY, Condition1); | |||||
| return false; | |||||
| } | |||||
| auto p = MoveToNextBlock(api, Way[ThisStep + 1][0], Way[ThisStep + 1][1]); | |||||
| if (!p[0]) | |||||
| ThisStep++; | |||||
| LastGridX = api.GetSelfInfo()->x; | |||||
| LastGridY = api.GetSelfInfo()->y; | |||||
| Update(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| bool IfDone() | |||||
| { | |||||
| Update(); | |||||
| return !Going; | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| namespace Homework | |||||
| { | |||||
| struct Homework | |||||
| { | |||||
| int32_t x = -1; | |||||
| int32_t y = -1;//cell | |||||
| int32_t Progress = 0; | |||||
| bool IfBeingDone = false;//待实现 | |||||
| Homework() :x(-1), y(-1), Progress(0), IfBeingDone(false) {} | |||||
| }; | |||||
| enum class Direction | |||||
| { | |||||
| Left = 0, | |||||
| Right = 1, | |||||
| Up = 2, | |||||
| Down = 3, | |||||
| LeftUp = 4, | |||||
| LeftDown = 5, | |||||
| RightUp = 6, | |||||
| RightDown = 7 | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework); | |||||
| class GoToNearestHomework :public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| Homework ThisHomework; | |||||
| public: | |||||
| GoToNearestHomework(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), ThisHomework() {} | |||||
| GoToNearestHomework& operator=(GoToNearestHomework P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| ThisHomework = Homework(); | |||||
| return (*this); | |||||
| } | |||||
| Homework FindTheNearestHomework()//考虑实现对于多人走向同一份作业的规避,目前未实现 | |||||
| { | |||||
| Homework NearestHomework; | |||||
| int32_t DistanceOfTheNearestHomework = -1; | |||||
| int32_t NumberOfTheNearestHomework = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 10; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), HomeworkLocations[i][0], HomeworkLocations[i][1], { false,true,false,true,true,false,false,false,true,true,true,false }, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestHomework && api.GetClassroomProgress(HomeworkLocations[i][0], HomeworkLocations[i][1]) < 10000000)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| } | |||||
| if (NumberOfTheNearestHomework != -1) | |||||
| { | |||||
| NearestHomework.x = HomeworkLocations[NumberOfTheNearestHomework][0]; | |||||
| NearestHomework.y = HomeworkLocations[NumberOfTheNearestHomework][1]; | |||||
| NearestHomework.Progress = api.GetClassroomProgress(NearestHomework.x, NearestHomework.y); | |||||
| } | |||||
| return NearestHomework; | |||||
| } | |||||
| std::vector<bool> MoveAStepToNearestHomework(Direction ThisDirection)//第一个位置返回是否有最近作业,第二个位置返回是否成功 | |||||
| { | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| auto a = MoveAStep(); | |||||
| return { true, a }; | |||||
| } | |||||
| void UnsetHomework() | |||||
| { | |||||
| ThisHomework = Homework(); | |||||
| } | |||||
| std::vector<bool> FindAndDo(Direction ThisDirection = Direction::Right)//是否找到,是否赶到,是否完成 | |||||
| { | |||||
| if (api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| { | |||||
| UnsetHomework(); | |||||
| Unset(); | |||||
| } | |||||
| if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /*if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1); | |||||
| }*/ | |||||
| //MoveAStep(); | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return { true,false,false }; | |||||
| } | |||||
| else | |||||
| { | |||||
| Unset(); | |||||
| auto a = DoHomework(api, ThisHomework); | |||||
| return{ true,true,!a }; | |||||
| } | |||||
| } | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (abs(api.GridToCell(self->x) - ThisHomework.x) > 1 || abs(api.GridToCell(self->y) - ThisHomework.y) > 1 || api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| return false; | |||||
| auto State = api.StartLearning(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| namespace Grass | |||||
| { | |||||
| class MoveToGrass : public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| bool Escaping = false; | |||||
| public: | |||||
| MoveToGrass(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), Escaping(false) {} | |||||
| MoveToGrass& operator=(MoveToGrass P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| Escaping = false; | |||||
| return (*this); | |||||
| } | |||||
| std::vector<int32_t> FindTheNearestGrass() | |||||
| { | |||||
| int32_t DistanceOfTheNearestGrass = -1; | |||||
| int32_t NumberOfTheNearestGrass = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 13; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), GrassLocations[i][0], GrassLocations[i][1], Condition1, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestGrass)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| } | |||||
| return { GrassLocations[NumberOfTheNearestGrass][0],GrassLocations[NumberOfTheNearestGrass][1] }; | |||||
| } | |||||
| void FindAndGo() | |||||
| { | |||||
| if (!Escaping) | |||||
| { | |||||
| auto p = FindTheNearestGrass(); | |||||
| SetDestination(p[0], p[1], Condition1); | |||||
| Escaping = true; | |||||
| } | |||||
| else | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return; | |||||
| } | |||||
| else | |||||
| { | |||||
| return; | |||||
| } | |||||
| } | |||||
| void StopEscape() | |||||
| { | |||||
| Escaping = false; | |||||
| } | |||||
| }; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| static std::vector<std::vector<int>> a{ {24,6},{15,6},{15,17},{24,17} }; | |||||
| if (this->playerID == 0)//三教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| //玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3)//六教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,772 @@ | |||||
| //四学霸策略 | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #include "API.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = | |||||
| { | |||||
| THUAI6::StudentType::StraightAStudent , | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| //全局变量 | |||||
| int32_t HomeworkLocations[10][2]{ {8,31},{18,5},{10,38},{19,41},{22,18},{28,26},{30,7},{33,40},{40,12},{44,32}}; | |||||
| int32_t GrassLocations[13][2]{ {8,2},{8,16},{5,25},{4,29},{10,46},{18,46},{20,36},{15,29},{20,1},{30,25},{40,24},{39,35},{46,8} }; | |||||
| bool IfResetDestination = true; | |||||
| std::vector<bool> Condition1 = { false, true, false, true, false, false, false, false, true ,true ,true, false };//门都可以,草地,land可以 | |||||
| namespace MoveFunc | |||||
| { | |||||
| int32_t Distance(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return int32_t(sqrt((gridX0 - gridX1) * (gridX0 - gridX1) + (gridY0 - gridY1) * (gridY0 - gridY1))); | |||||
| } | |||||
| //角度为0指向1 | |||||
| double Angle(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return atan2(gridY1 - gridY0, gridX1 - gridX0); | |||||
| } | |||||
| namespace FindWay | |||||
| { | |||||
| int IfAchievable(THUAI6::PlaceType ThisPlace, std::vector<bool> Condition) | |||||
| { | |||||
| //是否可以通过的条件可以进一步完善 | |||||
| if ((ThisPlace == THUAI6::PlaceType::NullPlaceType && Condition[0]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Land && Condition[1]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Wall && Condition[2]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Grass && Condition[3]) | |||||
| || (ThisPlace == THUAI6::PlaceType::ClassRoom && Condition[4]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Gate && Condition[5]) | |||||
| || (ThisPlace == THUAI6::PlaceType::HiddenGate && Condition[6]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Window && Condition[7]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door3 && Condition[8]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door5 && Condition[9]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door6 && Condition[10]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Chest && Condition[11])) | |||||
| return 0; | |||||
| else | |||||
| return 1;//1不可达 | |||||
| } | |||||
| const int kCost1 = 10; //直移一格消耗 | |||||
| const int kCost2 = 14; //斜移一格消耗 | |||||
| struct Point | |||||
| { | |||||
| int x, y; //点坐标,这里为了方便按照C++的数组来计算,x代表横排,y代表竖列 | |||||
| int F, G, H; //F=G+H | |||||
| Point* parent; //parent的坐标,这里没有用指针,从而简化代码 | |||||
| Point(int _x, int _y) :x(_x), y(_y), F(0), G(0), H(0), parent(NULL) //变量初始化 | |||||
| { | |||||
| } | |||||
| }; | |||||
| class Astar | |||||
| { | |||||
| public: | |||||
| void InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& api); | |||||
| std::list<Point*> GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<bool> ThisCondition; | |||||
| Astar(IStudentAPI& _api) : | |||||
| maze({ {} }), api(_api), openList({}), closeList({}) {} | |||||
| private: | |||||
| Point* findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<Point*> getSurroundPoints(const Point* point, bool isIgnoreCorner) const; | |||||
| bool isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const; //判断某点是否可以用于下一步判断 | |||||
| Point* isInList(const std::list<Point*>& list, const Point* point) const; //判断开启/关闭列表中是否包含某点 | |||||
| Point* getLeastFpoint(); //从开启列表中返回F值最小的节点 | |||||
| //计算FGH值 | |||||
| int calcG(Point* temp_start, Point* point); | |||||
| int calcH(Point* point, Point* end); | |||||
| int calcF(Point* point); | |||||
| private: | |||||
| std::vector<std::vector<THUAI6::PlaceType>> maze; | |||||
| std::list<Point*> openList; //开启列表 | |||||
| std::list<Point*> closeList; //关闭列表 | |||||
| IStudentAPI& api; | |||||
| }; | |||||
| void Astar::InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& _api) | |||||
| { | |||||
| maze = _maze; | |||||
| ThisCondition = Condition; | |||||
| api = _api; | |||||
| } | |||||
| int Astar::calcG(Point* temp_start, Point* point) | |||||
| { | |||||
| int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2; | |||||
| int parentG = point->parent == NULL ? 0 : point->parent->G; //如果是初始节点,则其父节点是空 | |||||
| return parentG + extraG; | |||||
| } | |||||
| int Astar::calcH(Point* point, Point* end) | |||||
| { | |||||
| //用简单的欧几里得距离计算H,这个H的计算是关键,还有很多算法,没深入研究^_^ | |||||
| return sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1; | |||||
| } | |||||
| int Astar::calcF(Point* point) | |||||
| { | |||||
| return point->G + point->H; | |||||
| } | |||||
| Point* Astar::getLeastFpoint() | |||||
| { | |||||
| if (!openList.empty()) | |||||
| { | |||||
| auto resPoint = openList.front(); | |||||
| for (auto& point : openList) | |||||
| if (point->F < resPoint->F) | |||||
| resPoint = point; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| openList.push_back(new Point(startPoint.x, startPoint.y)); //置入起点,拷贝开辟一个节点,内外隔离 | |||||
| while (!openList.empty()) | |||||
| { | |||||
| auto curPoint = getLeastFpoint(); //找到F值最小的点 | |||||
| openList.remove(curPoint); //从开启列表中删除 | |||||
| closeList.push_back(curPoint); //放到关闭列表 | |||||
| //1,找到当前周围八个格中可以通过的格子 | |||||
| auto surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner); | |||||
| for (auto& target : surroundPoints) | |||||
| { | |||||
| //2,对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H | |||||
| if (!isInList(openList, target)) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = calcG(curPoint, target); | |||||
| target->H = calcH(target, &endPoint); | |||||
| target->F = calcF(target); | |||||
| openList.push_back(target); | |||||
| } | |||||
| //3,对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F | |||||
| else | |||||
| { | |||||
| int tempG = calcG(curPoint, target); | |||||
| if (tempG < target->G) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = tempG; | |||||
| target->F = calcF(target); | |||||
| } | |||||
| } | |||||
| Point* resPoint = isInList(openList, &endPoint); | |||||
| if (resPoint) | |||||
| return resPoint; //返回列表里的节点指针,不要用原来传入的endpoint指针,因为发生了深拷贝 | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| std::list<Point*> Astar::GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| Point* result = findPath(startPoint, endPoint, isIgnoreCorner); | |||||
| std::list<Point*> path; | |||||
| //返回路径,如果没找到路径,返回空链表 | |||||
| while (result) | |||||
| { | |||||
| path.push_front(result); | |||||
| result = result->parent; | |||||
| } | |||||
| // 清空临时开闭列表,防止重复执行GetPath导致结果异常 | |||||
| openList.clear(); | |||||
| closeList.clear(); | |||||
| return path; | |||||
| } | |||||
| Point* Astar::isInList(const std::list<Point*>& list, const Point* point) const | |||||
| { | |||||
| //判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标 | |||||
| for (auto p : list) | |||||
| if (p->x == point->x && p->y == point->y) | |||||
| return p; | |||||
| return NULL; | |||||
| } | |||||
| bool Astar::isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const | |||||
| { | |||||
| if ((target->x==api.GridToCell(api.GetSelfInfo()->x)&& target->y == api.GridToCell(api.GetSelfInfo()->y)) | |||||
| ||target->x<0 || target->x>maze.size() - 1 | |||||
| || target->y<0 || target->y>maze[0].size() - 1 | |||||
| || IfAchievable(maze[target->x][target->y], ThisCondition) == 1 | |||||
| || target->x == point->x && target->y == point->y | |||||
| || isInList(closeList, target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false | |||||
| return false; | |||||
| else | |||||
| { | |||||
| if (abs(point->x - target->x) + abs(point->y - target->y) == 1) //非斜角可以 | |||||
| return true; | |||||
| else | |||||
| { | |||||
| //斜对角要判断是否绊住 | |||||
| if (IfAchievable(maze[point->x][target->y], ThisCondition) == 0 && IfAchievable(maze[target->x][point->y], ThisCondition) == 0) | |||||
| return true; | |||||
| else | |||||
| return isIgnoreCorner; | |||||
| } | |||||
| } | |||||
| } | |||||
| std::vector<Point*> Astar::getSurroundPoints(const Point* point, bool isIgnoreCorner) const | |||||
| { | |||||
| std::vector<Point*> surroundPoints; | |||||
| for (int x = point->x - 1; x <= point->x + 1; x++) | |||||
| for (int y = point->y - 1; y <= point->y + 1; y++) | |||||
| if (isCanreach(point, new Point(x, y), isIgnoreCorner)) | |||||
| surroundPoints.push_back(new Point(x, y)); | |||||
| return surroundPoints; | |||||
| } | |||||
| bool InPath(const int& row, const int& col, const std::list<Point*>& path) { | |||||
| for (const auto& p : path) { | |||||
| if (row == p->x && col == p->y) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| std::vector<std::vector<int32_t>> findPath(const std::vector<std::vector<THUAI6::PlaceType>>& map, int32_t cellX0, int32_t cellY0, int32_t cellX1, int32_t cellY1, std::vector<bool> Condition, IStudentAPI& api) | |||||
| { | |||||
| Astar astar(api); | |||||
| astar.InitAstar(map, Condition,api); | |||||
| Point start(cellX0, cellY0); | |||||
| Point end(cellX1, cellY1); | |||||
| std::list<Point*> path = astar.GetPath(start, end, false); | |||||
| std::vector<std::vector<int>> result{}; | |||||
| for (auto P : path) | |||||
| { | |||||
| std::vector<int> Loc{ P->x,P->y }; | |||||
| result.push_back(Loc); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| namespace GoToSomePlace | |||||
| { | |||||
| std::vector<bool> MoveToNextBlock(IStudentAPI& api, int32_t cellX, int32_t cellY) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (Distance(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY)) < 50)//可以调整 | |||||
| return { false,true }; | |||||
| auto p = api.Move(50, Angle(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY))); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| if (!p.get()) | |||||
| return { false,false }; | |||||
| return { true,true }; | |||||
| }//可以修改移动时间 | |||||
| //不检查是否可达,谨慎使用 | |||||
| class GoToAPlace | |||||
| { | |||||
| protected: | |||||
| bool Going = false;//是否正在走 | |||||
| int32_t DestinationX = -1;//cell | |||||
| int32_t DestinationY = -1; | |||||
| std::vector<std::vector<int32_t>> Way{ {} };//没吃到 | |||||
| int32_t StepNumber = 0; | |||||
| int32_t ThisStep = 0; | |||||
| IStudentAPI& api; | |||||
| std::vector<bool> ThisCondition; | |||||
| int32_t LastGridX=-1; | |||||
| int32_t LastGridY=-1; | |||||
| public: | |||||
| GoToAPlace(IStudentAPI& _api) : | |||||
| Going(false), DestinationX(-1), DestinationY(-1), LastGridX(-1), LastGridY(-1), api(_api), StepNumber(0), ThisStep(0), Way({}), ThisCondition{ {} } {} | |||||
| GoToAPlace& operator =(GoToAPlace P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way={ {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api=P.api; | |||||
| ThisCondition=P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return (*this); | |||||
| } | |||||
| bool SetDestination(int32_t cellX, int32_t cellY, std::vector<bool> Condition) | |||||
| { | |||||
| if (api.GridToCell(api.GetSelfInfo()->x) == cellX && api.GridToCell(api.GetSelfInfo()->y) == cellY) | |||||
| Way = { {} }; | |||||
| else | |||||
| Way = FindWay::findPath(api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), cellX, cellY, Condition, api); | |||||
| if (Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| Going = true; | |||||
| DestinationX = cellX; | |||||
| DestinationY = cellY; | |||||
| StepNumber = Way.size();//size-1是最大可访问下表 | |||||
| ThisStep = 0;//从0到size-1 | |||||
| ThisCondition = Condition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return true; | |||||
| } | |||||
| void Unset() | |||||
| { | |||||
| Going = false; | |||||
| DestinationX = -1; | |||||
| DestinationY = -1; | |||||
| Way = { {} }; | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| } | |||||
| void Update() | |||||
| { | |||||
| if (!Going) | |||||
| return; | |||||
| if (ThisStep >= Way.size() - 1) | |||||
| { | |||||
| Unset(); | |||||
| return; | |||||
| } | |||||
| return; | |||||
| } | |||||
| bool MoveAStep() | |||||
| { | |||||
| Update(); | |||||
| if (!Going||Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| if ((LastGridX != -1 && Distance(LastGridX, LastGridY, api.GetSelfInfo()->x, api.GetSelfInfo()->y) < 20) | |||||
| || abs(Way[ThisStep + 1][0] - api.GridToCell(api.GetSelfInfo()->x)) + abs(Way[ThisStep + 1][1] - api.GridToCell(api.GetSelfInfo()->y)) > 5) | |||||
| { | |||||
| SetDestination(DestinationX, DestinationY, Condition1); | |||||
| return false; | |||||
| } | |||||
| auto p = MoveToNextBlock(api, Way[ThisStep + 1][0], Way[ThisStep + 1][1]); | |||||
| if (!p[0]) | |||||
| ThisStep++; | |||||
| LastGridX = api.GetSelfInfo()->x; | |||||
| LastGridY = api.GetSelfInfo()->y; | |||||
| Update(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| bool IfDone() | |||||
| { | |||||
| Update(); | |||||
| return !Going; | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| namespace Homework | |||||
| { | |||||
| struct Homework | |||||
| { | |||||
| int32_t x = -1; | |||||
| int32_t y = -1;//cell | |||||
| int32_t Progress = 0; | |||||
| bool IfBeingDone = false;//待实现 | |||||
| Homework() :x(-1), y(-1), Progress(0), IfBeingDone(false) {} | |||||
| }; | |||||
| enum class Direction | |||||
| { | |||||
| Left = 0, | |||||
| Right = 1, | |||||
| Up = 2, | |||||
| Down = 3, | |||||
| LeftUp = 4, | |||||
| LeftDown = 5, | |||||
| RightUp = 6, | |||||
| RightDown = 7 | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework); | |||||
| class GoToNearestHomework :public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| Homework ThisHomework; | |||||
| public: | |||||
| GoToNearestHomework(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), ThisHomework() {} | |||||
| GoToNearestHomework& operator=(GoToNearestHomework P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| ThisHomework = Homework(); | |||||
| return (*this); | |||||
| } | |||||
| Homework FindTheNearestHomework()//考虑实现对于多人走向同一份作业的规避,目前未实现 | |||||
| { | |||||
| Homework NearestHomework; | |||||
| int32_t DistanceOfTheNearestHomework = -1; | |||||
| int32_t NumberOfTheNearestHomework = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 10; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), HomeworkLocations[i][0], HomeworkLocations[i][1], { false,true,false,true,true,false,false,false,true,true,true,false }, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestHomework && api.GetClassroomProgress(HomeworkLocations[i][0], HomeworkLocations[i][1]) < 10000000)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| } | |||||
| if (NumberOfTheNearestHomework != -1) | |||||
| { | |||||
| NearestHomework.x = HomeworkLocations[NumberOfTheNearestHomework][0]; | |||||
| NearestHomework.y = HomeworkLocations[NumberOfTheNearestHomework][1]; | |||||
| NearestHomework.Progress = api.GetClassroomProgress(NearestHomework.x, NearestHomework.y); | |||||
| } | |||||
| return NearestHomework; | |||||
| } | |||||
| std::vector<bool> MoveAStepToNearestHomework(Direction ThisDirection)//第一个位置返回是否有最近作业,第二个位置返回是否成功 | |||||
| { | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| auto a = MoveAStep(); | |||||
| return { true, a }; | |||||
| } | |||||
| void UnsetHomework() | |||||
| { | |||||
| ThisHomework = Homework(); | |||||
| } | |||||
| std::vector<bool> FindAndDo(Direction ThisDirection = Direction::Right)//是否找到,是否赶到,是否完成 | |||||
| { | |||||
| if (api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| { | |||||
| UnsetHomework(); | |||||
| Unset(); | |||||
| } | |||||
| if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /*if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1); | |||||
| }*/ | |||||
| //MoveAStep(); | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return { true,false,false }; | |||||
| } | |||||
| else | |||||
| { | |||||
| Unset(); | |||||
| auto a = DoHomework(api, ThisHomework); | |||||
| return{ true,true,!a }; | |||||
| } | |||||
| } | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (abs(api.GridToCell(self->x) - ThisHomework.x) > 1 || abs(api.GridToCell(self->y) - ThisHomework.y) > 1 || api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| return false; | |||||
| auto State = api.StartLearning(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| namespace Grass | |||||
| { | |||||
| class MoveToGrass : public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| bool Escaping = false; | |||||
| public: | |||||
| MoveToGrass(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), Escaping(false) {} | |||||
| MoveToGrass& operator=(MoveToGrass P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| Escaping = false; | |||||
| return (*this); | |||||
| } | |||||
| std::vector<int32_t> FindTheNearestGrass() | |||||
| { | |||||
| int32_t DistanceOfTheNearestGrass = -1; | |||||
| int32_t NumberOfTheNearestGrass = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 13; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), GrassLocations[i][0], GrassLocations[i][1], Condition1, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestGrass)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| } | |||||
| return { GrassLocations[NumberOfTheNearestGrass][0],GrassLocations[NumberOfTheNearestGrass][1] }; | |||||
| } | |||||
| void FindAndGo() | |||||
| { | |||||
| if (!Escaping) | |||||
| { | |||||
| auto p = FindTheNearestGrass(); | |||||
| SetDestination(p[0], p[1], Condition1); | |||||
| Escaping = true; | |||||
| } | |||||
| else | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return; | |||||
| } | |||||
| else | |||||
| { | |||||
| return; | |||||
| } | |||||
| } | |||||
| void StopEscape() | |||||
| { | |||||
| Escaping = false; | |||||
| } | |||||
| }; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| static std::vector<std::vector<int>> a{ {24,6},{15,6},{15,17},{24,17} }; | |||||
| if (this->playerID == 0)//三教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| //玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3)//六教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,772 @@ | |||||
| //四学霸策略 | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #include "API.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = | |||||
| { | |||||
| THUAI6::StudentType::StraightAStudent , | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| //全局变量 | |||||
| int32_t HomeworkLocations[10][2]{ {8,31},{18,5},{10,38},{19,41},{22,18},{28,26},{30,7},{33,40},{40,12},{44,32}}; | |||||
| int32_t GrassLocations[13][2]{ {8,2},{8,16},{5,25},{4,29},{10,46},{18,46},{20,36},{15,29},{20,1},{30,25},{40,24},{39,35},{46,8} }; | |||||
| bool IfResetDestination = true; | |||||
| std::vector<bool> Condition1 = { false, true, false, true, false, false, false, false, true ,true ,true, false };//门都可以,草地,land可以 | |||||
| namespace MoveFunc | |||||
| { | |||||
| int32_t Distance(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return int32_t(sqrt((gridX0 - gridX1) * (gridX0 - gridX1) + (gridY0 - gridY1) * (gridY0 - gridY1))); | |||||
| } | |||||
| //角度为0指向1 | |||||
| double Angle(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return atan2(gridY1 - gridY0, gridX1 - gridX0); | |||||
| } | |||||
| namespace FindWay | |||||
| { | |||||
| int IfAchievable(THUAI6::PlaceType ThisPlace, std::vector<bool> Condition) | |||||
| { | |||||
| //是否可以通过的条件可以进一步完善 | |||||
| if ((ThisPlace == THUAI6::PlaceType::NullPlaceType && Condition[0]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Land && Condition[1]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Wall && Condition[2]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Grass && Condition[3]) | |||||
| || (ThisPlace == THUAI6::PlaceType::ClassRoom && Condition[4]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Gate && Condition[5]) | |||||
| || (ThisPlace == THUAI6::PlaceType::HiddenGate && Condition[6]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Window && Condition[7]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door3 && Condition[8]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door5 && Condition[9]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door6 && Condition[10]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Chest && Condition[11])) | |||||
| return 0; | |||||
| else | |||||
| return 1;//1不可达 | |||||
| } | |||||
| const int kCost1 = 10; //直移一格消耗 | |||||
| const int kCost2 = 14; //斜移一格消耗 | |||||
| struct Point | |||||
| { | |||||
| int x, y; //点坐标,这里为了方便按照C++的数组来计算,x代表横排,y代表竖列 | |||||
| int F, G, H; //F=G+H | |||||
| Point* parent; //parent的坐标,这里没有用指针,从而简化代码 | |||||
| Point(int _x, int _y) :x(_x), y(_y), F(0), G(0), H(0), parent(NULL) //变量初始化 | |||||
| { | |||||
| } | |||||
| }; | |||||
| class Astar | |||||
| { | |||||
| public: | |||||
| void InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& api); | |||||
| std::list<Point*> GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<bool> ThisCondition; | |||||
| Astar(IStudentAPI& _api) : | |||||
| maze({ {} }), api(_api), openList({}), closeList({}) {} | |||||
| private: | |||||
| Point* findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<Point*> getSurroundPoints(const Point* point, bool isIgnoreCorner) const; | |||||
| bool isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const; //判断某点是否可以用于下一步判断 | |||||
| Point* isInList(const std::list<Point*>& list, const Point* point) const; //判断开启/关闭列表中是否包含某点 | |||||
| Point* getLeastFpoint(); //从开启列表中返回F值最小的节点 | |||||
| //计算FGH值 | |||||
| int calcG(Point* temp_start, Point* point); | |||||
| int calcH(Point* point, Point* end); | |||||
| int calcF(Point* point); | |||||
| private: | |||||
| std::vector<std::vector<THUAI6::PlaceType>> maze; | |||||
| std::list<Point*> openList; //开启列表 | |||||
| std::list<Point*> closeList; //关闭列表 | |||||
| IStudentAPI& api; | |||||
| }; | |||||
| void Astar::InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& _api) | |||||
| { | |||||
| maze = _maze; | |||||
| ThisCondition = Condition; | |||||
| api = _api; | |||||
| } | |||||
| int Astar::calcG(Point* temp_start, Point* point) | |||||
| { | |||||
| int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2; | |||||
| int parentG = point->parent == NULL ? 0 : point->parent->G; //如果是初始节点,则其父节点是空 | |||||
| return parentG + extraG; | |||||
| } | |||||
| int Astar::calcH(Point* point, Point* end) | |||||
| { | |||||
| //用简单的欧几里得距离计算H,这个H的计算是关键,还有很多算法,没深入研究^_^ | |||||
| return sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1; | |||||
| } | |||||
| int Astar::calcF(Point* point) | |||||
| { | |||||
| return point->G + point->H; | |||||
| } | |||||
| Point* Astar::getLeastFpoint() | |||||
| { | |||||
| if (!openList.empty()) | |||||
| { | |||||
| auto resPoint = openList.front(); | |||||
| for (auto& point : openList) | |||||
| if (point->F < resPoint->F) | |||||
| resPoint = point; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| openList.push_back(new Point(startPoint.x, startPoint.y)); //置入起点,拷贝开辟一个节点,内外隔离 | |||||
| while (!openList.empty()) | |||||
| { | |||||
| auto curPoint = getLeastFpoint(); //找到F值最小的点 | |||||
| openList.remove(curPoint); //从开启列表中删除 | |||||
| closeList.push_back(curPoint); //放到关闭列表 | |||||
| //1,找到当前周围八个格中可以通过的格子 | |||||
| auto surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner); | |||||
| for (auto& target : surroundPoints) | |||||
| { | |||||
| //2,对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H | |||||
| if (!isInList(openList, target)) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = calcG(curPoint, target); | |||||
| target->H = calcH(target, &endPoint); | |||||
| target->F = calcF(target); | |||||
| openList.push_back(target); | |||||
| } | |||||
| //3,对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F | |||||
| else | |||||
| { | |||||
| int tempG = calcG(curPoint, target); | |||||
| if (tempG < target->G) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = tempG; | |||||
| target->F = calcF(target); | |||||
| } | |||||
| } | |||||
| Point* resPoint = isInList(openList, &endPoint); | |||||
| if (resPoint) | |||||
| return resPoint; //返回列表里的节点指针,不要用原来传入的endpoint指针,因为发生了深拷贝 | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| std::list<Point*> Astar::GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| Point* result = findPath(startPoint, endPoint, isIgnoreCorner); | |||||
| std::list<Point*> path; | |||||
| //返回路径,如果没找到路径,返回空链表 | |||||
| while (result) | |||||
| { | |||||
| path.push_front(result); | |||||
| result = result->parent; | |||||
| } | |||||
| // 清空临时开闭列表,防止重复执行GetPath导致结果异常 | |||||
| openList.clear(); | |||||
| closeList.clear(); | |||||
| return path; | |||||
| } | |||||
| Point* Astar::isInList(const std::list<Point*>& list, const Point* point) const | |||||
| { | |||||
| //判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标 | |||||
| for (auto p : list) | |||||
| if (p->x == point->x && p->y == point->y) | |||||
| return p; | |||||
| return NULL; | |||||
| } | |||||
| bool Astar::isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const | |||||
| { | |||||
| if ((target->x==api.GridToCell(api.GetSelfInfo()->x)&& target->y == api.GridToCell(api.GetSelfInfo()->y)) | |||||
| ||target->x<0 || target->x>maze.size() - 1 | |||||
| || target->y<0 || target->y>maze[0].size() - 1 | |||||
| || IfAchievable(maze[target->x][target->y], ThisCondition) == 1 | |||||
| || target->x == point->x && target->y == point->y | |||||
| || isInList(closeList, target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false | |||||
| return false; | |||||
| else | |||||
| { | |||||
| if (abs(point->x - target->x) + abs(point->y - target->y) == 1) //非斜角可以 | |||||
| return true; | |||||
| else | |||||
| { | |||||
| //斜对角要判断是否绊住 | |||||
| if (IfAchievable(maze[point->x][target->y], ThisCondition) == 0 && IfAchievable(maze[target->x][point->y], ThisCondition) == 0) | |||||
| return true; | |||||
| else | |||||
| return isIgnoreCorner; | |||||
| } | |||||
| } | |||||
| } | |||||
| std::vector<Point*> Astar::getSurroundPoints(const Point* point, bool isIgnoreCorner) const | |||||
| { | |||||
| std::vector<Point*> surroundPoints; | |||||
| for (int x = point->x - 1; x <= point->x + 1; x++) | |||||
| for (int y = point->y - 1; y <= point->y + 1; y++) | |||||
| if (isCanreach(point, new Point(x, y), isIgnoreCorner)) | |||||
| surroundPoints.push_back(new Point(x, y)); | |||||
| return surroundPoints; | |||||
| } | |||||
| bool InPath(const int& row, const int& col, const std::list<Point*>& path) { | |||||
| for (const auto& p : path) { | |||||
| if (row == p->x && col == p->y) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| std::vector<std::vector<int32_t>> findPath(const std::vector<std::vector<THUAI6::PlaceType>>& map, int32_t cellX0, int32_t cellY0, int32_t cellX1, int32_t cellY1, std::vector<bool> Condition, IStudentAPI& api) | |||||
| { | |||||
| Astar astar(api); | |||||
| astar.InitAstar(map, Condition,api); | |||||
| Point start(cellX0, cellY0); | |||||
| Point end(cellX1, cellY1); | |||||
| std::list<Point*> path = astar.GetPath(start, end, false); | |||||
| std::vector<std::vector<int>> result{}; | |||||
| for (auto P : path) | |||||
| { | |||||
| std::vector<int> Loc{ P->x,P->y }; | |||||
| result.push_back(Loc); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| namespace GoToSomePlace | |||||
| { | |||||
| std::vector<bool> MoveToNextBlock(IStudentAPI& api, int32_t cellX, int32_t cellY) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (Distance(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY)) < 50)//可以调整 | |||||
| return { false,true }; | |||||
| auto p = api.Move(50, Angle(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY))); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| if (!p.get()) | |||||
| return { false,false }; | |||||
| return { true,true }; | |||||
| }//可以修改移动时间 | |||||
| //不检查是否可达,谨慎使用 | |||||
| class GoToAPlace | |||||
| { | |||||
| protected: | |||||
| bool Going = false;//是否正在走 | |||||
| int32_t DestinationX = -1;//cell | |||||
| int32_t DestinationY = -1; | |||||
| std::vector<std::vector<int32_t>> Way{ {} };//没吃到 | |||||
| int32_t StepNumber = 0; | |||||
| int32_t ThisStep = 0; | |||||
| IStudentAPI& api; | |||||
| std::vector<bool> ThisCondition; | |||||
| int32_t LastGridX=-1; | |||||
| int32_t LastGridY=-1; | |||||
| public: | |||||
| GoToAPlace(IStudentAPI& _api) : | |||||
| Going(false), DestinationX(-1), DestinationY(-1), LastGridX(-1), LastGridY(-1), api(_api), StepNumber(0), ThisStep(0), Way({}), ThisCondition{ {} } {} | |||||
| GoToAPlace& operator =(GoToAPlace P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way={ {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api=P.api; | |||||
| ThisCondition=P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return (*this); | |||||
| } | |||||
| bool SetDestination(int32_t cellX, int32_t cellY, std::vector<bool> Condition) | |||||
| { | |||||
| if (api.GridToCell(api.GetSelfInfo()->x) == cellX && api.GridToCell(api.GetSelfInfo()->y) == cellY) | |||||
| Way = { {} }; | |||||
| else | |||||
| Way = FindWay::findPath(api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), cellX, cellY, Condition, api); | |||||
| if (Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| Going = true; | |||||
| DestinationX = cellX; | |||||
| DestinationY = cellY; | |||||
| StepNumber = Way.size();//size-1是最大可访问下表 | |||||
| ThisStep = 0;//从0到size-1 | |||||
| ThisCondition = Condition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return true; | |||||
| } | |||||
| void Unset() | |||||
| { | |||||
| Going = false; | |||||
| DestinationX = -1; | |||||
| DestinationY = -1; | |||||
| Way = { {} }; | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| } | |||||
| void Update() | |||||
| { | |||||
| if (!Going) | |||||
| return; | |||||
| if (ThisStep >= Way.size() - 1) | |||||
| { | |||||
| Unset(); | |||||
| return; | |||||
| } | |||||
| return; | |||||
| } | |||||
| bool MoveAStep() | |||||
| { | |||||
| Update(); | |||||
| if (!Going||Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| if ((LastGridX != -1 && Distance(LastGridX, LastGridY, api.GetSelfInfo()->x, api.GetSelfInfo()->y) < 20) | |||||
| || abs(Way[ThisStep + 1][0] - api.GridToCell(api.GetSelfInfo()->x)) + abs(Way[ThisStep + 1][1] - api.GridToCell(api.GetSelfInfo()->y)) > 5) | |||||
| { | |||||
| SetDestination(DestinationX, DestinationY, Condition1); | |||||
| return false; | |||||
| } | |||||
| auto p = MoveToNextBlock(api, Way[ThisStep + 1][0], Way[ThisStep + 1][1]); | |||||
| if (!p[0]) | |||||
| ThisStep++; | |||||
| LastGridX = api.GetSelfInfo()->x; | |||||
| LastGridY = api.GetSelfInfo()->y; | |||||
| Update(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| bool IfDone() | |||||
| { | |||||
| Update(); | |||||
| return !Going; | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| namespace Homework | |||||
| { | |||||
| struct Homework | |||||
| { | |||||
| int32_t x = -1; | |||||
| int32_t y = -1;//cell | |||||
| int32_t Progress = 0; | |||||
| bool IfBeingDone = false;//待实现 | |||||
| Homework() :x(-1), y(-1), Progress(0), IfBeingDone(false) {} | |||||
| }; | |||||
| enum class Direction | |||||
| { | |||||
| Left = 0, | |||||
| Right = 1, | |||||
| Up = 2, | |||||
| Down = 3, | |||||
| LeftUp = 4, | |||||
| LeftDown = 5, | |||||
| RightUp = 6, | |||||
| RightDown = 7 | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework); | |||||
| class GoToNearestHomework :public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| Homework ThisHomework; | |||||
| public: | |||||
| GoToNearestHomework(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), ThisHomework() {} | |||||
| GoToNearestHomework& operator=(GoToNearestHomework P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| ThisHomework = Homework(); | |||||
| return (*this); | |||||
| } | |||||
| Homework FindTheNearestHomework()//考虑实现对于多人走向同一份作业的规避,目前未实现 | |||||
| { | |||||
| Homework NearestHomework; | |||||
| int32_t DistanceOfTheNearestHomework = -1; | |||||
| int32_t NumberOfTheNearestHomework = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 10; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), HomeworkLocations[i][0], HomeworkLocations[i][1], { false,true,false,true,true,false,false,false,true,true,true,false }, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestHomework && api.GetClassroomProgress(HomeworkLocations[i][0], HomeworkLocations[i][1]) < 10000000)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| } | |||||
| if (NumberOfTheNearestHomework != -1) | |||||
| { | |||||
| NearestHomework.x = HomeworkLocations[NumberOfTheNearestHomework][0]; | |||||
| NearestHomework.y = HomeworkLocations[NumberOfTheNearestHomework][1]; | |||||
| NearestHomework.Progress = api.GetClassroomProgress(NearestHomework.x, NearestHomework.y); | |||||
| } | |||||
| return NearestHomework; | |||||
| } | |||||
| std::vector<bool> MoveAStepToNearestHomework(Direction ThisDirection)//第一个位置返回是否有最近作业,第二个位置返回是否成功 | |||||
| { | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| auto a = MoveAStep(); | |||||
| return { true, a }; | |||||
| } | |||||
| void UnsetHomework() | |||||
| { | |||||
| ThisHomework = Homework(); | |||||
| } | |||||
| std::vector<bool> FindAndDo(Direction ThisDirection = Direction::Right)//是否找到,是否赶到,是否完成 | |||||
| { | |||||
| if (api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| { | |||||
| UnsetHomework(); | |||||
| Unset(); | |||||
| } | |||||
| if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /*if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1); | |||||
| }*/ | |||||
| //MoveAStep(); | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return { true,false,false }; | |||||
| } | |||||
| else | |||||
| { | |||||
| Unset(); | |||||
| auto a = DoHomework(api, ThisHomework); | |||||
| return{ true,true,!a }; | |||||
| } | |||||
| } | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (abs(api.GridToCell(self->x) - ThisHomework.x) > 1 || abs(api.GridToCell(self->y) - ThisHomework.y) > 1 || api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| return false; | |||||
| auto State = api.StartLearning(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| namespace Grass | |||||
| { | |||||
| class MoveToGrass : public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| bool Escaping = false; | |||||
| public: | |||||
| MoveToGrass(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), Escaping(false) {} | |||||
| MoveToGrass& operator=(MoveToGrass P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| Escaping = false; | |||||
| return (*this); | |||||
| } | |||||
| std::vector<int32_t> FindTheNearestGrass() | |||||
| { | |||||
| int32_t DistanceOfTheNearestGrass = -1; | |||||
| int32_t NumberOfTheNearestGrass = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 13; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), GrassLocations[i][0], GrassLocations[i][1], Condition1, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestGrass)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| } | |||||
| return { GrassLocations[NumberOfTheNearestGrass][0],GrassLocations[NumberOfTheNearestGrass][1] }; | |||||
| } | |||||
| void FindAndGo() | |||||
| { | |||||
| if (!Escaping) | |||||
| { | |||||
| auto p = FindTheNearestGrass(); | |||||
| SetDestination(p[0], p[1], Condition1); | |||||
| Escaping = true; | |||||
| } | |||||
| else | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return; | |||||
| } | |||||
| else | |||||
| { | |||||
| return; | |||||
| } | |||||
| } | |||||
| void StopEscape() | |||||
| { | |||||
| Escaping = false; | |||||
| } | |||||
| }; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| static std::vector<std::vector<int>> a{ {24,6},{15,6},{15,17},{24,17} }; | |||||
| if (this->playerID == 0)//三教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| //玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3)//六教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,772 @@ | |||||
| //四学霸策略 | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #include "API.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = | |||||
| { | |||||
| THUAI6::StudentType::StraightAStudent , | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| //全局变量 | |||||
| int32_t HomeworkLocations[10][2]{ {8,31},{18,5},{10,38},{19,41},{22,18},{28,26},{30,7},{33,40},{40,12},{44,32}}; | |||||
| int32_t GrassLocations[13][2]{ {8,2},{8,16},{5,25},{4,29},{10,46},{18,46},{20,36},{15,29},{20,1},{30,25},{40,24},{39,35},{46,8} }; | |||||
| bool IfResetDestination = true; | |||||
| std::vector<bool> Condition1 = { false, true, false, true, false, false, false, false, true ,true ,true, false };//门都可以,草地,land可以 | |||||
| namespace MoveFunc | |||||
| { | |||||
| int32_t Distance(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return int32_t(sqrt((gridX0 - gridX1) * (gridX0 - gridX1) + (gridY0 - gridY1) * (gridY0 - gridY1))); | |||||
| } | |||||
| //角度为0指向1 | |||||
| double Angle(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return atan2(gridY1 - gridY0, gridX1 - gridX0); | |||||
| } | |||||
| namespace FindWay | |||||
| { | |||||
| int IfAchievable(THUAI6::PlaceType ThisPlace, std::vector<bool> Condition) | |||||
| { | |||||
| //是否可以通过的条件可以进一步完善 | |||||
| if ((ThisPlace == THUAI6::PlaceType::NullPlaceType && Condition[0]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Land && Condition[1]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Wall && Condition[2]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Grass && Condition[3]) | |||||
| || (ThisPlace == THUAI6::PlaceType::ClassRoom && Condition[4]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Gate && Condition[5]) | |||||
| || (ThisPlace == THUAI6::PlaceType::HiddenGate && Condition[6]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Window && Condition[7]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door3 && Condition[8]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door5 && Condition[9]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door6 && Condition[10]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Chest && Condition[11])) | |||||
| return 0; | |||||
| else | |||||
| return 1;//1不可达 | |||||
| } | |||||
| const int kCost1 = 10; //直移一格消耗 | |||||
| const int kCost2 = 14; //斜移一格消耗 | |||||
| struct Point | |||||
| { | |||||
| int x, y; //点坐标,这里为了方便按照C++的数组来计算,x代表横排,y代表竖列 | |||||
| int F, G, H; //F=G+H | |||||
| Point* parent; //parent的坐标,这里没有用指针,从而简化代码 | |||||
| Point(int _x, int _y) :x(_x), y(_y), F(0), G(0), H(0), parent(NULL) //变量初始化 | |||||
| { | |||||
| } | |||||
| }; | |||||
| class Astar | |||||
| { | |||||
| public: | |||||
| void InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& api); | |||||
| std::list<Point*> GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<bool> ThisCondition; | |||||
| Astar(IStudentAPI& _api) : | |||||
| maze({ {} }), api(_api), openList({}), closeList({}) {} | |||||
| private: | |||||
| Point* findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<Point*> getSurroundPoints(const Point* point, bool isIgnoreCorner) const; | |||||
| bool isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const; //判断某点是否可以用于下一步判断 | |||||
| Point* isInList(const std::list<Point*>& list, const Point* point) const; //判断开启/关闭列表中是否包含某点 | |||||
| Point* getLeastFpoint(); //从开启列表中返回F值最小的节点 | |||||
| //计算FGH值 | |||||
| int calcG(Point* temp_start, Point* point); | |||||
| int calcH(Point* point, Point* end); | |||||
| int calcF(Point* point); | |||||
| private: | |||||
| std::vector<std::vector<THUAI6::PlaceType>> maze; | |||||
| std::list<Point*> openList; //开启列表 | |||||
| std::list<Point*> closeList; //关闭列表 | |||||
| IStudentAPI& api; | |||||
| }; | |||||
| void Astar::InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, IStudentAPI& _api) | |||||
| { | |||||
| maze = _maze; | |||||
| ThisCondition = Condition; | |||||
| api = _api; | |||||
| } | |||||
| int Astar::calcG(Point* temp_start, Point* point) | |||||
| { | |||||
| int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2; | |||||
| int parentG = point->parent == NULL ? 0 : point->parent->G; //如果是初始节点,则其父节点是空 | |||||
| return parentG + extraG; | |||||
| } | |||||
| int Astar::calcH(Point* point, Point* end) | |||||
| { | |||||
| //用简单的欧几里得距离计算H,这个H的计算是关键,还有很多算法,没深入研究^_^ | |||||
| return sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1; | |||||
| } | |||||
| int Astar::calcF(Point* point) | |||||
| { | |||||
| return point->G + point->H; | |||||
| } | |||||
| Point* Astar::getLeastFpoint() | |||||
| { | |||||
| if (!openList.empty()) | |||||
| { | |||||
| auto resPoint = openList.front(); | |||||
| for (auto& point : openList) | |||||
| if (point->F < resPoint->F) | |||||
| resPoint = point; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| openList.push_back(new Point(startPoint.x, startPoint.y)); //置入起点,拷贝开辟一个节点,内外隔离 | |||||
| while (!openList.empty()) | |||||
| { | |||||
| auto curPoint = getLeastFpoint(); //找到F值最小的点 | |||||
| openList.remove(curPoint); //从开启列表中删除 | |||||
| closeList.push_back(curPoint); //放到关闭列表 | |||||
| //1,找到当前周围八个格中可以通过的格子 | |||||
| auto surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner); | |||||
| for (auto& target : surroundPoints) | |||||
| { | |||||
| //2,对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H | |||||
| if (!isInList(openList, target)) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = calcG(curPoint, target); | |||||
| target->H = calcH(target, &endPoint); | |||||
| target->F = calcF(target); | |||||
| openList.push_back(target); | |||||
| } | |||||
| //3,对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F | |||||
| else | |||||
| { | |||||
| int tempG = calcG(curPoint, target); | |||||
| if (tempG < target->G) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = tempG; | |||||
| target->F = calcF(target); | |||||
| } | |||||
| } | |||||
| Point* resPoint = isInList(openList, &endPoint); | |||||
| if (resPoint) | |||||
| return resPoint; //返回列表里的节点指针,不要用原来传入的endpoint指针,因为发生了深拷贝 | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| std::list<Point*> Astar::GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| Point* result = findPath(startPoint, endPoint, isIgnoreCorner); | |||||
| std::list<Point*> path; | |||||
| //返回路径,如果没找到路径,返回空链表 | |||||
| while (result) | |||||
| { | |||||
| path.push_front(result); | |||||
| result = result->parent; | |||||
| } | |||||
| // 清空临时开闭列表,防止重复执行GetPath导致结果异常 | |||||
| openList.clear(); | |||||
| closeList.clear(); | |||||
| return path; | |||||
| } | |||||
| Point* Astar::isInList(const std::list<Point*>& list, const Point* point) const | |||||
| { | |||||
| //判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标 | |||||
| for (auto p : list) | |||||
| if (p->x == point->x && p->y == point->y) | |||||
| return p; | |||||
| return NULL; | |||||
| } | |||||
| bool Astar::isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const | |||||
| { | |||||
| if ((target->x==api.GridToCell(api.GetSelfInfo()->x)&& target->y == api.GridToCell(api.GetSelfInfo()->y)) | |||||
| ||target->x<0 || target->x>maze.size() - 1 | |||||
| || target->y<0 || target->y>maze[0].size() - 1 | |||||
| || IfAchievable(maze[target->x][target->y], ThisCondition) == 1 | |||||
| || target->x == point->x && target->y == point->y | |||||
| || isInList(closeList, target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false | |||||
| return false; | |||||
| else | |||||
| { | |||||
| if (abs(point->x - target->x) + abs(point->y - target->y) == 1) //非斜角可以 | |||||
| return true; | |||||
| else | |||||
| { | |||||
| //斜对角要判断是否绊住 | |||||
| if (IfAchievable(maze[point->x][target->y], ThisCondition) == 0 && IfAchievable(maze[target->x][point->y], ThisCondition) == 0) | |||||
| return true; | |||||
| else | |||||
| return isIgnoreCorner; | |||||
| } | |||||
| } | |||||
| } | |||||
| std::vector<Point*> Astar::getSurroundPoints(const Point* point, bool isIgnoreCorner) const | |||||
| { | |||||
| std::vector<Point*> surroundPoints; | |||||
| for (int x = point->x - 1; x <= point->x + 1; x++) | |||||
| for (int y = point->y - 1; y <= point->y + 1; y++) | |||||
| if (isCanreach(point, new Point(x, y), isIgnoreCorner)) | |||||
| surroundPoints.push_back(new Point(x, y)); | |||||
| return surroundPoints; | |||||
| } | |||||
| bool InPath(const int& row, const int& col, const std::list<Point*>& path) { | |||||
| for (const auto& p : path) { | |||||
| if (row == p->x && col == p->y) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| std::vector<std::vector<int32_t>> findPath(const std::vector<std::vector<THUAI6::PlaceType>>& map, int32_t cellX0, int32_t cellY0, int32_t cellX1, int32_t cellY1, std::vector<bool> Condition, IStudentAPI& api) | |||||
| { | |||||
| Astar astar(api); | |||||
| astar.InitAstar(map, Condition,api); | |||||
| Point start(cellX0, cellY0); | |||||
| Point end(cellX1, cellY1); | |||||
| std::list<Point*> path = astar.GetPath(start, end, false); | |||||
| std::vector<std::vector<int>> result{}; | |||||
| for (auto P : path) | |||||
| { | |||||
| std::vector<int> Loc{ P->x,P->y }; | |||||
| result.push_back(Loc); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| namespace GoToSomePlace | |||||
| { | |||||
| std::vector<bool> MoveToNextBlock(IStudentAPI& api, int32_t cellX, int32_t cellY) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (Distance(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY)) < 50)//可以调整 | |||||
| return { false,true }; | |||||
| auto p = api.Move(50, Angle(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY))); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| if (!p.get()) | |||||
| return { false,false }; | |||||
| return { true,true }; | |||||
| }//可以修改移动时间 | |||||
| //不检查是否可达,谨慎使用 | |||||
| class GoToAPlace | |||||
| { | |||||
| protected: | |||||
| bool Going = false;//是否正在走 | |||||
| int32_t DestinationX = -1;//cell | |||||
| int32_t DestinationY = -1; | |||||
| std::vector<std::vector<int32_t>> Way{ {} };//没吃到 | |||||
| int32_t StepNumber = 0; | |||||
| int32_t ThisStep = 0; | |||||
| IStudentAPI& api; | |||||
| std::vector<bool> ThisCondition; | |||||
| int32_t LastGridX=-1; | |||||
| int32_t LastGridY=-1; | |||||
| public: | |||||
| GoToAPlace(IStudentAPI& _api) : | |||||
| Going(false), DestinationX(-1), DestinationY(-1), LastGridX(-1), LastGridY(-1), api(_api), StepNumber(0), ThisStep(0), Way({}), ThisCondition{ {} } {} | |||||
| GoToAPlace& operator =(GoToAPlace P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way={ {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api=P.api; | |||||
| ThisCondition=P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return (*this); | |||||
| } | |||||
| bool SetDestination(int32_t cellX, int32_t cellY, std::vector<bool> Condition) | |||||
| { | |||||
| if (api.GridToCell(api.GetSelfInfo()->x) == cellX && api.GridToCell(api.GetSelfInfo()->y) == cellY) | |||||
| Way = { {} }; | |||||
| else | |||||
| Way = FindWay::findPath(api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), cellX, cellY, Condition, api); | |||||
| if (Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| Going = true; | |||||
| DestinationX = cellX; | |||||
| DestinationY = cellY; | |||||
| StepNumber = Way.size();//size-1是最大可访问下表 | |||||
| ThisStep = 0;//从0到size-1 | |||||
| ThisCondition = Condition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return true; | |||||
| } | |||||
| void Unset() | |||||
| { | |||||
| Going = false; | |||||
| DestinationX = -1; | |||||
| DestinationY = -1; | |||||
| Way = { {} }; | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| } | |||||
| void Update() | |||||
| { | |||||
| if (!Going) | |||||
| return; | |||||
| if (ThisStep >= Way.size() - 1) | |||||
| { | |||||
| Unset(); | |||||
| return; | |||||
| } | |||||
| return; | |||||
| } | |||||
| bool MoveAStep() | |||||
| { | |||||
| Update(); | |||||
| if (!Going||Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| if ((LastGridX != -1 && Distance(LastGridX, LastGridY, api.GetSelfInfo()->x, api.GetSelfInfo()->y) < 20) | |||||
| || abs(Way[ThisStep + 1][0] - api.GridToCell(api.GetSelfInfo()->x)) + abs(Way[ThisStep + 1][1] - api.GridToCell(api.GetSelfInfo()->y)) > 5) | |||||
| { | |||||
| SetDestination(DestinationX, DestinationY, Condition1); | |||||
| return false; | |||||
| } | |||||
| auto p = MoveToNextBlock(api, Way[ThisStep + 1][0], Way[ThisStep + 1][1]); | |||||
| if (!p[0]) | |||||
| ThisStep++; | |||||
| LastGridX = api.GetSelfInfo()->x; | |||||
| LastGridY = api.GetSelfInfo()->y; | |||||
| Update(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| bool IfDone() | |||||
| { | |||||
| Update(); | |||||
| return !Going; | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| namespace Homework | |||||
| { | |||||
| struct Homework | |||||
| { | |||||
| int32_t x = -1; | |||||
| int32_t y = -1;//cell | |||||
| int32_t Progress = 0; | |||||
| bool IfBeingDone = false;//待实现 | |||||
| Homework() :x(-1), y(-1), Progress(0), IfBeingDone(false) {} | |||||
| }; | |||||
| enum class Direction | |||||
| { | |||||
| Left = 0, | |||||
| Right = 1, | |||||
| Up = 2, | |||||
| Down = 3, | |||||
| LeftUp = 4, | |||||
| LeftDown = 5, | |||||
| RightUp = 6, | |||||
| RightDown = 7 | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework); | |||||
| class GoToNearestHomework :public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| Homework ThisHomework; | |||||
| public: | |||||
| GoToNearestHomework(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), ThisHomework() {} | |||||
| GoToNearestHomework& operator=(GoToNearestHomework P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| ThisHomework = Homework(); | |||||
| return (*this); | |||||
| } | |||||
| Homework FindTheNearestHomework()//考虑实现对于多人走向同一份作业的规避,目前未实现 | |||||
| { | |||||
| Homework NearestHomework; | |||||
| int32_t DistanceOfTheNearestHomework = -1; | |||||
| int32_t NumberOfTheNearestHomework = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 10; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), HomeworkLocations[i][0], HomeworkLocations[i][1], { false,true,false,true,true,false,false,false,true,true,true,false }, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestHomework && api.GetClassroomProgress(HomeworkLocations[i][0], HomeworkLocations[i][1]) < 10000000)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestHomework = D; | |||||
| NumberOfTheNearestHomework = i; | |||||
| } | |||||
| } | |||||
| if (NumberOfTheNearestHomework != -1) | |||||
| { | |||||
| NearestHomework.x = HomeworkLocations[NumberOfTheNearestHomework][0]; | |||||
| NearestHomework.y = HomeworkLocations[NumberOfTheNearestHomework][1]; | |||||
| NearestHomework.Progress = api.GetClassroomProgress(NearestHomework.x, NearestHomework.y); | |||||
| } | |||||
| return NearestHomework; | |||||
| } | |||||
| std::vector<bool> MoveAStepToNearestHomework(Direction ThisDirection)//第一个位置返回是否有最近作业,第二个位置返回是否成功 | |||||
| { | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| auto a = MoveAStep(); | |||||
| return { true, a }; | |||||
| } | |||||
| void UnsetHomework() | |||||
| { | |||||
| ThisHomework = Homework(); | |||||
| } | |||||
| std::vector<bool> FindAndDo(Direction ThisDirection = Direction::Right)//是否找到,是否赶到,是否完成 | |||||
| { | |||||
| if (api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| { | |||||
| UnsetHomework(); | |||||
| Unset(); | |||||
| } | |||||
| if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| if (ThisHomework.x == -1) | |||||
| return { false,false,false }; | |||||
| switch (ThisDirection) | |||||
| { | |||||
| case Direction::Left: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::Right: | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::Up: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::Down: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| break; | |||||
| case Direction::LeftUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::LeftDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| break; | |||||
| case Direction::RightUp: | |||||
| SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| case Direction::RightDown: | |||||
| SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /*if (ThisHomework.x == -1) | |||||
| { | |||||
| ThisHomework = FindTheNearestHomework(); | |||||
| SetDestination(ThisHomework.x, ThisHomework.y + 1); | |||||
| }*/ | |||||
| //MoveAStep(); | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return { true,false,false }; | |||||
| } | |||||
| else | |||||
| { | |||||
| Unset(); | |||||
| auto a = DoHomework(api, ThisHomework); | |||||
| return{ true,true,!a }; | |||||
| } | |||||
| } | |||||
| }; | |||||
| bool DoHomework(IStudentAPI& api, Homework ThisHomework) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (abs(api.GridToCell(self->x) - ThisHomework.x) > 1 || abs(api.GridToCell(self->y) - ThisHomework.y) > 1 || api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| return false; | |||||
| auto State = api.StartLearning(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| namespace Grass | |||||
| { | |||||
| class MoveToGrass : public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| { | |||||
| private: | |||||
| bool Escaping = false; | |||||
| public: | |||||
| MoveToGrass(IStudentAPI& _api) : | |||||
| GoToAPlace(_api), Escaping(false) {} | |||||
| MoveToGrass& operator=(MoveToGrass P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| Escaping = false; | |||||
| return (*this); | |||||
| } | |||||
| std::vector<int32_t> FindTheNearestGrass() | |||||
| { | |||||
| int32_t DistanceOfTheNearestGrass = -1; | |||||
| int32_t NumberOfTheNearestGrass = -1; | |||||
| //int32_t i = 0;//遍历次数 | |||||
| for (int32_t i = 0; i < 13; i++) | |||||
| { | |||||
| int32_t D = MoveFunc::FindWay::findPath | |||||
| (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), GrassLocations[i][0], GrassLocations[i][1], Condition1, api).size(); | |||||
| if (i == 0) | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| else if (D < DistanceOfTheNearestGrass)//待实现无人正在写 | |||||
| { | |||||
| DistanceOfTheNearestGrass = D; | |||||
| NumberOfTheNearestGrass = i; | |||||
| } | |||||
| } | |||||
| return { GrassLocations[NumberOfTheNearestGrass][0],GrassLocations[NumberOfTheNearestGrass][1] }; | |||||
| } | |||||
| void FindAndGo() | |||||
| { | |||||
| if (!Escaping) | |||||
| { | |||||
| auto p = FindTheNearestGrass(); | |||||
| SetDestination(p[0], p[1], Condition1); | |||||
| Escaping = true; | |||||
| } | |||||
| else | |||||
| if (!IfDone()) | |||||
| { | |||||
| MoveAStep(); | |||||
| return; | |||||
| } | |||||
| else | |||||
| { | |||||
| return; | |||||
| } | |||||
| } | |||||
| void StopEscape() | |||||
| { | |||||
| Escaping = false; | |||||
| } | |||||
| }; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| static std::vector<std::vector<int>> a{ {24,6},{15,6},{15,17},{24,17} }; | |||||
| if (this->playerID == 0)//三教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| //玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2)//外边 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3)//六教 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| } | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,871 @@ | |||||
| //四学霸策略 | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #include "API.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = | |||||
| { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent | |||||
| }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::ANoisyPerson; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| //全局变量 | |||||
| int32_t HomeworkLocations[10][2]{ {8,31},{18,5},{10,38},{19,41},{22,18},{28,26},{30,7},{33,40},{40,12},{44,32} }; | |||||
| int32_t GrassLocations[13][2]{ {8,2},{8,16},{5,25},{4,29},{10,46},{18,46},{20,36},{15,29},{20,1},{30,25},{40,24},{39,35},{46,8} }; | |||||
| bool IfResetDestination = true; | |||||
| std::vector<bool> Condition1 = { false, true, false, true, false, false, false, false, true ,true ,true, false };//门都可以,草地,land可以 | |||||
| namespace MoveFunc | |||||
| { | |||||
| int32_t Distance(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return int32_t(sqrt((gridX0 - gridX1) * (gridX0 - gridX1) + (gridY0 - gridY1) * (gridY0 - gridY1))); | |||||
| } | |||||
| //角度为0指向1 | |||||
| double Angle(int32_t gridX0, int32_t gridY0, int32_t gridX1, int32_t gridY1) | |||||
| { | |||||
| return atan2(gridY1 - gridY0, gridX1 - gridX0); | |||||
| } | |||||
| namespace FindWay | |||||
| { | |||||
| int IfAchievable(THUAI6::PlaceType ThisPlace, std::vector<bool> Condition) | |||||
| { | |||||
| //是否可以通过的条件可以进一步完善 | |||||
| if ((ThisPlace == THUAI6::PlaceType::NullPlaceType && Condition[0]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Land && Condition[1]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Wall && Condition[2]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Grass && Condition[3]) | |||||
| || (ThisPlace == THUAI6::PlaceType::ClassRoom && Condition[4]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Gate && Condition[5]) | |||||
| || (ThisPlace == THUAI6::PlaceType::HiddenGate && Condition[6]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Window && Condition[7]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door3 && Condition[8]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door5 && Condition[9]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Door6 && Condition[10]) | |||||
| || (ThisPlace == THUAI6::PlaceType::Chest && Condition[11])) | |||||
| return 0; | |||||
| else | |||||
| return 1;//1不可达 | |||||
| } | |||||
| const int kCost1 = 10; //直移一格消耗 | |||||
| const int kCost2 = 14; //斜移一格消耗 | |||||
| struct Point | |||||
| { | |||||
| int x, y; //点坐标,这里为了方便按照C++的数组来计算,x代表横排,y代表竖列 | |||||
| int F, G, H; //F=G+H | |||||
| Point* parent; //parent的坐标,这里没有用指针,从而简化代码 | |||||
| Point(int _x, int _y) :x(_x), y(_y), F(0), G(0), H(0), parent(NULL) //变量初始化 | |||||
| { | |||||
| } | |||||
| }; | |||||
| class Astar | |||||
| { | |||||
| public: | |||||
| void InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, ITrickerAPI& api); | |||||
| std::list<Point*> GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<bool> ThisCondition; | |||||
| Astar(ITrickerAPI& _api) : | |||||
| maze({ {} }), api(_api), openList({}), closeList({}) {} | |||||
| private: | |||||
| Point* findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner); | |||||
| std::vector<Point*> getSurroundPoints(const Point* point, bool isIgnoreCorner) const; | |||||
| bool isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const; //判断某点是否可以用于下一步判断 | |||||
| Point* isInList(const std::list<Point*>& list, const Point* point) const; //判断开启/关闭列表中是否包含某点 | |||||
| Point* getLeastFpoint(); //从开启列表中返回F值最小的节点 | |||||
| //计算FGH值 | |||||
| int calcG(Point* temp_start, Point* point); | |||||
| int calcH(Point* point, Point* end); | |||||
| int calcF(Point* point); | |||||
| private: | |||||
| std::vector<std::vector<THUAI6::PlaceType>> maze; | |||||
| std::list<Point*> openList; //开启列表 | |||||
| std::list<Point*> closeList; //关闭列表 | |||||
| ITrickerAPI& api; | |||||
| }; | |||||
| void Astar::InitAstar(const std::vector<std::vector<THUAI6::PlaceType>>& _maze, std::vector<bool> Condition, ITrickerAPI& _api) | |||||
| { | |||||
| maze = _maze; | |||||
| ThisCondition = Condition; | |||||
| api = _api; | |||||
| } | |||||
| int Astar::calcG(Point* temp_start, Point* point) | |||||
| { | |||||
| int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2; | |||||
| int parentG = point->parent == NULL ? 0 : point->parent->G; //如果是初始节点,则其父节点是空 | |||||
| return parentG + extraG; | |||||
| } | |||||
| int Astar::calcH(Point* point, Point* end) | |||||
| { | |||||
| //用简单的欧几里得距离计算H,这个H的计算是关键,还有很多算法,没深入研究^_^ | |||||
| return sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1; | |||||
| } | |||||
| int Astar::calcF(Point* point) | |||||
| { | |||||
| return point->G + point->H; | |||||
| } | |||||
| Point* Astar::getLeastFpoint() | |||||
| { | |||||
| if (!openList.empty()) | |||||
| { | |||||
| auto resPoint = openList.front(); | |||||
| for (auto& point : openList) | |||||
| if (point->F < resPoint->F) | |||||
| resPoint = point; | |||||
| return resPoint; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| Point* Astar::findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| openList.push_back(new Point(startPoint.x, startPoint.y)); //置入起点,拷贝开辟一个节点,内外隔离 | |||||
| while (!openList.empty()) | |||||
| { | |||||
| auto curPoint = getLeastFpoint(); //找到F值最小的点 | |||||
| openList.remove(curPoint); //从开启列表中删除 | |||||
| closeList.push_back(curPoint); //放到关闭列表 | |||||
| //1,找到当前周围八个格中可以通过的格子 | |||||
| auto surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner); | |||||
| for (auto& target : surroundPoints) | |||||
| { | |||||
| //2,对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H | |||||
| if (!isInList(openList, target)) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = calcG(curPoint, target); | |||||
| target->H = calcH(target, &endPoint); | |||||
| target->F = calcF(target); | |||||
| openList.push_back(target); | |||||
| } | |||||
| //3,对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F | |||||
| else | |||||
| { | |||||
| int tempG = calcG(curPoint, target); | |||||
| if (tempG < target->G) | |||||
| { | |||||
| target->parent = curPoint; | |||||
| target->G = tempG; | |||||
| target->F = calcF(target); | |||||
| } | |||||
| } | |||||
| Point* resPoint = isInList(openList, &endPoint); | |||||
| if (resPoint) | |||||
| return resPoint; //返回列表里的节点指针,不要用原来传入的endpoint指针,因为发生了深拷贝 | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| std::list<Point*> Astar::GetPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner) | |||||
| { | |||||
| Point* result = findPath(startPoint, endPoint, isIgnoreCorner); | |||||
| std::list<Point*> path; | |||||
| //返回路径,如果没找到路径,返回空链表 | |||||
| while (result) | |||||
| { | |||||
| path.push_front(result); | |||||
| result = result->parent; | |||||
| } | |||||
| // 清空临时开闭列表,防止重复执行GetPath导致结果异常 | |||||
| openList.clear(); | |||||
| closeList.clear(); | |||||
| return path; | |||||
| } | |||||
| Point* Astar::isInList(const std::list<Point*>& list, const Point* point) const | |||||
| { | |||||
| //判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标 | |||||
| for (auto p : list) | |||||
| if (p->x == point->x && p->y == point->y) | |||||
| return p; | |||||
| return NULL; | |||||
| } | |||||
| bool Astar::isCanreach(const Point* point, const Point* target, bool isIgnoreCorner) const | |||||
| { | |||||
| if ((target->x == api.GridToCell(api.GetSelfInfo()->x) && target->y == api.GridToCell(api.GetSelfInfo()->y)) | |||||
| || target->x<0 || target->x>maze.size() - 1 | |||||
| || target->y<0 || target->y>maze[0].size() - 1 | |||||
| || IfAchievable(maze[target->x][target->y], ThisCondition) == 1 | |||||
| || target->x == point->x && target->y == point->y | |||||
| || isInList(closeList, target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false | |||||
| return false; | |||||
| else | |||||
| { | |||||
| if (abs(point->x - target->x) + abs(point->y - target->y) == 1) //非斜角可以 | |||||
| return true; | |||||
| else | |||||
| { | |||||
| //斜对角要判断是否绊住 | |||||
| if (IfAchievable(maze[point->x][target->y], ThisCondition) == 0 && IfAchievable(maze[target->x][point->y], ThisCondition) == 0) | |||||
| return true; | |||||
| else | |||||
| return isIgnoreCorner; | |||||
| } | |||||
| } | |||||
| } | |||||
| std::vector<Point*> Astar::getSurroundPoints(const Point* point, bool isIgnoreCorner) const | |||||
| { | |||||
| std::vector<Point*> surroundPoints; | |||||
| for (int x = point->x - 1; x <= point->x + 1; x++) | |||||
| for (int y = point->y - 1; y <= point->y + 1; y++) | |||||
| if (isCanreach(point, new Point(x, y), isIgnoreCorner)) | |||||
| surroundPoints.push_back(new Point(x, y)); | |||||
| return surroundPoints; | |||||
| } | |||||
| bool InPath(const int& row, const int& col, const std::list<Point*>& path) { | |||||
| for (const auto& p : path) { | |||||
| if (row == p->x && col == p->y) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| std::vector<std::vector<int32_t>> findPath(const std::vector<std::vector<THUAI6::PlaceType>>& map, int32_t cellX0, int32_t cellY0, int32_t cellX1, int32_t cellY1, std::vector<bool> Condition, ITrickerAPI& api) | |||||
| { | |||||
| Astar astar(api); | |||||
| astar.InitAstar(map, Condition, api); | |||||
| Point start(cellX0, cellY0); | |||||
| Point end(cellX1, cellY1); | |||||
| std::list<Point*> path = astar.GetPath(start, end, false); | |||||
| std::vector<std::vector<int>> result{}; | |||||
| for (auto P : path) | |||||
| { | |||||
| std::vector<int> Loc{ P->x,P->y }; | |||||
| result.push_back(Loc); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| namespace GoToSomePlace | |||||
| { | |||||
| std::vector<bool> MoveToNextBlock(ITrickerAPI& api, int32_t cellX, int32_t cellY) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| if (Distance(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY)) < 60)//可以调整 | |||||
| return { false,true }; | |||||
| auto p = api.Move(50, Angle(self->x, self->y, api.CellToGrid(cellX), api.CellToGrid(cellY))); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| api.EndAllAction(); | |||||
| if (!p.get()) | |||||
| return { false,false }; | |||||
| return { true,true }; | |||||
| }//可以修改移动时间 | |||||
| //不检查是否可达,谨慎使用 | |||||
| class GoToAPlace | |||||
| { | |||||
| protected: | |||||
| bool Going = false;//是否正在走 | |||||
| int32_t DestinationX = -1;//cell | |||||
| int32_t DestinationY = -1; | |||||
| std::vector<std::vector<int32_t>> Way{ {} };//没吃到 | |||||
| int32_t StepNumber = 0; | |||||
| int32_t ThisStep = 0; | |||||
| ITrickerAPI& api; | |||||
| std::vector<bool> ThisCondition; | |||||
| int32_t LastGridX = -1; | |||||
| int32_t LastGridY = -1; | |||||
| public: | |||||
| GoToAPlace(ITrickerAPI& _api) : | |||||
| Going(false), DestinationX(-1), DestinationY(-1), LastGridX(-1), LastGridY(-1), api(_api), StepNumber(0), ThisStep(0), Way({}), ThisCondition{ {} } {} | |||||
| GoToAPlace& operator =(GoToAPlace P) | |||||
| { | |||||
| bool Going = false;//是否正在走 | |||||
| DestinationX = -1;//cell | |||||
| DestinationY = -1; | |||||
| Way = { {} };//没吃到 | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| api = P.api; | |||||
| ThisCondition = P.ThisCondition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return (*this); | |||||
| } | |||||
| bool SetDestination(int32_t cellX, int32_t cellY, std::vector<bool> Condition) | |||||
| { | |||||
| if (api.GridToCell(api.GetSelfInfo()->x) == cellX && api.GridToCell(api.GetSelfInfo()->y) == cellY) | |||||
| Way = { {} }; | |||||
| else | |||||
| Way = FindWay::findPath(api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), cellX, cellY, Condition, api); | |||||
| if (Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| Going = true; | |||||
| DestinationX = cellX; | |||||
| DestinationY = cellY; | |||||
| StepNumber = Way.size();//size-1是最大可访问下表 | |||||
| ThisStep = 0;//从0到size-1 | |||||
| ThisCondition = Condition; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| return true; | |||||
| } | |||||
| void Unset() | |||||
| { | |||||
| Going = false; | |||||
| DestinationX = -1; | |||||
| DestinationY = -1; | |||||
| Way = { {} }; | |||||
| StepNumber = 0; | |||||
| ThisStep = 0; | |||||
| LastGridX = -1; | |||||
| LastGridY = -1; | |||||
| } | |||||
| void Update() | |||||
| { | |||||
| if (!Going) | |||||
| return; | |||||
| if (ThisStep >= Way.size() - 1) | |||||
| { | |||||
| Unset(); | |||||
| return; | |||||
| } | |||||
| return; | |||||
| } | |||||
| bool MoveAStep() | |||||
| { | |||||
| Update(); | |||||
| if (!Going || Way.empty()) | |||||
| { | |||||
| Unset(); | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| if ((LastGridX != -1 && Distance(LastGridX, LastGridY, api.GetSelfInfo()->x, api.GetSelfInfo()->y) < 20) | |||||
| || abs(Way[ThisStep + 1][0] - api.GridToCell(api.GetSelfInfo()->x)) + abs(Way[ThisStep + 1][1] - api.GridToCell(api.GetSelfInfo()->y)) > 5) | |||||
| { | |||||
| SetDestination(DestinationX, DestinationY, Condition1); | |||||
| return false; | |||||
| } | |||||
| auto p = MoveToNextBlock(api, Way[ThisStep + 1][0], Way[ThisStep + 1][1]); | |||||
| if (!p[0]) | |||||
| ThisStep++; | |||||
| LastGridX = api.GetSelfInfo()->x; | |||||
| LastGridY = api.GetSelfInfo()->y; | |||||
| Update(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| bool IfDone() | |||||
| { | |||||
| Update(); | |||||
| return !Going; | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| //namespace Homework | |||||
| //{ | |||||
| // struct Homework | |||||
| // { | |||||
| // | |||||
| // int32_t x = -1; | |||||
| // int32_t y = -1;//cell | |||||
| // int32_t Progress = 0; | |||||
| // bool IfBeingDone = false;//待实现 | |||||
| // Homework() :x(-1), y(-1), Progress(0), IfBeingDone(false) {} | |||||
| // }; | |||||
| // | |||||
| // enum class Direction | |||||
| // { | |||||
| // Left = 0, | |||||
| // Right = 1, | |||||
| // Up = 2, | |||||
| // Down = 3, | |||||
| // LeftUp = 4, | |||||
| // LeftDown = 5, | |||||
| // RightUp = 6, | |||||
| // RightDown = 7 | |||||
| // }; | |||||
| // | |||||
| // bool DoHomework(IStudentAPI& api, Homework ThisHomework); | |||||
| // | |||||
| // class GoToNearestHomework :public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| // { | |||||
| // private: | |||||
| // Homework ThisHomework; | |||||
| // public: | |||||
| // GoToNearestHomework(IStudentAPI& _api) : | |||||
| // GoToAPlace(_api), ThisHomework() {} | |||||
| // | |||||
| // GoToNearestHomework& operator=(GoToNearestHomework P) | |||||
| // { | |||||
| // bool Going = false;//是否正在走 | |||||
| // DestinationX = -1;//cell | |||||
| // DestinationY = -1; | |||||
| // Way = { {} };//没吃到 | |||||
| // StepNumber = 0; | |||||
| // ThisStep = 0; | |||||
| // api = P.api; | |||||
| // ThisCondition = P.ThisCondition; | |||||
| // LastGridX = -1; | |||||
| // LastGridY = -1; | |||||
| // ThisHomework = Homework(); | |||||
| // return (*this); | |||||
| // } | |||||
| // | |||||
| // Homework FindTheNearestHomework()//考虑实现对于多人走向同一份作业的规避,目前未实现 | |||||
| // { | |||||
| // Homework NearestHomework; | |||||
| // int32_t DistanceOfTheNearestHomework = -1; | |||||
| // int32_t NumberOfTheNearestHomework = -1; | |||||
| // //int32_t i = 0;//遍历次数 | |||||
| // | |||||
| // for (int32_t i = 0; i < 10; i++) | |||||
| // { | |||||
| // int32_t D = MoveFunc::FindWay::findPath | |||||
| // (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), HomeworkLocations[i][0], HomeworkLocations[i][1], { false,true,false,true,true,false,false,false,true,true,true,false }, api).size(); | |||||
| // if (i == 0) | |||||
| // { | |||||
| // DistanceOfTheNearestHomework = D; | |||||
| // NumberOfTheNearestHomework = i; | |||||
| // } | |||||
| // else if (D < DistanceOfTheNearestHomework && api.GetClassroomProgress(HomeworkLocations[i][0], HomeworkLocations[i][1]) < 10000000)//待实现无人正在写 | |||||
| // { | |||||
| // DistanceOfTheNearestHomework = D; | |||||
| // NumberOfTheNearestHomework = i; | |||||
| // } | |||||
| // } | |||||
| // if (NumberOfTheNearestHomework != -1) | |||||
| // { | |||||
| // NearestHomework.x = HomeworkLocations[NumberOfTheNearestHomework][0]; | |||||
| // NearestHomework.y = HomeworkLocations[NumberOfTheNearestHomework][1]; | |||||
| // NearestHomework.Progress = api.GetClassroomProgress(NearestHomework.x, NearestHomework.y); | |||||
| // } | |||||
| // return NearestHomework; | |||||
| // } | |||||
| // | |||||
| // std::vector<bool> MoveAStepToNearestHomework(Direction ThisDirection)//第一个位置返回是否有最近作业,第二个位置返回是否成功 | |||||
| // { | |||||
| // if (ThisHomework.x == -1) | |||||
| // return { false,false }; | |||||
| // switch (ThisDirection) | |||||
| // { | |||||
| // case Direction::Left: | |||||
| // SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| // break; | |||||
| // case Direction::Right: | |||||
| // SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| // break; | |||||
| // case Direction::Up: | |||||
| // SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| // break; | |||||
| // case Direction::Down: | |||||
| // SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| // break; | |||||
| // case Direction::LeftUp: | |||||
| // SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| // break; | |||||
| // case Direction::LeftDown: | |||||
| // SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| // break; | |||||
| // case Direction::RightUp: | |||||
| // SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| // break; | |||||
| // case Direction::RightDown: | |||||
| // SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| // break; | |||||
| // } | |||||
| // auto a = MoveAStep(); | |||||
| // return { true, a }; | |||||
| // } | |||||
| // | |||||
| // void UnsetHomework() | |||||
| // { | |||||
| // ThisHomework = Homework(); | |||||
| // } | |||||
| // | |||||
| // std::vector<bool> FindAndDo(Direction ThisDirection = Direction::Right)//是否找到,是否赶到,是否完成 | |||||
| // { | |||||
| // if (api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| // { | |||||
| // UnsetHomework(); | |||||
| // Unset(); | |||||
| // } | |||||
| // if (ThisHomework.x == -1) | |||||
| // { | |||||
| // ThisHomework = FindTheNearestHomework(); | |||||
| // if (ThisHomework.x == -1) | |||||
| // return { false,false,false }; | |||||
| // switch (ThisDirection) | |||||
| // { | |||||
| // case Direction::Left: | |||||
| // SetDestination(ThisHomework.x, ThisHomework.y - 1, Condition1); | |||||
| // break; | |||||
| // case Direction::Right: | |||||
| // SetDestination(ThisHomework.x, ThisHomework.y + 1, Condition1); | |||||
| // break; | |||||
| // case Direction::Up: | |||||
| // SetDestination(ThisHomework.x - 1, ThisHomework.y, Condition1); | |||||
| // break; | |||||
| // case Direction::Down: | |||||
| // SetDestination(ThisHomework.x + 1, ThisHomework.y, Condition1); | |||||
| // break; | |||||
| // case Direction::LeftUp: | |||||
| // SetDestination(ThisHomework.x - 1, ThisHomework.y - 1, Condition1); | |||||
| // break; | |||||
| // case Direction::LeftDown: | |||||
| // SetDestination(ThisHomework.x + 1, ThisHomework.y - 1, Condition1); | |||||
| // break; | |||||
| // case Direction::RightUp: | |||||
| // SetDestination(ThisHomework.x - 1, ThisHomework.y + 1, Condition1); | |||||
| // break; | |||||
| // case Direction::RightDown: | |||||
| // SetDestination(ThisHomework.x + 1, ThisHomework.y + 1, Condition1); | |||||
| // break; | |||||
| // } | |||||
| // } | |||||
| // /*if (ThisHomework.x == -1) | |||||
| // { | |||||
| // ThisHomework = FindTheNearestHomework(); | |||||
| // SetDestination(ThisHomework.x, ThisHomework.y + 1); | |||||
| // }*/ | |||||
| // //MoveAStep(); | |||||
| // if (!IfDone()) | |||||
| // { | |||||
| // MoveAStep(); | |||||
| // return { true,false,false }; | |||||
| // } | |||||
| // else | |||||
| // { | |||||
| // Unset(); | |||||
| // auto a = DoHomework(api, ThisHomework); | |||||
| // return{ true,true,!a }; | |||||
| // } | |||||
| // } | |||||
| // }; | |||||
| // | |||||
| // bool DoHomework(IStudentAPI& api, Homework ThisHomework) | |||||
| // { | |||||
| // auto self = api.GetSelfInfo(); | |||||
| // if (abs(api.GridToCell(self->x) - ThisHomework.x) > 1 || abs(api.GridToCell(self->y) - ThisHomework.y) > 1 || api.GetClassroomProgress(ThisHomework.x, ThisHomework.y) >= 10000000) | |||||
| // return false; | |||||
| // auto State = api.StartLearning(); | |||||
| // std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||||
| // api.EndAllAction(); | |||||
| // return true; | |||||
| // } | |||||
| //} | |||||
| // | |||||
| // | |||||
| //namespace Grass | |||||
| //{ | |||||
| // class MoveToGrass : public MoveFunc::GoToSomePlace::GoToAPlace | |||||
| // { | |||||
| // private: | |||||
| // bool Escaping = false; | |||||
| // public: | |||||
| // MoveToGrass(IStudentAPI& _api) : | |||||
| // GoToAPlace(_api), Escaping(false) {} | |||||
| // | |||||
| // MoveToGrass& operator=(MoveToGrass P) | |||||
| // { | |||||
| // bool Going = false;//是否正在走 | |||||
| // DestinationX = -1;//cell | |||||
| // DestinationY = -1; | |||||
| // Way = { {} };//没吃到 | |||||
| // StepNumber = 0; | |||||
| // ThisStep = 0; | |||||
| // api = P.api; | |||||
| // ThisCondition = P.ThisCondition; | |||||
| // LastGridX = -1; | |||||
| // LastGridY = -1; | |||||
| // Escaping = false; | |||||
| // return (*this); | |||||
| // } | |||||
| // | |||||
| // std::vector<int32_t> FindTheNearestGrass() | |||||
| // { | |||||
| // int32_t DistanceOfTheNearestGrass = -1; | |||||
| // int32_t NumberOfTheNearestGrass = -1; | |||||
| // //int32_t i = 0;//遍历次数 | |||||
| // | |||||
| // for (int32_t i = 0; i < 13; i++) | |||||
| // { | |||||
| // int32_t D = MoveFunc::FindWay::findPath | |||||
| // (api.GetFullMap(), api.GridToCell(api.GetSelfInfo()->x), api.GridToCell(api.GetSelfInfo()->y), GrassLocations[i][0], GrassLocations[i][1], Condition1, api).size(); | |||||
| // if (i == 0) | |||||
| // { | |||||
| // DistanceOfTheNearestGrass = D; | |||||
| // NumberOfTheNearestGrass = i; | |||||
| // } | |||||
| // else if (D < DistanceOfTheNearestGrass)//待实现无人正在写 | |||||
| // { | |||||
| // DistanceOfTheNearestGrass = D; | |||||
| // NumberOfTheNearestGrass = i; | |||||
| // } | |||||
| // } | |||||
| // return { GrassLocations[NumberOfTheNearestGrass][0],GrassLocations[NumberOfTheNearestGrass][1] }; | |||||
| // } | |||||
| // | |||||
| // void FindAndGo() | |||||
| // { | |||||
| // if (!Escaping) | |||||
| // { | |||||
| // auto p = FindTheNearestGrass(); | |||||
| // SetDestination(p[0], p[1], Condition1); | |||||
| // Escaping = true; | |||||
| // } | |||||
| // else | |||||
| // if (!IfDone()) | |||||
| // { | |||||
| // MoveAStep(); | |||||
| // return; | |||||
| // } | |||||
| // else | |||||
| // { | |||||
| // return; | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // void StopEscape() | |||||
| // { | |||||
| // Escaping = false; | |||||
| // } | |||||
| // }; | |||||
| //} | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| //static std::vector<std::vector<int>> a{ {24,6},{15,6},{15,17},{24,17} }; | |||||
| if (this->playerID == 0)//三教 | |||||
| { | |||||
| /*static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| }*/ | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1)//外边 | |||||
| { | |||||
| /*static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| }*/ | |||||
| //玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2)//外边 | |||||
| { | |||||
| /*static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| }*/ | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3)//六教 | |||||
| { | |||||
| /*static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static Homework::GoToNearestHomework HomeworkTest(api); | |||||
| static Grass::MoveToGrass GrassTest(api); | |||||
| static int i = 0; | |||||
| if (api.GetTrickers().empty()) | |||||
| { | |||||
| GrassTest.StopEscape(); | |||||
| HomeworkTest.FindAndDo(); | |||||
| } | |||||
| else | |||||
| { | |||||
| GrassTest.FindAndGo(); | |||||
| }*/ | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| bool HaveStudentInSight(ITrickerAPI& api)//判断视野内是否有学生 | |||||
| { | |||||
| if (api.GetStudents().empty()) | |||||
| return false; | |||||
| else return true; | |||||
| } | |||||
| void MoveInFixedTrace(ITrickerAPI& api)//沿包含作业的固定路线走 | |||||
| { | |||||
| static MoveFunc::GoToSomePlace::GoToAPlace Test(api); | |||||
| static auto i = 0; | |||||
| if (i == 0) | |||||
| { | |||||
| Test.SetDestination(28, 27, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 1) | |||||
| { | |||||
| Test.SetDestination(10, 39, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 2) | |||||
| { | |||||
| Test.SetDestination(8, 32, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 3) | |||||
| { | |||||
| Test.SetDestination(18, 6, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 4) | |||||
| { | |||||
| Test.SetDestination(30, 8, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 5) | |||||
| { | |||||
| Test.SetDestination(22, 19, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 6) | |||||
| { | |||||
| Test.SetDestination(40, 13, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 7) | |||||
| { | |||||
| Test.SetDestination(44, 33, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 8) | |||||
| { | |||||
| Test.SetDestination(33, 41, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| if (i == 9) | |||||
| { | |||||
| Test.SetDestination(28, 27, Condition1); | |||||
| i++; | |||||
| } | |||||
| Test.MoveAStep(); | |||||
| return; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| //捣蛋鬼策略(喧哗者) | |||||
| //沿包含所有作业的固定路线循环走,视野范围内有学生则攻击,若学生在视野内消失,则找到并前往最近的作业,回到之前的轨道 | |||||
| if (HaveStudentInSight(api))//视野内无学生,调用寻路函数走规定路线 | |||||
| { | |||||
| MoveInFixedTrace(api); | |||||
| } | |||||
| else//视野内有学生,优先使用技能攻击,在技能CD期间用普通攻击 | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto result1 = api.UseSkill(0); | |||||
| bool actualresult = result1.get(); | |||||
| api.Print(fmt::format("use skill result: {}", actualresult)); | |||||
| //if (THUAI6::Player::timeUntilSkillAvailable <= std::chrono::milliseconds(25000))//技能在cd中 | |||||
| //{ | |||||
| // bool attackresult = result2.get(); | |||||
| // api.Print(fmt::format("attack a student.")); | |||||
| //} | |||||
| //else//有技能可用 | |||||
| //{ | |||||
| // bool actualresult = result1.get(); | |||||
| // api.Print(fmt::format("use skill result: {}", actualresult)); | |||||
| //} | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,310 @@ | |||||
| #include <future> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = true; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数THUAI6:: | |||||
| struct node | |||||
| { | |||||
| int x; int y; | |||||
| }; | |||||
| int step = 1 * 1000 * 1000 / Constants::Athlete::moveSpeed; | |||||
| void LeftTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->y) | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| //右移至x处 | |||||
| void RightTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->y) | |||||
| api.MoveRight(50); | |||||
| } | |||||
| //上移至x处 | |||||
| void UpTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->x) | |||||
| api.MoveUp(50); | |||||
| } | |||||
| //下移至x处 | |||||
| void DownTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->x) | |||||
| api.MoveDown(50); | |||||
| } | |||||
| void moveto(int a, int b, IStudentAPI& api)//移动到cell(a,b) | |||||
| { | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto3(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| while ((api.GetSelfInfo()->y < 1000 * (b + 1) - 600) || (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) || (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) || (api.GetSelfInfo()->x > 1000 * (a + 1) - 400)) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| void slide1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::Window || judge2 == THUAI6::PlaceType::Window || judge3 == THUAI6::PlaceType::Window || judge4 == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.SkipWindow(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void learn1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::ClassRoom || judge2 == THUAI6::PlaceType::ClassRoom || judge3 == THUAI6::PlaceType::ClassRoom || judge4 == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.StartLearning(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto1(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, ITrickerAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void slide(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| api.Wait(); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| } | |||||
| void learn(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| api.StartLearning(); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| if (api.GetClassroomProgress(22, 18) < 100) | |||||
| { | |||||
| moveto(22, 17, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(22, 18) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(30, 7) < 100) | |||||
| { | |||||
| moveto2(30, 6, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(30, 7) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(18, 5) < 100) | |||||
| { | |||||
| moveto2(18, 6, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(18, 5) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| /*api.EndAllAction(); | |||||
| moveto(17, 5, api); | |||||
| moveto(14, 5, api); | |||||
| api.EndAllAction(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(2000)); | |||||
| slide1(api);*/ | |||||
| moveto(14, 13, api); | |||||
| moveto(9, 13, api); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(2000)); | |||||
| moveto2(6, 6, api); | |||||
| api.EndAllAction(); | |||||
| while (api.GetGateProgress(5, 6) < 18000) | |||||
| { | |||||
| api.StartOpenGate(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| api.Graduate(); | |||||
| api.EndAllAction(); | |||||
| //slide1(api); | |||||
| /*moveto(22, 17, api); | |||||
| while (api.GetClassroomProgress(22, 18) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| learn1(api);*/ | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,310 @@ | |||||
| #include <future> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = true; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数THUAI6:: | |||||
| struct node | |||||
| { | |||||
| int x; int y; | |||||
| }; | |||||
| int step = 1 * 1000 * 1000 / Constants::Athlete::moveSpeed; | |||||
| void LeftTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->y) | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| //右移至x处 | |||||
| void RightTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->y) | |||||
| api.MoveRight(50); | |||||
| } | |||||
| //上移至x处 | |||||
| void UpTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->x) | |||||
| api.MoveUp(50); | |||||
| } | |||||
| //下移至x处 | |||||
| void DownTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->x) | |||||
| api.MoveDown(50); | |||||
| } | |||||
| void moveto(int a, int b, IStudentAPI& api)//移动到cell(a,b) | |||||
| { | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto3(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| while ((api.GetSelfInfo()->y < 1000 * (b + 1) - 600) || (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) || (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) || (api.GetSelfInfo()->x > 1000 * (a + 1) - 400)) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| void slide1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::Window || judge2 == THUAI6::PlaceType::Window || judge3 == THUAI6::PlaceType::Window || judge4 == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.SkipWindow(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void learn1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::ClassRoom || judge2 == THUAI6::PlaceType::ClassRoom || judge3 == THUAI6::PlaceType::ClassRoom || judge4 == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.StartLearning(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto1(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, ITrickerAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void slide(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| api.Wait(); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| } | |||||
| void learn(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| api.StartLearning(); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| /* moveto(25, 48, api); | |||||
| moveto(5, 48, api); | |||||
| moveto(5, 37, api); | |||||
| api.EndAllAction(); | |||||
| api.SkipWindow(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| slide1(api); | |||||
| if (api.GetClassroomProgress(19, 41) < 100) | |||||
| { | |||||
| moveto(16, 37, api); | |||||
| moveto2(19, 42, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(19, 41) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(10, 38) < 100) | |||||
| { | |||||
| moveto(10, 39, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(10, 38) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(8, 31) < 100) | |||||
| { | |||||
| moveto(8, 32, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(8, 31) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| moveto(6, 32, api); | |||||
| api.EndAllAction(); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| slide1(api); */ | |||||
| moveto(42, 44, api); | |||||
| /*if (api.GetGateProgress(46, 45) >0) | |||||
| { | |||||
| moveto(42, 43, api); | |||||
| moveto(46, 44, api); | |||||
| api.StartOpenGate(); | |||||
| }*/ | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(70000)); | |||||
| moveto(42, 43, api); | |||||
| moveto(46, 44, api); | |||||
| api.StartOpenGate(); | |||||
| api.Graduate(); | |||||
| api.EndAllAction(); | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,297 @@ | |||||
| #include <future> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = true; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数THUAI6:: | |||||
| struct node | |||||
| { | |||||
| int x; int y; | |||||
| }; | |||||
| int step = 1 * 1000 * 1000 / Constants::Athlete::moveSpeed; | |||||
| void LeftTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->y) | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| //右移至x处 | |||||
| void RightTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->y) | |||||
| api.MoveRight(50); | |||||
| } | |||||
| //上移至x处 | |||||
| void UpTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->x) | |||||
| api.MoveUp(50); | |||||
| } | |||||
| //下移至x处 | |||||
| void DownTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->x) | |||||
| api.MoveDown(50); | |||||
| } | |||||
| void moveto(int a, int b, IStudentAPI& api)//移动到cell(a,b) | |||||
| { | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto3(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| while ((api.GetSelfInfo()->y < 1000 * (b + 1) - 600) || (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) || (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) || (api.GetSelfInfo()->x > 1000 * (a + 1) - 400)) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| void slide1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::Window || judge2 == THUAI6::PlaceType::Window || judge3 == THUAI6::PlaceType::Window || judge4 == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.SkipWindow(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void learn1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::ClassRoom || judge2 == THUAI6::PlaceType::ClassRoom || judge3 == THUAI6::PlaceType::ClassRoom || judge4 == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.StartLearning(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto1(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, ITrickerAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void slide(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| api.Wait(); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| } | |||||
| void learn(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| api.StartLearning(); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| if (api.GetClassroomProgress(10, 38) < 100) | |||||
| { | |||||
| moveto(12, 25, api); | |||||
| moveto(4, 37, api); | |||||
| moveto(10, 37, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(10, 38) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| if (api.GetClassroomProgress(8, 31) < 100) | |||||
| { | |||||
| moveto(8, 32, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(8, 31) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| if (api.GetClassroomProgress(19, 41) < 100) | |||||
| { | |||||
| moveto2(19, 42, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(19, 41) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| moveto2(8, 42, api); | |||||
| moveto2(4, 37, api); | |||||
| moveto2(4, 18, api); | |||||
| moveto(2, 7, api); | |||||
| moveto2(2, 4, api); | |||||
| moveto(5, 5, api); | |||||
| while (api.GetGateProgress(5, 6) < 18000) | |||||
| { | |||||
| api.StartOpenGate(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| api.Graduate(); | |||||
| api.EndAllAction(); | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,314 @@ | |||||
| #include <future> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = true; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数THUAI6:: | |||||
| struct node | |||||
| { | |||||
| int x; int y; | |||||
| }; | |||||
| int step = 1 * 1000 * 1000 / Constants::Athlete::moveSpeed; | |||||
| void LeftTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->y) | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| //右移至x处 | |||||
| void RightTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->y) | |||||
| api.MoveRight(50); | |||||
| } | |||||
| //上移至x处 | |||||
| void UpTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->x) | |||||
| api.MoveUp(50); | |||||
| } | |||||
| //下移至x处 | |||||
| void DownTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->x) | |||||
| api.MoveDown(50); | |||||
| } | |||||
| void moveto(int a, int b, IStudentAPI& api)//移动到cell(a,b) | |||||
| { | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto3(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| while ((api.GetSelfInfo()->y < 1000 * (b + 1) - 600) || (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) || (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) || (api.GetSelfInfo()->x > 1000 * (a + 1) - 400)) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| void slide1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::Window || judge2 == THUAI6::PlaceType::Window || judge3 == THUAI6::PlaceType::Window || judge4 == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.SkipWindow(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void learn1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::ClassRoom || judge2 == THUAI6::PlaceType::ClassRoom || judge3 == THUAI6::PlaceType::ClassRoom || judge4 == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.StartLearning(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto1(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, ITrickerAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void slide(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| api.Wait(); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| } | |||||
| void learn(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| api.StartLearning(); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| if (api.GetClassroomProgress(40, 12) < 100) | |||||
| { | |||||
| moveto(40, 13, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(40, 12) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(28, 26) < 100) | |||||
| { | |||||
| moveto(40, 25, api); | |||||
| moveto(28, 25, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(28, 26) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(33, 40) < 100) | |||||
| { | |||||
| moveto(29, 34, api); | |||||
| moveto(33, 39, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(33, 40) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| if (api.GetClassroomProgress(44, 32) < 100) | |||||
| { | |||||
| moveto(33, 31, api); | |||||
| moveto(44, 31, api); | |||||
| } | |||||
| while (api.GetClassroomProgress(44, 32) < 10000000) | |||||
| { | |||||
| learn1(api); | |||||
| } | |||||
| moveto(43, 31, api); | |||||
| moveto2(41, 35, api); | |||||
| moveto2(41, 39, api); | |||||
| moveto(46, 44, api); | |||||
| /*api.EndAllAction(); | |||||
| moveto(45, 37, api); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(2000)); | |||||
| slide1(api); | |||||
| moveto(46, 44, api); | |||||
| api.EndAllAction();*/ | |||||
| while (api.GetGateProgress(46, 45) < 18000) | |||||
| { | |||||
| api.StartOpenGate(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| api.Graduate(); | |||||
| api.EndAllAction(); | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,342 @@ | |||||
| #include <future> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = true; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数THUAI6:: | |||||
| struct node | |||||
| { | |||||
| int x; int y; | |||||
| }; | |||||
| int step = 1 * 1000 * 1000 / Constants::Athlete::moveSpeed; | |||||
| void LeftTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->y) | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| //右移至x处 | |||||
| void RightTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->y) | |||||
| api.MoveRight(50); | |||||
| } | |||||
| //上移至x处 | |||||
| void UpTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x < self->x) | |||||
| api.MoveUp(50); | |||||
| } | |||||
| //下移至x处 | |||||
| void DownTo(ITrickerAPI& api, int x) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| while (x > self->x) | |||||
| api.MoveDown(50); | |||||
| } | |||||
| void moveto(int a, int b, IStudentAPI& api)//移动到cell(a,b) | |||||
| { | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto3(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| while ((api.GetSelfInfo()->y < 1000 * (b + 1) - 600) || (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) || (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) || (api.GetSelfInfo()->x > 1000 * (a + 1) - 400)) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| void slide1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::Window || judge2 == THUAI6::PlaceType::Window || judge3 == THUAI6::PlaceType::Window || judge4 == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.SkipWindow(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void learn1(IStudentAPI& api) | |||||
| { | |||||
| auto p1 = api.GetSelfInfo(); | |||||
| int x1 = api.GridToCell(p1->x); | |||||
| int y1 = api.GridToCell(p1->y); | |||||
| auto judge1 = api.GetPlaceType(x1 + 1, y1); | |||||
| auto judge2 = api.GetPlaceType(x1 - 1, y1); | |||||
| auto judge3 = api.GetPlaceType(x1, y1 + 1); | |||||
| auto judge4 = api.GetPlaceType(x1, y1 - 1); | |||||
| if (judge1 == THUAI6::PlaceType::ClassRoom || judge2 == THUAI6::PlaceType::ClassRoom || judge3 == THUAI6::PlaceType::ClassRoom || judge4 == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.StartLearning(); | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto1(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, IStudentAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void moveto2(int a, int b, ITrickerAPI& api) | |||||
| { | |||||
| if ((b + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->y < 1000 * (b + 1) - 600) | |||||
| { | |||||
| api.MoveRight(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->y > 1000 * (b + 1) - 400) | |||||
| { | |||||
| api.MoveLeft(50); | |||||
| } | |||||
| } | |||||
| if ((a + 1) != 0) | |||||
| { | |||||
| while (api.GetSelfInfo()->x < 1000 * (a + 1) - 600) | |||||
| { | |||||
| api.MoveDown(50); | |||||
| } | |||||
| while (api.GetSelfInfo()->x > 1000 * (a + 1) - 400) | |||||
| { | |||||
| api.MoveUp(50); | |||||
| } | |||||
| } | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void slide(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::Window) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |||||
| api.Wait(); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| } | |||||
| void learn(IStudentAPI& api, node b) | |||||
| { | |||||
| int x2, y2; | |||||
| int x0 = api.GetSelfInfo()->x; | |||||
| int y0 = api.GetSelfInfo()->y; | |||||
| x2 = api.CellToGrid(b.x); | |||||
| y2 = api.CellToGrid(b.y); | |||||
| if (api.GetPlaceType(b.x, b.y) == THUAI6::PlaceType::ClassRoom) | |||||
| { | |||||
| api.Move(step, atan2(y2 - y0, x2 - x0)); | |||||
| x0 = api.GetSelfInfo()->x; | |||||
| y0 = api.GetSelfInfo()->y; | |||||
| } | |||||
| api.StartLearning(); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto Student = api.GetStudents(); | |||||
| api.PrintStudent(); | |||||
| if (self->timeUntilSkillAvailable[0] == 0) | |||||
| api.UseSkill(0); | |||||
| if (!Student.empty()) | |||||
| { | |||||
| int i = Student.size(); | |||||
| for (int k = 0; k < i; k++) | |||||
| { | |||||
| if (Student[k]->studentType != THUAI6::StudentType::Teacher && (Student[k]->playerState != THUAI6::PlayerState::Quit) && (Student[k]->playerState != THUAI6::PlayerState::Addicted)) | |||||
| { | |||||
| if (sqrt(((self->y) - (Student[k]->y)) * ((self->y) - (Student[k]->y)) + ((self->x) - (Student[k]->x)) * ((self->x) - (Student[k]->x))) > 4000) | |||||
| { | |||||
| api.Move(50, 3.141592653589 + atan2(((self->y) - (Student[k]->y)), (self->x) - (Student[k]->x))); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| if (self->timeUntilSkillAvailable[1] == 0) | |||||
| { | |||||
| if (sqrt(((self->y) - (Student[k]->y)) * ((self->y) - (Student[k]->y)) + ((self->x) - (Student[k]->x)) * ((self->x) - (Student[k]->x))) < 3000) | |||||
| { | |||||
| api.UseSkill(1); | |||||
| api.Attack(3.141592653589 + atan2(((self->y) - (Student[k]->y)), (self->x) - (Student[k]->x))); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| if (sqrt(((self->y) - (Student[k]->y)) * ((self->y) - (Student[k]->y)) + ((self->x) - (Student[k]->x)) * ((self->x) - (Student[k]->x))) > 1400) | |||||
| { | |||||
| api.Move(50, 3.141592653589 + atan2(((self->y) - (Student[k]->y)), (self->x) - (Student[k]->x))); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| if (sqrt(((self->y) - (Student[k]->y)) * ((self->y) - (Student[k]->y)) + ((self->x) - (Student[k]->x)) * ((self->x) - (Student[k]->x))) < 1500) | |||||
| { | |||||
| api.Attack(3.141592653589 + atan2(((self->y) - (Student[k]->y)), (self->x) - (Student[k]->x))); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| } | |||||
| else break; | |||||
| } | |||||
| } | |||||
| if (Student.empty()) | |||||
| { | |||||
| /* if (api.GridToCell(self->x) > 21) | |||||
| { | |||||
| UpTo(api, 21); | |||||
| if (IfTouch(api, THUAI6::PlaceType::Wall)) | |||||
| api.EndAllAction(); | |||||
| } | |||||
| else if (api.GridToCell(self->x) < 21) | |||||
| { | |||||
| DownTo(api, 21); | |||||
| if (IfTouch(api, THUAI6::PlaceType::Wall)) | |||||
| api.EndAllAction(); | |||||
| } | |||||
| if (api.GridToCell(self->y) > 25) | |||||
| { | |||||
| LeftTo(api, 25); | |||||
| if (IfTouch(api, THUAI6::PlaceType::Wall)) | |||||
| api.EndAllAction(); | |||||
| } | |||||
| else if (api.GridToCell(self->y) < 25) | |||||
| { | |||||
| RightTo(api, 25); | |||||
| if (IfTouch(api, THUAI6::PlaceType::Wall)) | |||||
| api.EndAllAction(); | |||||
| }*/ | |||||
| while (api.GetPlaceType(api.GridToCell(self->x) - 1, api.GridToCell(self->y)) == THUAI6::PlaceType::Wall) | |||||
| { | |||||
| api.MoveLeft(100); | |||||
| api.EndAllAction(); | |||||
| } | |||||
| if (api.GridToCell(self->x) != 5 && api.GridToCell(self->y) != 3) | |||||
| { | |||||
| moveto2(2, api.GridToCell(self->y), api); | |||||
| moveto2(2, 3, api); | |||||
| moveto2(5, 3, api); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,425 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Teacher, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine}; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| bool move = false; | |||||
| int place[2] = { 2,2 }; | |||||
| //全局变量,坐标 | |||||
| long long abs_X = 0; | |||||
| long long abs_Y = 0; | |||||
| //卡位微调参量 | |||||
| int Turn_little = 0; | |||||
| bool Turn_can = 0; | |||||
| bool Turn_flag = 1; | |||||
| int Y_change = 0; | |||||
| void GetTo(int32_t X, int32_t Y, IStudentAPI& api)//X,Y为格子坐标,api直接放api即可 | |||||
| { | |||||
| //初始化参量 | |||||
| bool Y_can_go = 1, X_can_go = 1; | |||||
| bool Down_Player;//下移参量,判断是否下移 | |||||
| bool Up_Player;//上移参量,判断是否上移 | |||||
| bool Left_Player;//左移参量 | |||||
| bool Right_Player;//右移参量 | |||||
| bool Go_Go_X = 1;//卡位参量 0卡位,1不卡位 | |||||
| bool Go_Go_Y = 1;//卡位参量 0卡位,1不卡位 | |||||
| auto Selfinfo = api.GetSelfInfo();//获取人物信息 | |||||
| auto X_player = api.GridToCell(Selfinfo->x);//得到人物坐标X(格子) | |||||
| auto Y_player = api.GridToCell(Selfinfo->y);//得到人物坐标Y(格子) | |||||
| //获得人物周边信息 | |||||
| auto Placetype_LU = api.GetPlaceType(X_player - 1, Y_player - 1); | |||||
| auto Placetype_L0 = api.GetPlaceType(X_player, Y_player - 1); | |||||
| auto Placetype_LD = api.GetPlaceType(X_player + 1, Y_player - 1); | |||||
| auto Placetype_0U = api.GetPlaceType(X_player - 1, Y_player); | |||||
| auto Placetype_00 = api.GetPlaceType(X_player, Y_player); | |||||
| auto Placetype_0D = api.GetPlaceType(X_player + 1, Y_player); | |||||
| auto Placetype_RU = api.GetPlaceType(X_player - 1, Y_player + 1); | |||||
| auto Placetype_R0 = api.GetPlaceType(X_player, Y_player + 1); | |||||
| auto Placetype_RD = api.GetPlaceType(X_player + 1, Y_player + 1); | |||||
| //判断上下左右是否可走 | |||||
| int Left_can_go = (Placetype_L0 == THUAI6::PlaceType::Land || Placetype_L0 == THUAI6::PlaceType::Grass); | |||||
| int Right_can_go = (Placetype_R0 == THUAI6::PlaceType::Land || Placetype_R0 == THUAI6::PlaceType::Grass); | |||||
| int Up_can_go = (Placetype_0U == THUAI6::PlaceType::Land || Placetype_0U == THUAI6::PlaceType::Grass); | |||||
| int Down_can_go = (Placetype_0D == THUAI6::PlaceType::Land || Placetype_0D == THUAI6::PlaceType::Grass); | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //判断朝向 | |||||
| if (X > X_player)//Down | |||||
| { | |||||
| Down_Player = true; | |||||
| } | |||||
| else Down_Player = false; | |||||
| if (X < X_player)//Up | |||||
| { | |||||
| Up_Player = true; | |||||
| } | |||||
| else Up_Player = false; | |||||
| if (Y < Y_player)//Left | |||||
| { | |||||
| Left_Player = true; | |||||
| } | |||||
| else Left_Player = false; | |||||
| if (Y < Y_player)//Right | |||||
| { | |||||
| Right_Player = true; | |||||
| } | |||||
| else Right_Player = false; | |||||
| //先保证X对齐 | |||||
| //可走则走 | |||||
| if (Down_Player == true && Down_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveDown(10); | |||||
| } | |||||
| if (Up_Player == true && Up_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveUp(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Down_Player == true && Down_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Up_Player == true && Up_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_X == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Down_Player == 1 && !Down_can_go && Go_Go_X) | |||||
| { | |||||
| if (Right_Player && Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else if (Left_Player && Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else Y_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!Y_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| // | |||||
| //Y方向在X对齐后仍采用相同的方法,但是调整后成功Y对齐后需要进行再一次X对齐,反复循环直到对齐 | |||||
| // | |||||
| // | |||||
| // | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走则走 | |||||
| if (Right_Player == true && Right_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveRight(10); | |||||
| } | |||||
| if (Left_Player == true && Left_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveLeft(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Right_Player == true && Right_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Left_Player == true && Left_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_Y == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_Y == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Right_Player == 1 && !Right_can_go && Go_Go_Y) | |||||
| { | |||||
| if (Down_Player && Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else if (Up_Player && Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else X_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!X_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| //保留最后坐标 | |||||
| auto Selfinfo_Last = api.GetSelfInfo(); | |||||
| abs_X = Selfinfo_Last->x; | |||||
| abs_Y = Selfinfo_Last->y; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| GetTo(30, 30, api); | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| GetTo(10, 10, api); | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| auto mapInfo = api.GetFullMap(); | |||||
| auto SelfInfo = api.GetSelfInfo(); | |||||
| auto cellx = api.GridToCell(SelfInfo->x); | |||||
| auto celly = api.GridToCell(SelfInfo->y); | |||||
| int flash = 0; | |||||
| int i = cellx, j = celly; | |||||
| if (place[0] == 2 ) | |||||
| { | |||||
| for (flash = 0; i < (cellx + 10); i = i + 1) | |||||
| { | |||||
| for (; j < (celly + 10); j = j + 1) | |||||
| { | |||||
| if (mapInfo[i][j] == THUAI6::PlaceType::Chest) | |||||
| { | |||||
| flash = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (flash == 1) break; | |||||
| } | |||||
| if (flash == 1) | |||||
| { | |||||
| place[0] = i; place[1] = j; | |||||
| } | |||||
| } | |||||
| else if(1) | |||||
| {double angle = atan2(place[1] - celly, place[0] - cellx); | |||||
| api.Move(50, angle); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (api.GetChestProgress(place[0], place[1]) < 10000000) | |||||
| { | |||||
| api.StartOpenChest(); | |||||
| } | |||||
| else | |||||
| { | |||||
| auto PropInfo = api.GetProps(); | |||||
| api.PickProp(PropInfo[0]->type); | |||||
| api.PickProp(PropInfo[1]->type); | |||||
| } | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| GetTo(20, 20, api); | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,425 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Teacher, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine}; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| bool move = false; | |||||
| int place[2] = { 2,2 }; | |||||
| //全局变量,坐标 | |||||
| long long abs_X = 0; | |||||
| long long abs_Y = 0; | |||||
| //卡位微调参量 | |||||
| int Turn_little = 0; | |||||
| bool Turn_can = 0; | |||||
| bool Turn_flag = 1; | |||||
| int Y_change = 0; | |||||
| void GetTo(int32_t X, int32_t Y, IStudentAPI& api)//X,Y为格子坐标,api直接放api即可 | |||||
| { | |||||
| //初始化参量 | |||||
| bool Y_can_go = 1, X_can_go = 1; | |||||
| bool Down_Player;//下移参量,判断是否下移 | |||||
| bool Up_Player;//上移参量,判断是否上移 | |||||
| bool Left_Player;//左移参量 | |||||
| bool Right_Player;//右移参量 | |||||
| bool Go_Go_X = 1;//卡位参量 0卡位,1不卡位 | |||||
| bool Go_Go_Y = 1;//卡位参量 0卡位,1不卡位 | |||||
| auto Selfinfo = api.GetSelfInfo();//获取人物信息 | |||||
| auto X_player = api.GridToCell(Selfinfo->x);//得到人物坐标X(格子) | |||||
| auto Y_player = api.GridToCell(Selfinfo->y);//得到人物坐标Y(格子) | |||||
| //获得人物周边信息 | |||||
| auto Placetype_LU = api.GetPlaceType(X_player - 1, Y_player - 1); | |||||
| auto Placetype_L0 = api.GetPlaceType(X_player, Y_player - 1); | |||||
| auto Placetype_LD = api.GetPlaceType(X_player + 1, Y_player - 1); | |||||
| auto Placetype_0U = api.GetPlaceType(X_player - 1, Y_player); | |||||
| auto Placetype_00 = api.GetPlaceType(X_player, Y_player); | |||||
| auto Placetype_0D = api.GetPlaceType(X_player + 1, Y_player); | |||||
| auto Placetype_RU = api.GetPlaceType(X_player - 1, Y_player + 1); | |||||
| auto Placetype_R0 = api.GetPlaceType(X_player, Y_player + 1); | |||||
| auto Placetype_RD = api.GetPlaceType(X_player + 1, Y_player + 1); | |||||
| //判断上下左右是否可走 | |||||
| int Left_can_go = (Placetype_L0 == THUAI6::PlaceType::Land || Placetype_L0 == THUAI6::PlaceType::Grass); | |||||
| int Right_can_go = (Placetype_R0 == THUAI6::PlaceType::Land || Placetype_R0 == THUAI6::PlaceType::Grass); | |||||
| int Up_can_go = (Placetype_0U == THUAI6::PlaceType::Land || Placetype_0U == THUAI6::PlaceType::Grass); | |||||
| int Down_can_go = (Placetype_0D == THUAI6::PlaceType::Land || Placetype_0D == THUAI6::PlaceType::Grass); | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //判断朝向 | |||||
| if (X > X_player)//Down | |||||
| { | |||||
| Down_Player = true; | |||||
| } | |||||
| else Down_Player = false; | |||||
| if (X < X_player)//Up | |||||
| { | |||||
| Up_Player = true; | |||||
| } | |||||
| else Up_Player = false; | |||||
| if (Y < Y_player)//Left | |||||
| { | |||||
| Left_Player = true; | |||||
| } | |||||
| else Left_Player = false; | |||||
| if (Y < Y_player)//Right | |||||
| { | |||||
| Right_Player = true; | |||||
| } | |||||
| else Right_Player = false; | |||||
| //先保证X对齐 | |||||
| //可走则走 | |||||
| if (Down_Player == true && Down_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveDown(10); | |||||
| } | |||||
| if (Up_Player == true && Up_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveUp(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Down_Player == true && Down_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Up_Player == true && Up_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_X == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Down_Player == 1 && !Down_can_go && Go_Go_X) | |||||
| { | |||||
| if (Right_Player && Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else if (Left_Player && Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else Y_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!Y_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| // | |||||
| //Y方向在X对齐后仍采用相同的方法,但是调整后成功Y对齐后需要进行再一次X对齐,反复循环直到对齐 | |||||
| // | |||||
| // | |||||
| // | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走则走 | |||||
| if (Right_Player == true && Right_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveRight(10); | |||||
| } | |||||
| if (Left_Player == true && Left_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveLeft(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Right_Player == true && Right_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Left_Player == true && Left_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_Y == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_Y == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Right_Player == 1 && !Right_can_go && Go_Go_Y) | |||||
| { | |||||
| if (Down_Player && Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else if (Up_Player && Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else X_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!X_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| //保留最后坐标 | |||||
| auto Selfinfo_Last = api.GetSelfInfo(); | |||||
| abs_X = Selfinfo_Last->x; | |||||
| abs_Y = Selfinfo_Last->y; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| GetTo(30, 30, api); | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| GetTo(10, 10, api); | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| auto mapInfo = api.GetFullMap(); | |||||
| auto SelfInfo = api.GetSelfInfo(); | |||||
| auto cellx = api.GridToCell(SelfInfo->x); | |||||
| auto celly = api.GridToCell(SelfInfo->y); | |||||
| int flash = 0; | |||||
| int i = cellx, j = celly; | |||||
| if (place[0] == 2 ) | |||||
| { | |||||
| for (flash = 0; i < (cellx + 10); i = i + 1) | |||||
| { | |||||
| for (; j < (celly + 10); j = j + 1) | |||||
| { | |||||
| if (mapInfo[i][j] == THUAI6::PlaceType::Chest) | |||||
| { | |||||
| flash = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (flash == 1) break; | |||||
| } | |||||
| if (flash == 1) | |||||
| { | |||||
| place[0] = i; place[1] = j; | |||||
| } | |||||
| } | |||||
| else if(1) | |||||
| {double angle = atan2(place[1] - celly, place[0] - cellx); | |||||
| api.Move(50, angle); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (api.GetChestProgress(place[0], place[1]) < 10000000) | |||||
| { | |||||
| api.StartOpenChest(); | |||||
| } | |||||
| else | |||||
| { | |||||
| auto PropInfo = api.GetProps(); | |||||
| api.PickProp(PropInfo[0]->type); | |||||
| api.PickProp(PropInfo[1]->type); | |||||
| } | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| GetTo(20, 20, api); | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,425 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Teacher, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine}; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| bool move = false; | |||||
| int place[2] = { 2,2 }; | |||||
| //全局变量,坐标 | |||||
| long long abs_X = 0; | |||||
| long long abs_Y = 0; | |||||
| //卡位微调参量 | |||||
| int Turn_little = 0; | |||||
| bool Turn_can = 0; | |||||
| bool Turn_flag = 1; | |||||
| int Y_change = 0; | |||||
| void GetTo(int32_t X, int32_t Y, IStudentAPI& api)//X,Y为格子坐标,api直接放api即可 | |||||
| { | |||||
| //初始化参量 | |||||
| bool Y_can_go = 1, X_can_go = 1; | |||||
| bool Down_Player;//下移参量,判断是否下移 | |||||
| bool Up_Player;//上移参量,判断是否上移 | |||||
| bool Left_Player;//左移参量 | |||||
| bool Right_Player;//右移参量 | |||||
| bool Go_Go_X = 1;//卡位参量 0卡位,1不卡位 | |||||
| bool Go_Go_Y = 1;//卡位参量 0卡位,1不卡位 | |||||
| auto Selfinfo = api.GetSelfInfo();//获取人物信息 | |||||
| auto X_player = api.GridToCell(Selfinfo->x);//得到人物坐标X(格子) | |||||
| auto Y_player = api.GridToCell(Selfinfo->y);//得到人物坐标Y(格子) | |||||
| //获得人物周边信息 | |||||
| auto Placetype_LU = api.GetPlaceType(X_player - 1, Y_player - 1); | |||||
| auto Placetype_L0 = api.GetPlaceType(X_player, Y_player - 1); | |||||
| auto Placetype_LD = api.GetPlaceType(X_player + 1, Y_player - 1); | |||||
| auto Placetype_0U = api.GetPlaceType(X_player - 1, Y_player); | |||||
| auto Placetype_00 = api.GetPlaceType(X_player, Y_player); | |||||
| auto Placetype_0D = api.GetPlaceType(X_player + 1, Y_player); | |||||
| auto Placetype_RU = api.GetPlaceType(X_player - 1, Y_player + 1); | |||||
| auto Placetype_R0 = api.GetPlaceType(X_player, Y_player + 1); | |||||
| auto Placetype_RD = api.GetPlaceType(X_player + 1, Y_player + 1); | |||||
| //判断上下左右是否可走 | |||||
| int Left_can_go = (Placetype_L0 == THUAI6::PlaceType::Land || Placetype_L0 == THUAI6::PlaceType::Grass); | |||||
| int Right_can_go = (Placetype_R0 == THUAI6::PlaceType::Land || Placetype_R0 == THUAI6::PlaceType::Grass); | |||||
| int Up_can_go = (Placetype_0U == THUAI6::PlaceType::Land || Placetype_0U == THUAI6::PlaceType::Grass); | |||||
| int Down_can_go = (Placetype_0D == THUAI6::PlaceType::Land || Placetype_0D == THUAI6::PlaceType::Grass); | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //判断朝向 | |||||
| if (X > X_player)//Down | |||||
| { | |||||
| Down_Player = true; | |||||
| } | |||||
| else Down_Player = false; | |||||
| if (X < X_player)//Up | |||||
| { | |||||
| Up_Player = true; | |||||
| } | |||||
| else Up_Player = false; | |||||
| if (Y < Y_player)//Left | |||||
| { | |||||
| Left_Player = true; | |||||
| } | |||||
| else Left_Player = false; | |||||
| if (Y < Y_player)//Right | |||||
| { | |||||
| Right_Player = true; | |||||
| } | |||||
| else Right_Player = false; | |||||
| //先保证X对齐 | |||||
| //可走则走 | |||||
| if (Down_Player == true && Down_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveDown(10); | |||||
| } | |||||
| if (Up_Player == true && Up_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveUp(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Down_Player == true && Down_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Up_Player == true && Up_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_X == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Down_Player == 1 && !Down_can_go && Go_Go_X) | |||||
| { | |||||
| if (Right_Player && Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else if (Left_Player && Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else Y_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!Y_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| // | |||||
| //Y方向在X对齐后仍采用相同的方法,但是调整后成功Y对齐后需要进行再一次X对齐,反复循环直到对齐 | |||||
| // | |||||
| // | |||||
| // | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走则走 | |||||
| if (Right_Player == true && Right_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveRight(10); | |||||
| } | |||||
| if (Left_Player == true && Left_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveLeft(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Right_Player == true && Right_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Left_Player == true && Left_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_Y == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_Y == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Right_Player == 1 && !Right_can_go && Go_Go_Y) | |||||
| { | |||||
| if (Down_Player && Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else if (Up_Player && Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else X_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!X_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| //保留最后坐标 | |||||
| auto Selfinfo_Last = api.GetSelfInfo(); | |||||
| abs_X = Selfinfo_Last->x; | |||||
| abs_Y = Selfinfo_Last->y; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| GetTo(30, 30, api); | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| GetTo(10, 10, api); | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| auto mapInfo = api.GetFullMap(); | |||||
| auto SelfInfo = api.GetSelfInfo(); | |||||
| auto cellx = api.GridToCell(SelfInfo->x); | |||||
| auto celly = api.GridToCell(SelfInfo->y); | |||||
| int flash = 0; | |||||
| int i = cellx, j = celly; | |||||
| if (place[0] == 2 ) | |||||
| { | |||||
| for (flash = 0; i < (cellx + 10); i = i + 1) | |||||
| { | |||||
| for (; j < (celly + 10); j = j + 1) | |||||
| { | |||||
| if (mapInfo[i][j] == THUAI6::PlaceType::Chest) | |||||
| { | |||||
| flash = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (flash == 1) break; | |||||
| } | |||||
| if (flash == 1) | |||||
| { | |||||
| place[0] = i; place[1] = j; | |||||
| } | |||||
| } | |||||
| else if(1) | |||||
| {double angle = atan2(place[1] - celly, place[0] - cellx); | |||||
| api.Move(50, angle); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (api.GetChestProgress(place[0], place[1]) < 10000000) | |||||
| { | |||||
| api.StartOpenChest(); | |||||
| } | |||||
| else | |||||
| { | |||||
| auto PropInfo = api.GetProps(); | |||||
| api.PickProp(PropInfo[0]->type); | |||||
| api.PickProp(PropInfo[1]->type); | |||||
| } | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| GetTo(20, 20, api); | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,425 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Teacher, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine}; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| // 可以在AI.cpp内部声明变量与函数 | |||||
| bool move = false; | |||||
| int place[2] = { 2,2 }; | |||||
| //全局变量,坐标 | |||||
| long long abs_X = 0; | |||||
| long long abs_Y = 0; | |||||
| //卡位微调参量 | |||||
| int Turn_little = 0; | |||||
| bool Turn_can = 0; | |||||
| bool Turn_flag = 1; | |||||
| int Y_change = 0; | |||||
| void GetTo(int32_t X, int32_t Y, IStudentAPI& api)//X,Y为格子坐标,api直接放api即可 | |||||
| { | |||||
| //初始化参量 | |||||
| bool Y_can_go = 1, X_can_go = 1; | |||||
| bool Down_Player;//下移参量,判断是否下移 | |||||
| bool Up_Player;//上移参量,判断是否上移 | |||||
| bool Left_Player;//左移参量 | |||||
| bool Right_Player;//右移参量 | |||||
| bool Go_Go_X = 1;//卡位参量 0卡位,1不卡位 | |||||
| bool Go_Go_Y = 1;//卡位参量 0卡位,1不卡位 | |||||
| auto Selfinfo = api.GetSelfInfo();//获取人物信息 | |||||
| auto X_player = api.GridToCell(Selfinfo->x);//得到人物坐标X(格子) | |||||
| auto Y_player = api.GridToCell(Selfinfo->y);//得到人物坐标Y(格子) | |||||
| //获得人物周边信息 | |||||
| auto Placetype_LU = api.GetPlaceType(X_player - 1, Y_player - 1); | |||||
| auto Placetype_L0 = api.GetPlaceType(X_player, Y_player - 1); | |||||
| auto Placetype_LD = api.GetPlaceType(X_player + 1, Y_player - 1); | |||||
| auto Placetype_0U = api.GetPlaceType(X_player - 1, Y_player); | |||||
| auto Placetype_00 = api.GetPlaceType(X_player, Y_player); | |||||
| auto Placetype_0D = api.GetPlaceType(X_player + 1, Y_player); | |||||
| auto Placetype_RU = api.GetPlaceType(X_player - 1, Y_player + 1); | |||||
| auto Placetype_R0 = api.GetPlaceType(X_player, Y_player + 1); | |||||
| auto Placetype_RD = api.GetPlaceType(X_player + 1, Y_player + 1); | |||||
| //判断上下左右是否可走 | |||||
| int Left_can_go = (Placetype_L0 == THUAI6::PlaceType::Land || Placetype_L0 == THUAI6::PlaceType::Grass); | |||||
| int Right_can_go = (Placetype_R0 == THUAI6::PlaceType::Land || Placetype_R0 == THUAI6::PlaceType::Grass); | |||||
| int Up_can_go = (Placetype_0U == THUAI6::PlaceType::Land || Placetype_0U == THUAI6::PlaceType::Grass); | |||||
| int Down_can_go = (Placetype_0D == THUAI6::PlaceType::Land || Placetype_0D == THUAI6::PlaceType::Grass); | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //判断朝向 | |||||
| if (X > X_player)//Down | |||||
| { | |||||
| Down_Player = true; | |||||
| } | |||||
| else Down_Player = false; | |||||
| if (X < X_player)//Up | |||||
| { | |||||
| Up_Player = true; | |||||
| } | |||||
| else Up_Player = false; | |||||
| if (Y < Y_player)//Left | |||||
| { | |||||
| Left_Player = true; | |||||
| } | |||||
| else Left_Player = false; | |||||
| if (Y < Y_player)//Right | |||||
| { | |||||
| Right_Player = true; | |||||
| } | |||||
| else Right_Player = false; | |||||
| //先保证X对齐 | |||||
| //可走则走 | |||||
| if (Down_Player == true && Down_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveDown(10); | |||||
| } | |||||
| if (Up_Player == true && Up_can_go && Go_Go_X) | |||||
| { | |||||
| api.MoveUp(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Down_Player == true && Down_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Up_Player == true && Up_can_go) | |||||
| { | |||||
| if ((Go_Go_X == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveLeft(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveRight(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_X == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Down_Player == 1 && !Down_can_go && Go_Go_X) | |||||
| { | |||||
| if (Right_Player && Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else if (Left_Player && Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Left_can_go) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else if (Right_can_go) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else Y_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!Y_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| // | |||||
| //Y方向在X对齐后仍采用相同的方法,但是调整后成功Y对齐后需要进行再一次X对齐,反复循环直到对齐 | |||||
| // | |||||
| // | |||||
| // | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走则走 | |||||
| if (Right_Player == true && Right_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveRight(10); | |||||
| } | |||||
| if (Left_Player == true && Left_can_go && Go_Go_Y) | |||||
| { | |||||
| api.MoveLeft(10); | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //可走但是卡位,则微调,需要每次检查是否仍卡位 | |||||
| if (Right_Player == true && Right_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 15) Turn_flag = 0; | |||||
| else; | |||||
| if ((Go_Go_X == 0) && Turn_flag == 1 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Left_Player == true && Left_can_go) | |||||
| { | |||||
| if ((Go_Go_Y == 0) && Turn_can == 0 && Turn_flag == 1) | |||||
| { | |||||
| api.MoveUp(3); | |||||
| Turn_little += 1; | |||||
| } | |||||
| else; | |||||
| if (Turn_little >= 5) Turn_flag = 1; | |||||
| else; | |||||
| if ((Go_Go_Y == 0) && Turn_flag == 0 && Turn_can == 0) | |||||
| { | |||||
| api.MoveDown(3); | |||||
| } | |||||
| else; | |||||
| } | |||||
| else; | |||||
| if (Go_Go_Y == 1) | |||||
| { | |||||
| Turn_little = 0; | |||||
| Turn_can = 0; | |||||
| Turn_flag = 1; | |||||
| } | |||||
| //判断是否卡位 | |||||
| Selfinfo = api.GetSelfInfo(); | |||||
| if ((abs_X == Selfinfo->x) && (abs_Y == Selfinfo->y)) | |||||
| { | |||||
| Go_Go_X = 0; | |||||
| Go_Go_Y = 0;//卡位 | |||||
| } | |||||
| else | |||||
| { | |||||
| Go_Go_X = 1; | |||||
| Go_Go_Y = 1;//未卡 | |||||
| } | |||||
| //若X对齐时竖直受阻,则调整Y参量,直到可以继续对齐 | |||||
| //若是不可走,则水平调整,直到可走,先朝向Y调整,直到可走或者卡位,可走则继续,卡位则反向调整 | |||||
| if (Right_Player == 1 && !Right_can_go && Go_Go_Y) | |||||
| { | |||||
| if (Down_Player && Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else if (Up_Player && Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Up_can_go) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else if (Down_can_go) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else X_can_go = 0; | |||||
| } | |||||
| //若反向调整仍不行,则向原X方向反向移动,再进行上述相同的调整,直到可以继续对齐 | |||||
| if (!X_can_go) | |||||
| { | |||||
| api.MoveUp(100); | |||||
| } | |||||
| else; | |||||
| //保留最后坐标 | |||||
| auto Selfinfo_Last = api.GetSelfInfo(); | |||||
| abs_X = Selfinfo_Last->x; | |||||
| abs_Y = Selfinfo_Last->y; | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| GetTo(30, 30, api); | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| GetTo(10, 10, api); | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| auto mapInfo = api.GetFullMap(); | |||||
| auto SelfInfo = api.GetSelfInfo(); | |||||
| auto cellx = api.GridToCell(SelfInfo->x); | |||||
| auto celly = api.GridToCell(SelfInfo->y); | |||||
| int flash = 0; | |||||
| int i = cellx, j = celly; | |||||
| if (place[0] == 2 ) | |||||
| { | |||||
| for (flash = 0; i < (cellx + 10); i = i + 1) | |||||
| { | |||||
| for (; j < (celly + 10); j = j + 1) | |||||
| { | |||||
| if (mapInfo[i][j] == THUAI6::PlaceType::Chest) | |||||
| { | |||||
| flash = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (flash == 1) break; | |||||
| } | |||||
| if (flash == 1) | |||||
| { | |||||
| place[0] = i; place[1] = j; | |||||
| } | |||||
| } | |||||
| else if(1) | |||||
| {double angle = atan2(place[1] - celly, place[0] - cellx); | |||||
| api.Move(50, angle); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (api.GetChestProgress(place[0], place[1]) < 10000000) | |||||
| { | |||||
| api.StartOpenChest(); | |||||
| } | |||||
| else | |||||
| { | |||||
| auto PropInfo = api.GetProps(); | |||||
| api.PickProp(PropInfo[0]->type); | |||||
| api.PickProp(PropInfo[1]->type); | |||||
| } | |||||
| } | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| GetTo(20, 20, api); | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| // 当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| } | |||||
| @@ -0,0 +1,168 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include <chrono> | |||||
| #include <cmath> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::Athlete, | |||||
| THUAI6::StudentType::Teacher, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine}; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| // 玩家2执行操作 | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| } | |||||
| int PatrolMode = 0; | |||||
| bool Passable(THUAI6::PlaceType a) | |||||
| { | |||||
| if (a == THUAI6::PlaceType::Land)return true; | |||||
| else if (a == THUAI6::PlaceType::Grass)return true; | |||||
| else return false; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| api.PrintSelfInfo(); | |||||
| auto AllMap = api.GetFullMap(); | |||||
| int NumberOfAims = 0; | |||||
| int x = api.GridToCell(self->x); | |||||
| int y = api.GridToCell(self->y); | |||||
| bool CenterLeft = self->y % 1000 <= 500; | |||||
| bool CenterUp = self->x % 1000 <= 500; | |||||
| auto Aims = api.GetStudents(); | |||||
| auto Aims_location = Aims.begin(); | |||||
| if(Aims.size() != 0) | |||||
| for(;Aims_location!=Aims.end();++Aims_location) | |||||
| { | |||||
| if ((*Aims_location)->playerState != THUAI6::PlayerState::Addicted)NumberOfAims++; | |||||
| } | |||||
| Aims_location = Aims.begin(); | |||||
| if (NumberOfAims!=0) | |||||
| { | |||||
| int Aims_x = api.GridToCell((*Aims_location)->x); | |||||
| int Aims_y = api.GridToCell((*Aims_location)->y); | |||||
| double relative_x = (*Aims_location)->x - self->x; | |||||
| double relative_y = (*Aims_location)->y - self->y; | |||||
| double Aims_angle = atan2(relative_y,relative_x); | |||||
| api.Move(20, Aims_angle); | |||||
| if (relative_x*relative_x+relative_y*relative_y<=4000000&&(*Aims_location)->playerState!=THUAI6::PlayerState::Addicted) | |||||
| { | |||||
| api.Attack(Aims_angle); | |||||
| } | |||||
| //else if ((*Aims_location)->playerState == THUAI6::PlayerState::Addicted && Aims_location != Aims.end()) | |||||
| // { | |||||
| // Aims_location++; | |||||
| // } | |||||
| //else if((*Aims_location)->playerState == THUAI6::PlayerState::Addicted) | |||||
| } | |||||
| else | |||||
| { | |||||
| switch (PatrolMode) | |||||
| { | |||||
| case 0: | |||||
| if (Passable(api.GetPlaceType(x + 1, y)) && Passable(api.GetPlaceType(x + 1, y + 1 - CenterLeft * 2))) | |||||
| { | |||||
| api.MoveDown(20); | |||||
| } | |||||
| else | |||||
| { | |||||
| PatrolMode++; | |||||
| } | |||||
| break; | |||||
| case 1: | |||||
| if (Passable(api.GetPlaceType(x, y + 1)) && Passable(api.GetPlaceType(x + 1 - CenterUp * 2, y + 1 )) ) | |||||
| { | |||||
| api.MoveRight(20); | |||||
| } | |||||
| else | |||||
| { | |||||
| PatrolMode++; | |||||
| } | |||||
| break; | |||||
| case 2: | |||||
| if (Passable(api.GetPlaceType(x-1, y )) && Passable(api.GetPlaceType(x - 1, y + 1 - CenterLeft * 2)) ) | |||||
| { | |||||
| api.MoveUp(20); | |||||
| } | |||||
| else | |||||
| { | |||||
| PatrolMode++; | |||||
| } | |||||
| break; | |||||
| case 3: | |||||
| if (Passable(api.GetPlaceType(x, y - 1)) && Passable(api.GetPlaceType(x + 1 - CenterUp * 2, y - 1))) | |||||
| { | |||||
| api.MoveLeft(20); | |||||
| } | |||||
| else | |||||
| { | |||||
| PatrolMode++; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| PatrolMode = 0; | |||||
| break; | |||||
| } | |||||
| if (PatrolMode == 4)PatrolMode =0; | |||||
| } | |||||
| /*NullPlaceType = 0, | |||||
| Land = 1, | |||||
| Wall = 2, | |||||
| Grass = 3, | |||||
| ClassRoom = 4, | |||||
| Gate = 5, | |||||
| HiddenGate = 6, | |||||
| Window = 7, | |||||
| Door3 = 8, | |||||
| Door5 = 9, | |||||
| Door6 = 10, | |||||
| Chest = 11,*/ | |||||
| } | |||||
| @@ -0,0 +1,899 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #define M_PI 3.1415926535 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| auto sec = std::chrono::duration_cast<std::chrono::milliseconds> | |||||
| (std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| Point() | |||||
| { | |||||
| x = 0; | |||||
| y = 0; | |||||
| prex = 0; | |||||
| prey = 0; | |||||
| cost = 1; | |||||
| sumcost = 0; | |||||
| } | |||||
| Point(int tx, int ty, int tprex = 0, int tprey = 0, int tcost = 0, int tsumcost = 0) | |||||
| { | |||||
| x = tx; | |||||
| y = ty; | |||||
| prex = tprex; | |||||
| prey = tprey; | |||||
| cost = tcost; | |||||
| sumcost = tsumcost; | |||||
| } | |||||
| int x; | |||||
| int y; | |||||
| int prex; | |||||
| int prey; | |||||
| int cost; | |||||
| int sumcost; | |||||
| inline int32_t operator-(Point& p) | |||||
| { | |||||
| return (this->x - p.x) * (this->x - p.x) + (this->y - p.y) * (this->y - p.y); | |||||
| } | |||||
| }; | |||||
| int direction[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} }; | |||||
| int moveStep = 50; | |||||
| std::vector<Point> path; | |||||
| int iPath = 0; // 指向下一目标位置的指针 | |||||
| const int x = 50; | |||||
| const int y = 50; | |||||
| Point map[x][y]; | |||||
| bool hasLearned[10] = { false, false, false, false, false, false, false, false, false, false }; | |||||
| using Place = THUAI6::PlaceType; | |||||
| const std::vector<std::vector<THUAI6::PlaceType>> Map = | |||||
| { | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Chest, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Gate, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Door5, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Door3, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Window, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Window, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Door5, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Chest, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Window, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Door3, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Door6, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Door6, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Chest, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Door6, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Window, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Gate, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| }; | |||||
| std::vector<Point> classrooms = { | |||||
| {18, 5}, {30, 7}, {22, 18}, // Building 3 | |||||
| {8, 31}, {10, 38}, {19, 41}, // Building 5 | |||||
| {28, 26}, {33, 40}, {40, 12}, {44, 32} // Building 6 | |||||
| }; | |||||
| void initmap() | |||||
| { | |||||
| for (int i = 0; i < x; i++) | |||||
| { | |||||
| for (int j = 0; j < y; j++) | |||||
| { | |||||
| map[i][j].x = i; | |||||
| map[i][j].y = j; | |||||
| switch (Map[i][j]) | |||||
| { | |||||
| case Place::Wall: | |||||
| map[i][j].cost = 1000; | |||||
| break; | |||||
| default: | |||||
| map[i][j].cost = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void findPath(Point begin, Point end) | |||||
| { | |||||
| path.clear(); | |||||
| iPath = 0; | |||||
| std::vector<Point> extending; | |||||
| std::vector<Point> unextended; | |||||
| std::vector<Point> extended; | |||||
| int min = 0; | |||||
| extended.push_back(begin); | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extended[0].x + direction[i][0] >= 0 && extended[0].x + direction[i][0] < x && extended[0].y + direction[i][1] < y && extended[0].y + direction[i][1] >= 0) | |||||
| { | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].sumcost = extended[0].sumcost + map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].cost; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prex = begin.x; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prey = begin.y; | |||||
| extending.push_back(map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| while (!extending.empty()) | |||||
| { | |||||
| if (extending.size() > 1) | |||||
| { | |||||
| min = 0; | |||||
| for (int i = 1; i < extending.size(); i++) | |||||
| { | |||||
| //find the point in extending whose sumcost is least | |||||
| if (extending[i].sumcost < extending[min].sumcost) | |||||
| { | |||||
| min = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| min = 0; | |||||
| } | |||||
| int flag = 0; | |||||
| int flag1 = 0; | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] >= 0 && extending[min].x + direction[i][0] < x && extending[min].y + direction[i][1] < y && extending[min].y + direction[i][1] >= 0) | |||||
| { | |||||
| for (int j = 0; j < extended.size(); j++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] == extended[j].x && extending[min].y + direction[i][1] == extended[j].y) | |||||
| { | |||||
| flag++; //the point has added in extended | |||||
| } | |||||
| } | |||||
| if (flag == 0) //the point is not extended | |||||
| { | |||||
| for (int k = 0; k < extending.size(); k++) | |||||
| { | |||||
| if (map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].x == extending[k].x && map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].y == extending[k].y) | |||||
| { | |||||
| if (extending[k].sumcost > extending[min].sumcost + extending[k].cost) | |||||
| { | |||||
| extending[k].sumcost = extending[min].sumcost + extending[k].cost; | |||||
| extending[k].prex = extending[min].x; | |||||
| extending[k].prey = extending[min].y; | |||||
| } | |||||
| flag1++; | |||||
| } | |||||
| } | |||||
| if (flag1 == 0) | |||||
| { | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].sumcost = extending[min].sumcost + map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].cost; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prex = extending[min].x; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prey = extending[min].y; | |||||
| extending.push_back(map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| } | |||||
| flag = 0; | |||||
| flag1 = 0; | |||||
| } | |||||
| extended.push_back(extending[min]); | |||||
| extending.erase(extending.begin() + min); | |||||
| } | |||||
| Point temp; | |||||
| for (int i = 0; i < extended.size(); i++) | |||||
| { | |||||
| if (extended[i].x == end.x && extended[i].y == end.y) | |||||
| { | |||||
| temp = extended[i]; | |||||
| while (1) | |||||
| { | |||||
| if (temp.x != begin.x || temp.y != begin.y) | |||||
| { | |||||
| path.push_back(temp); | |||||
| temp = map[temp.prex][temp.prey]; | |||||
| } | |||||
| else | |||||
| { | |||||
| path.push_back(begin); | |||||
| return; | |||||
| } | |||||
| } | |||||
| while (temp.prex != begin.x || temp.prey != begin.y); | |||||
| { | |||||
| } | |||||
| path.push_back(begin); | |||||
| } | |||||
| } | |||||
| } | |||||
| Point GetNearestClass(int xCell, int yCell) | |||||
| { | |||||
| Point res, now{ xCell, yCell }; | |||||
| int32_t min = 0x3f3f3f3f; | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| Point temp = classrooms[i]; | |||||
| if (!hasLearned[i]) | |||||
| { | |||||
| int32_t dist = temp - now; | |||||
| if (dist < min) | |||||
| { | |||||
| min = dist; | |||||
| res = temp; | |||||
| } | |||||
| } | |||||
| } | |||||
| return res; | |||||
| } | |||||
| Point GoToClass(IStudentAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto target = GetNearestClass(api.GridToCell(self->x), api.GridToCell(self->y)); | |||||
| double deg = atan2(target.y - api.GridToCell(self->y), target.x - api.GridToCell(self->x)); | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| return target; | |||||
| } | |||||
| void EscapeFromTricker(IStudentAPI& api) | |||||
| { | |||||
| auto tricker = api.GetTrickers(); | |||||
| if (!tricker.size()) return; | |||||
| auto player = api.GetSelfInfo(); | |||||
| double deg = atan2(player->y - tricker[0]->y, player->x - tricker[0]->x); | |||||
| int xCell = api.GridToCell(player->x), yCell = api.GridToCell(player->y); | |||||
| auto gridType = api.GetPlaceType(xCell, yCell); | |||||
| //switch (gridType) | |||||
| //{ | |||||
| // case THUAI6::PlaceType::Land: | |||||
| // api.Move(Constants::frameDuration, deg); | |||||
| // break; | |||||
| //} | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| if (api.GetFrameCount() == 1) { | |||||
| initmap(); // 初始化地图 | |||||
| api.Print("地图初始化完毕"); | |||||
| } | |||||
| EscapeFromTricker(api); | |||||
| Point classroom = GoToClass(api); | |||||
| api.StartLearning(); | |||||
| if (api.GetClassroomProgress(classroom.x, classroom.y) == Constants::maxClassroomProgress) | |||||
| { | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| if (classrooms[i].x == classroom.x && classrooms[i].y == classroom.y) | |||||
| { | |||||
| hasLearned[i] = true; | |||||
| for (int j = 0; j < 4; ++j) | |||||
| { | |||||
| api.SendMessage(j, fmt::format("{}", i)); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (api.HaveMessage()) | |||||
| { | |||||
| auto msg = api.GetMessage(); | |||||
| int classroomID = msg.second[0] - '0'; | |||||
| hasLearned[classroomID] = true; | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| /*鬼相关函数*/ | |||||
| // 视野内有人 | |||||
| int isStudentinView(ITrickerAPI& api) { | |||||
| auto info = api.GetStudents(); | |||||
| return (info.size() != 0); | |||||
| } | |||||
| void avoidObstacles(ITrickerAPI& api, double dirc) { | |||||
| const int selfRadius = 450; | |||||
| const int moveStep = 10; // 待定 | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto selfX = selfInfo->x; | |||||
| auto selfY = selfInfo->y; | |||||
| // 定义左右点 | |||||
| int leftPointX = selfX - (int)(selfRadius * sin(dirc)); | |||||
| int leftPointY = selfY + (int)(selfRadius * cos(dirc)); | |||||
| int rightPointX = selfX + (int)(selfRadius * sin(dirc)); | |||||
| int rightPointY = selfY - (int)(selfRadius * cos(dirc)); | |||||
| auto gridX = api.GridToCell(leftPointX + (int)(1000 * cos(dirc))); | |||||
| auto gridY = api.GridToCell(leftPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc - M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| return; | |||||
| } | |||||
| gridX = api.GridToCell(rightPointX + (int)(1000 * cos(dirc))); | |||||
| gridY = api.GridToCell(rightPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc + M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto myPos = Point(selfInfo->x, selfInfo->y); | |||||
| api.PrintSelfInfo(); | |||||
| if (isStudentinView(api)) { | |||||
| // 追击 | |||||
| auto studentInfo = api.GetStudents(); | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto dx = studentInfo[0]->x - selfInfo->x; | |||||
| auto dy = studentInfo[0]->y - selfInfo->y; | |||||
| auto dirc = atan2(dy, dx); | |||||
| avoidObstacles(api, dirc); | |||||
| api.Move(moveStep, dirc); | |||||
| double distance = sqrt(dx * dx + dy + dy); | |||||
| if (distance < Constants::basicAttackShortRange) { | |||||
| api.Attack(dirc); | |||||
| } | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| @@ -0,0 +1,899 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #define M_PI 3.1415926535 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| auto sec = std::chrono::duration_cast<std::chrono::milliseconds> | |||||
| (std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| Point() | |||||
| { | |||||
| x = 0; | |||||
| y = 0; | |||||
| prex = 0; | |||||
| prey = 0; | |||||
| cost = 1; | |||||
| sumcost = 0; | |||||
| } | |||||
| Point(int tx, int ty, int tprex = 0, int tprey = 0, int tcost = 0, int tsumcost = 0) | |||||
| { | |||||
| x = tx; | |||||
| y = ty; | |||||
| prex = tprex; | |||||
| prey = tprey; | |||||
| cost = tcost; | |||||
| sumcost = tsumcost; | |||||
| } | |||||
| int x; | |||||
| int y; | |||||
| int prex; | |||||
| int prey; | |||||
| int cost; | |||||
| int sumcost; | |||||
| inline int32_t operator-(Point& p) | |||||
| { | |||||
| return (this->x - p.x) * (this->x - p.x) + (this->y - p.y) * (this->y - p.y); | |||||
| } | |||||
| }; | |||||
| int direction[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} }; | |||||
| int moveStep = 50; | |||||
| std::vector<Point> path; | |||||
| int iPath = 0; // 指向下一目标位置的指针 | |||||
| const int x = 50; | |||||
| const int y = 50; | |||||
| Point map[x][y]; | |||||
| bool hasLearned[10] = { false, false, false, false, false, false, false, false, false, false }; | |||||
| using Place = THUAI6::PlaceType; | |||||
| const std::vector<std::vector<THUAI6::PlaceType>> Map = | |||||
| { | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Chest, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Gate, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Door5, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Door3, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Window, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Window, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Door5, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Chest, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Window, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Door3, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Door6, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Door6, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Chest, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Door6, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Window, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Gate, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| }; | |||||
| std::vector<Point> classrooms = { | |||||
| {18, 5}, {30, 7}, {22, 18}, // Building 3 | |||||
| {8, 31}, {10, 38}, {19, 41}, // Building 5 | |||||
| {28, 26}, {33, 40}, {40, 12}, {44, 32} // Building 6 | |||||
| }; | |||||
| void initmap() | |||||
| { | |||||
| for (int i = 0; i < x; i++) | |||||
| { | |||||
| for (int j = 0; j < y; j++) | |||||
| { | |||||
| map[i][j].x = i; | |||||
| map[i][j].y = j; | |||||
| switch (Map[i][j]) | |||||
| { | |||||
| case Place::Wall: | |||||
| map[i][j].cost = 1000; | |||||
| break; | |||||
| default: | |||||
| map[i][j].cost = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void findPath(Point begin, Point end) | |||||
| { | |||||
| path.clear(); | |||||
| iPath = 0; | |||||
| std::vector<Point> extending; | |||||
| std::vector<Point> unextended; | |||||
| std::vector<Point> extended; | |||||
| int min = 0; | |||||
| extended.push_back(begin); | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extended[0].x + direction[i][0] >= 0 && extended[0].x + direction[i][0] < x && extended[0].y + direction[i][1] < y && extended[0].y + direction[i][1] >= 0) | |||||
| { | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].sumcost = extended[0].sumcost + map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].cost; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prex = begin.x; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prey = begin.y; | |||||
| extending.push_back(map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| while (!extending.empty()) | |||||
| { | |||||
| if (extending.size() > 1) | |||||
| { | |||||
| min = 0; | |||||
| for (int i = 1; i < extending.size(); i++) | |||||
| { | |||||
| //find the point in extending whose sumcost is least | |||||
| if (extending[i].sumcost < extending[min].sumcost) | |||||
| { | |||||
| min = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| min = 0; | |||||
| } | |||||
| int flag = 0; | |||||
| int flag1 = 0; | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] >= 0 && extending[min].x + direction[i][0] < x && extending[min].y + direction[i][1] < y && extending[min].y + direction[i][1] >= 0) | |||||
| { | |||||
| for (int j = 0; j < extended.size(); j++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] == extended[j].x && extending[min].y + direction[i][1] == extended[j].y) | |||||
| { | |||||
| flag++; //the point has added in extended | |||||
| } | |||||
| } | |||||
| if (flag == 0) //the point is not extended | |||||
| { | |||||
| for (int k = 0; k < extending.size(); k++) | |||||
| { | |||||
| if (map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].x == extending[k].x && map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].y == extending[k].y) | |||||
| { | |||||
| if (extending[k].sumcost > extending[min].sumcost + extending[k].cost) | |||||
| { | |||||
| extending[k].sumcost = extending[min].sumcost + extending[k].cost; | |||||
| extending[k].prex = extending[min].x; | |||||
| extending[k].prey = extending[min].y; | |||||
| } | |||||
| flag1++; | |||||
| } | |||||
| } | |||||
| if (flag1 == 0) | |||||
| { | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].sumcost = extending[min].sumcost + map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].cost; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prex = extending[min].x; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prey = extending[min].y; | |||||
| extending.push_back(map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| } | |||||
| flag = 0; | |||||
| flag1 = 0; | |||||
| } | |||||
| extended.push_back(extending[min]); | |||||
| extending.erase(extending.begin() + min); | |||||
| } | |||||
| Point temp; | |||||
| for (int i = 0; i < extended.size(); i++) | |||||
| { | |||||
| if (extended[i].x == end.x && extended[i].y == end.y) | |||||
| { | |||||
| temp = extended[i]; | |||||
| while (1) | |||||
| { | |||||
| if (temp.x != begin.x || temp.y != begin.y) | |||||
| { | |||||
| path.push_back(temp); | |||||
| temp = map[temp.prex][temp.prey]; | |||||
| } | |||||
| else | |||||
| { | |||||
| path.push_back(begin); | |||||
| return; | |||||
| } | |||||
| } | |||||
| while (temp.prex != begin.x || temp.prey != begin.y); | |||||
| { | |||||
| } | |||||
| path.push_back(begin); | |||||
| } | |||||
| } | |||||
| } | |||||
| Point GetNearestClass(int xCell, int yCell) | |||||
| { | |||||
| Point res, now{ xCell, yCell }; | |||||
| int32_t min = 0x3f3f3f3f; | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| Point temp = classrooms[i]; | |||||
| if (!hasLearned[i]) | |||||
| { | |||||
| int32_t dist = temp - now; | |||||
| if (dist < min) | |||||
| { | |||||
| min = dist; | |||||
| res = temp; | |||||
| } | |||||
| } | |||||
| } | |||||
| return res; | |||||
| } | |||||
| Point GoToClass(IStudentAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto target = GetNearestClass(api.GridToCell(self->x), api.GridToCell(self->y)); | |||||
| double deg = atan2(target.y - api.GridToCell(self->y), target.x - api.GridToCell(self->x)); | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| return target; | |||||
| } | |||||
| void EscapeFromTricker(IStudentAPI& api) | |||||
| { | |||||
| auto tricker = api.GetTrickers(); | |||||
| if (!tricker.size()) return; | |||||
| auto player = api.GetSelfInfo(); | |||||
| double deg = atan2(player->y - tricker[0]->y, player->x - tricker[0]->x); | |||||
| int xCell = api.GridToCell(player->x), yCell = api.GridToCell(player->y); | |||||
| auto gridType = api.GetPlaceType(xCell, yCell); | |||||
| //switch (gridType) | |||||
| //{ | |||||
| // case THUAI6::PlaceType::Land: | |||||
| // api.Move(Constants::frameDuration, deg); | |||||
| // break; | |||||
| //} | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| if (api.GetFrameCount() == 1) { | |||||
| initmap(); // 初始化地图 | |||||
| api.Print("地图初始化完毕"); | |||||
| } | |||||
| EscapeFromTricker(api); | |||||
| Point classroom = GoToClass(api); | |||||
| api.StartLearning(); | |||||
| if (api.GetClassroomProgress(classroom.x, classroom.y) == Constants::maxClassroomProgress) | |||||
| { | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| if (classrooms[i].x == classroom.x && classrooms[i].y == classroom.y) | |||||
| { | |||||
| hasLearned[i] = true; | |||||
| for (int j = 0; j < 4; ++j) | |||||
| { | |||||
| api.SendMessage(j, fmt::format("{}", i)); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (api.HaveMessage()) | |||||
| { | |||||
| auto msg = api.GetMessage(); | |||||
| int classroomID = msg.second[0] - '0'; | |||||
| hasLearned[classroomID] = true; | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| /*鬼相关函数*/ | |||||
| // 视野内有人 | |||||
| int isStudentinView(ITrickerAPI& api) { | |||||
| auto info = api.GetStudents(); | |||||
| return (info.size() != 0); | |||||
| } | |||||
| void avoidObstacles(ITrickerAPI& api, double dirc) { | |||||
| const int selfRadius = 450; | |||||
| const int moveStep = 10; // 待定 | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto selfX = selfInfo->x; | |||||
| auto selfY = selfInfo->y; | |||||
| // 定义左右点 | |||||
| int leftPointX = selfX - (int)(selfRadius * sin(dirc)); | |||||
| int leftPointY = selfY + (int)(selfRadius * cos(dirc)); | |||||
| int rightPointX = selfX + (int)(selfRadius * sin(dirc)); | |||||
| int rightPointY = selfY - (int)(selfRadius * cos(dirc)); | |||||
| auto gridX = api.GridToCell(leftPointX + (int)(1000 * cos(dirc))); | |||||
| auto gridY = api.GridToCell(leftPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc - M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| return; | |||||
| } | |||||
| gridX = api.GridToCell(rightPointX + (int)(1000 * cos(dirc))); | |||||
| gridY = api.GridToCell(rightPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc + M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto myPos = Point(selfInfo->x, selfInfo->y); | |||||
| api.PrintSelfInfo(); | |||||
| if (isStudentinView(api)) { | |||||
| // 追击 | |||||
| auto studentInfo = api.GetStudents(); | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto dx = studentInfo[0]->x - selfInfo->x; | |||||
| auto dy = studentInfo[0]->y - selfInfo->y; | |||||
| auto dirc = atan2(dy, dx); | |||||
| avoidObstacles(api, dirc); | |||||
| api.Move(moveStep, dirc); | |||||
| double distance = sqrt(dx * dx + dy + dy); | |||||
| if (distance < Constants::basicAttackShortRange) { | |||||
| api.Attack(dirc); | |||||
| } | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| @@ -0,0 +1,899 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #define M_PI 3.1415926535 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| auto sec = std::chrono::duration_cast<std::chrono::milliseconds> | |||||
| (std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| Point() | |||||
| { | |||||
| x = 0; | |||||
| y = 0; | |||||
| prex = 0; | |||||
| prey = 0; | |||||
| cost = 1; | |||||
| sumcost = 0; | |||||
| } | |||||
| Point(int tx, int ty, int tprex = 0, int tprey = 0, int tcost = 0, int tsumcost = 0) | |||||
| { | |||||
| x = tx; | |||||
| y = ty; | |||||
| prex = tprex; | |||||
| prey = tprey; | |||||
| cost = tcost; | |||||
| sumcost = tsumcost; | |||||
| } | |||||
| int x; | |||||
| int y; | |||||
| int prex; | |||||
| int prey; | |||||
| int cost; | |||||
| int sumcost; | |||||
| inline int32_t operator-(Point& p) | |||||
| { | |||||
| return (this->x - p.x) * (this->x - p.x) + (this->y - p.y) * (this->y - p.y); | |||||
| } | |||||
| }; | |||||
| int direction[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} }; | |||||
| int moveStep = 50; | |||||
| std::vector<Point> path; | |||||
| int iPath = 0; // 指向下一目标位置的指针 | |||||
| const int x = 50; | |||||
| const int y = 50; | |||||
| Point map[x][y]; | |||||
| bool hasLearned[10] = { false, false, false, false, false, false, false, false, false, false }; | |||||
| using Place = THUAI6::PlaceType; | |||||
| const std::vector<std::vector<THUAI6::PlaceType>> Map = | |||||
| { | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Chest, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Gate, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Door5, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Door3, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Window, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Window, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Door5, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Chest, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Window, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Door3, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Door6, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Door6, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Chest, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Door6, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Window, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Gate, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| }; | |||||
| std::vector<Point> classrooms = { | |||||
| {18, 5}, {30, 7}, {22, 18}, // Building 3 | |||||
| {8, 31}, {10, 38}, {19, 41}, // Building 5 | |||||
| {28, 26}, {33, 40}, {40, 12}, {44, 32} // Building 6 | |||||
| }; | |||||
| void initmap() | |||||
| { | |||||
| for (int i = 0; i < x; i++) | |||||
| { | |||||
| for (int j = 0; j < y; j++) | |||||
| { | |||||
| map[i][j].x = i; | |||||
| map[i][j].y = j; | |||||
| switch (Map[i][j]) | |||||
| { | |||||
| case Place::Wall: | |||||
| map[i][j].cost = 1000; | |||||
| break; | |||||
| default: | |||||
| map[i][j].cost = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void findPath(Point begin, Point end) | |||||
| { | |||||
| path.clear(); | |||||
| iPath = 0; | |||||
| std::vector<Point> extending; | |||||
| std::vector<Point> unextended; | |||||
| std::vector<Point> extended; | |||||
| int min = 0; | |||||
| extended.push_back(begin); | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extended[0].x + direction[i][0] >= 0 && extended[0].x + direction[i][0] < x && extended[0].y + direction[i][1] < y && extended[0].y + direction[i][1] >= 0) | |||||
| { | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].sumcost = extended[0].sumcost + map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].cost; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prex = begin.x; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prey = begin.y; | |||||
| extending.push_back(map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| while (!extending.empty()) | |||||
| { | |||||
| if (extending.size() > 1) | |||||
| { | |||||
| min = 0; | |||||
| for (int i = 1; i < extending.size(); i++) | |||||
| { | |||||
| //find the point in extending whose sumcost is least | |||||
| if (extending[i].sumcost < extending[min].sumcost) | |||||
| { | |||||
| min = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| min = 0; | |||||
| } | |||||
| int flag = 0; | |||||
| int flag1 = 0; | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] >= 0 && extending[min].x + direction[i][0] < x && extending[min].y + direction[i][1] < y && extending[min].y + direction[i][1] >= 0) | |||||
| { | |||||
| for (int j = 0; j < extended.size(); j++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] == extended[j].x && extending[min].y + direction[i][1] == extended[j].y) | |||||
| { | |||||
| flag++; //the point has added in extended | |||||
| } | |||||
| } | |||||
| if (flag == 0) //the point is not extended | |||||
| { | |||||
| for (int k = 0; k < extending.size(); k++) | |||||
| { | |||||
| if (map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].x == extending[k].x && map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].y == extending[k].y) | |||||
| { | |||||
| if (extending[k].sumcost > extending[min].sumcost + extending[k].cost) | |||||
| { | |||||
| extending[k].sumcost = extending[min].sumcost + extending[k].cost; | |||||
| extending[k].prex = extending[min].x; | |||||
| extending[k].prey = extending[min].y; | |||||
| } | |||||
| flag1++; | |||||
| } | |||||
| } | |||||
| if (flag1 == 0) | |||||
| { | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].sumcost = extending[min].sumcost + map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].cost; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prex = extending[min].x; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prey = extending[min].y; | |||||
| extending.push_back(map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| } | |||||
| flag = 0; | |||||
| flag1 = 0; | |||||
| } | |||||
| extended.push_back(extending[min]); | |||||
| extending.erase(extending.begin() + min); | |||||
| } | |||||
| Point temp; | |||||
| for (int i = 0; i < extended.size(); i++) | |||||
| { | |||||
| if (extended[i].x == end.x && extended[i].y == end.y) | |||||
| { | |||||
| temp = extended[i]; | |||||
| while (1) | |||||
| { | |||||
| if (temp.x != begin.x || temp.y != begin.y) | |||||
| { | |||||
| path.push_back(temp); | |||||
| temp = map[temp.prex][temp.prey]; | |||||
| } | |||||
| else | |||||
| { | |||||
| path.push_back(begin); | |||||
| return; | |||||
| } | |||||
| } | |||||
| while (temp.prex != begin.x || temp.prey != begin.y); | |||||
| { | |||||
| } | |||||
| path.push_back(begin); | |||||
| } | |||||
| } | |||||
| } | |||||
| Point GetNearestClass(int xCell, int yCell) | |||||
| { | |||||
| Point res, now{ xCell, yCell }; | |||||
| int32_t min = 0x3f3f3f3f; | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| Point temp = classrooms[i]; | |||||
| if (!hasLearned[i]) | |||||
| { | |||||
| int32_t dist = temp - now; | |||||
| if (dist < min) | |||||
| { | |||||
| min = dist; | |||||
| res = temp; | |||||
| } | |||||
| } | |||||
| } | |||||
| return res; | |||||
| } | |||||
| Point GoToClass(IStudentAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto target = GetNearestClass(api.GridToCell(self->x), api.GridToCell(self->y)); | |||||
| double deg = atan2(target.y - api.GridToCell(self->y), target.x - api.GridToCell(self->x)); | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| return target; | |||||
| } | |||||
| void EscapeFromTricker(IStudentAPI& api) | |||||
| { | |||||
| auto tricker = api.GetTrickers(); | |||||
| if (!tricker.size()) return; | |||||
| auto player = api.GetSelfInfo(); | |||||
| double deg = atan2(player->y - tricker[0]->y, player->x - tricker[0]->x); | |||||
| int xCell = api.GridToCell(player->x), yCell = api.GridToCell(player->y); | |||||
| auto gridType = api.GetPlaceType(xCell, yCell); | |||||
| //switch (gridType) | |||||
| //{ | |||||
| // case THUAI6::PlaceType::Land: | |||||
| // api.Move(Constants::frameDuration, deg); | |||||
| // break; | |||||
| //} | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| if (api.GetFrameCount() == 1) { | |||||
| initmap(); // 初始化地图 | |||||
| api.Print("地图初始化完毕"); | |||||
| } | |||||
| EscapeFromTricker(api); | |||||
| Point classroom = GoToClass(api); | |||||
| api.StartLearning(); | |||||
| if (api.GetClassroomProgress(classroom.x, classroom.y) == Constants::maxClassroomProgress) | |||||
| { | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| if (classrooms[i].x == classroom.x && classrooms[i].y == classroom.y) | |||||
| { | |||||
| hasLearned[i] = true; | |||||
| for (int j = 0; j < 4; ++j) | |||||
| { | |||||
| api.SendMessage(j, fmt::format("{}", i)); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (api.HaveMessage()) | |||||
| { | |||||
| auto msg = api.GetMessage(); | |||||
| int classroomID = msg.second[0] - '0'; | |||||
| hasLearned[classroomID] = true; | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| /*鬼相关函数*/ | |||||
| // 视野内有人 | |||||
| int isStudentinView(ITrickerAPI& api) { | |||||
| auto info = api.GetStudents(); | |||||
| return (info.size() != 0); | |||||
| } | |||||
| void avoidObstacles(ITrickerAPI& api, double dirc) { | |||||
| const int selfRadius = 450; | |||||
| const int moveStep = 10; // 待定 | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto selfX = selfInfo->x; | |||||
| auto selfY = selfInfo->y; | |||||
| // 定义左右点 | |||||
| int leftPointX = selfX - (int)(selfRadius * sin(dirc)); | |||||
| int leftPointY = selfY + (int)(selfRadius * cos(dirc)); | |||||
| int rightPointX = selfX + (int)(selfRadius * sin(dirc)); | |||||
| int rightPointY = selfY - (int)(selfRadius * cos(dirc)); | |||||
| auto gridX = api.GridToCell(leftPointX + (int)(1000 * cos(dirc))); | |||||
| auto gridY = api.GridToCell(leftPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc - M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| return; | |||||
| } | |||||
| gridX = api.GridToCell(rightPointX + (int)(1000 * cos(dirc))); | |||||
| gridY = api.GridToCell(rightPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc + M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto myPos = Point(selfInfo->x, selfInfo->y); | |||||
| api.PrintSelfInfo(); | |||||
| if (isStudentinView(api)) { | |||||
| // 追击 | |||||
| auto studentInfo = api.GetStudents(); | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto dx = studentInfo[0]->x - selfInfo->x; | |||||
| auto dy = studentInfo[0]->y - selfInfo->y; | |||||
| auto dirc = atan2(dy, dx); | |||||
| avoidObstacles(api, dirc); | |||||
| api.Move(moveStep, dirc); | |||||
| double distance = sqrt(dx * dx + dy + dy); | |||||
| if (distance < Constants::basicAttackShortRange) { | |||||
| api.Attack(dirc); | |||||
| } | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| @@ -0,0 +1,899 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #define M_PI 3.1415926535 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::StraightAStudent }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| auto sec = std::chrono::duration_cast<std::chrono::milliseconds> | |||||
| (std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| Point() | |||||
| { | |||||
| x = 0; | |||||
| y = 0; | |||||
| prex = 0; | |||||
| prey = 0; | |||||
| cost = 1; | |||||
| sumcost = 0; | |||||
| } | |||||
| Point(int tx, int ty, int tprex = 0, int tprey = 0, int tcost = 0, int tsumcost = 0) | |||||
| { | |||||
| x = tx; | |||||
| y = ty; | |||||
| prex = tprex; | |||||
| prey = tprey; | |||||
| cost = tcost; | |||||
| sumcost = tsumcost; | |||||
| } | |||||
| int x; | |||||
| int y; | |||||
| int prex; | |||||
| int prey; | |||||
| int cost; | |||||
| int sumcost; | |||||
| inline int32_t operator-(Point& p) | |||||
| { | |||||
| return (this->x - p.x) * (this->x - p.x) + (this->y - p.y) * (this->y - p.y); | |||||
| } | |||||
| }; | |||||
| int direction[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} }; | |||||
| int moveStep = 50; | |||||
| std::vector<Point> path; | |||||
| int iPath = 0; // 指向下一目标位置的指针 | |||||
| const int x = 50; | |||||
| const int y = 50; | |||||
| Point map[x][y]; | |||||
| bool hasLearned[10] = { false, false, false, false, false, false, false, false, false, false }; | |||||
| using Place = THUAI6::PlaceType; | |||||
| const std::vector<std::vector<THUAI6::PlaceType>> Map = | |||||
| { | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Chest, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Gate, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Door5, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Door3, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Window, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Window, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Door5, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Chest, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Window, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Door3, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Door6, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Door6, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Chest, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Door6, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Window, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Gate, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| }; | |||||
| std::vector<Point> classrooms = { | |||||
| {18, 5}, {30, 7}, {22, 18}, // Building 3 | |||||
| {8, 31}, {10, 38}, {19, 41}, // Building 5 | |||||
| {28, 26}, {33, 40}, {40, 12}, {44, 32} // Building 6 | |||||
| }; | |||||
| void initmap() | |||||
| { | |||||
| for (int i = 0; i < x; i++) | |||||
| { | |||||
| for (int j = 0; j < y; j++) | |||||
| { | |||||
| map[i][j].x = i; | |||||
| map[i][j].y = j; | |||||
| switch (Map[i][j]) | |||||
| { | |||||
| case Place::Wall: | |||||
| map[i][j].cost = 1000; | |||||
| break; | |||||
| default: | |||||
| map[i][j].cost = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void findPath(Point begin, Point end) | |||||
| { | |||||
| path.clear(); | |||||
| iPath = 0; | |||||
| std::vector<Point> extending; | |||||
| std::vector<Point> unextended; | |||||
| std::vector<Point> extended; | |||||
| int min = 0; | |||||
| extended.push_back(begin); | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extended[0].x + direction[i][0] >= 0 && extended[0].x + direction[i][0] < x && extended[0].y + direction[i][1] < y && extended[0].y + direction[i][1] >= 0) | |||||
| { | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].sumcost = extended[0].sumcost + map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].cost; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prex = begin.x; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prey = begin.y; | |||||
| extending.push_back(map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| while (!extending.empty()) | |||||
| { | |||||
| if (extending.size() > 1) | |||||
| { | |||||
| min = 0; | |||||
| for (int i = 1; i < extending.size(); i++) | |||||
| { | |||||
| //find the point in extending whose sumcost is least | |||||
| if (extending[i].sumcost < extending[min].sumcost) | |||||
| { | |||||
| min = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| min = 0; | |||||
| } | |||||
| int flag = 0; | |||||
| int flag1 = 0; | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] >= 0 && extending[min].x + direction[i][0] < x && extending[min].y + direction[i][1] < y && extending[min].y + direction[i][1] >= 0) | |||||
| { | |||||
| for (int j = 0; j < extended.size(); j++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] == extended[j].x && extending[min].y + direction[i][1] == extended[j].y) | |||||
| { | |||||
| flag++; //the point has added in extended | |||||
| } | |||||
| } | |||||
| if (flag == 0) //the point is not extended | |||||
| { | |||||
| for (int k = 0; k < extending.size(); k++) | |||||
| { | |||||
| if (map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].x == extending[k].x && map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].y == extending[k].y) | |||||
| { | |||||
| if (extending[k].sumcost > extending[min].sumcost + extending[k].cost) | |||||
| { | |||||
| extending[k].sumcost = extending[min].sumcost + extending[k].cost; | |||||
| extending[k].prex = extending[min].x; | |||||
| extending[k].prey = extending[min].y; | |||||
| } | |||||
| flag1++; | |||||
| } | |||||
| } | |||||
| if (flag1 == 0) | |||||
| { | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].sumcost = extending[min].sumcost + map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].cost; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prex = extending[min].x; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prey = extending[min].y; | |||||
| extending.push_back(map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| } | |||||
| flag = 0; | |||||
| flag1 = 0; | |||||
| } | |||||
| extended.push_back(extending[min]); | |||||
| extending.erase(extending.begin() + min); | |||||
| } | |||||
| Point temp; | |||||
| for (int i = 0; i < extended.size(); i++) | |||||
| { | |||||
| if (extended[i].x == end.x && extended[i].y == end.y) | |||||
| { | |||||
| temp = extended[i]; | |||||
| while (1) | |||||
| { | |||||
| if (temp.x != begin.x || temp.y != begin.y) | |||||
| { | |||||
| path.push_back(temp); | |||||
| temp = map[temp.prex][temp.prey]; | |||||
| } | |||||
| else | |||||
| { | |||||
| path.push_back(begin); | |||||
| return; | |||||
| } | |||||
| } | |||||
| while (temp.prex != begin.x || temp.prey != begin.y); | |||||
| { | |||||
| } | |||||
| path.push_back(begin); | |||||
| } | |||||
| } | |||||
| } | |||||
| Point GetNearestClass(int xCell, int yCell) | |||||
| { | |||||
| Point res, now{ xCell, yCell }; | |||||
| int32_t min = 0x3f3f3f3f; | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| Point temp = classrooms[i]; | |||||
| if (!hasLearned[i]) | |||||
| { | |||||
| int32_t dist = temp - now; | |||||
| if (dist < min) | |||||
| { | |||||
| min = dist; | |||||
| res = temp; | |||||
| } | |||||
| } | |||||
| } | |||||
| return res; | |||||
| } | |||||
| Point GoToClass(IStudentAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto target = GetNearestClass(api.GridToCell(self->x), api.GridToCell(self->y)); | |||||
| double deg = atan2(target.y - api.GridToCell(self->y), target.x - api.GridToCell(self->x)); | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| return target; | |||||
| } | |||||
| void EscapeFromTricker(IStudentAPI& api) | |||||
| { | |||||
| auto tricker = api.GetTrickers(); | |||||
| if (!tricker.size()) return; | |||||
| auto player = api.GetSelfInfo(); | |||||
| double deg = atan2(player->y - tricker[0]->y, player->x - tricker[0]->x); | |||||
| int xCell = api.GridToCell(player->x), yCell = api.GridToCell(player->y); | |||||
| auto gridType = api.GetPlaceType(xCell, yCell); | |||||
| //switch (gridType) | |||||
| //{ | |||||
| // case THUAI6::PlaceType::Land: | |||||
| // api.Move(Constants::frameDuration, deg); | |||||
| // break; | |||||
| //} | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| if (api.GetFrameCount() == 1) { | |||||
| initmap(); // 初始化地图 | |||||
| api.Print("地图初始化完毕"); | |||||
| } | |||||
| EscapeFromTricker(api); | |||||
| Point classroom = GoToClass(api); | |||||
| api.StartLearning(); | |||||
| if (api.GetClassroomProgress(classroom.x, classroom.y) == Constants::maxClassroomProgress) | |||||
| { | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| if (classrooms[i].x == classroom.x && classrooms[i].y == classroom.y) | |||||
| { | |||||
| hasLearned[i] = true; | |||||
| for (int j = 0; j < 4; ++j) | |||||
| { | |||||
| api.SendMessage(j, fmt::format("{}", i)); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (api.HaveMessage()) | |||||
| { | |||||
| auto msg = api.GetMessage(); | |||||
| int classroomID = msg.second[0] - '0'; | |||||
| hasLearned[classroomID] = true; | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| /*鬼相关函数*/ | |||||
| // 视野内有人 | |||||
| int isStudentinView(ITrickerAPI& api) { | |||||
| auto info = api.GetStudents(); | |||||
| return (info.size() != 0); | |||||
| } | |||||
| void avoidObstacles(ITrickerAPI& api, double dirc) { | |||||
| const int selfRadius = 450; | |||||
| const int moveStep = 10; // 待定 | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto selfX = selfInfo->x; | |||||
| auto selfY = selfInfo->y; | |||||
| // 定义左右点 | |||||
| int leftPointX = selfX - (int)(selfRadius * sin(dirc)); | |||||
| int leftPointY = selfY + (int)(selfRadius * cos(dirc)); | |||||
| int rightPointX = selfX + (int)(selfRadius * sin(dirc)); | |||||
| int rightPointY = selfY - (int)(selfRadius * cos(dirc)); | |||||
| auto gridX = api.GridToCell(leftPointX + (int)(1000 * cos(dirc))); | |||||
| auto gridY = api.GridToCell(leftPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc - M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| return; | |||||
| } | |||||
| gridX = api.GridToCell(rightPointX + (int)(1000 * cos(dirc))); | |||||
| gridY = api.GridToCell(rightPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc + M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto myPos = Point(selfInfo->x, selfInfo->y); | |||||
| api.PrintSelfInfo(); | |||||
| if (isStudentinView(api)) { | |||||
| // 追击 | |||||
| auto studentInfo = api.GetStudents(); | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto dx = studentInfo[0]->x - selfInfo->x; | |||||
| auto dy = studentInfo[0]->y - selfInfo->y; | |||||
| auto dirc = atan2(dy, dx); | |||||
| avoidObstacles(api, dirc); | |||||
| api.Move(moveStep, dirc); | |||||
| double distance = sqrt(dx * dx + dy + dy); | |||||
| if (distance < Constants::basicAttackShortRange) { | |||||
| api.Attack(dirc); | |||||
| } | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| @@ -0,0 +1,930 @@ | |||||
| #include <vector> | |||||
| #include <thread> | |||||
| #include <array> | |||||
| #include "AI.h" | |||||
| #include "constants.h" | |||||
| #define M_PI 3.1415926535 | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | |||||
| extern const bool asynchronous = false; | |||||
| // 选手需要依次将player0到player4的职业在这里定义 | |||||
| extern const std::array<THUAI6::StudentType, 4> studentType = { | |||||
| THUAI6::StudentType::StraightAStudent, | |||||
| THUAI6::StudentType::Sunshine, | |||||
| THUAI6::StudentType::Teacher, | |||||
| THUAI6::StudentType::StraightAStudent }; | |||||
| extern const THUAI6::TrickerType trickerType = THUAI6::TrickerType::Assassin; | |||||
| //可以在AI.cpp内部声明变量与函数 | |||||
| auto sec = std::chrono::duration_cast<std::chrono::milliseconds> | |||||
| (std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| Point() | |||||
| { | |||||
| x = 0; | |||||
| y = 0; | |||||
| prex = 0; | |||||
| prey = 0; | |||||
| cost = 1; | |||||
| sumcost = 0; | |||||
| } | |||||
| Point(int tx, int ty, int tprex = 0, int tprey = 0, int tcost = 0, int tsumcost = 0) | |||||
| { | |||||
| x = tx; | |||||
| y = ty; | |||||
| prex = tprex; | |||||
| prey = tprey; | |||||
| cost = tcost; | |||||
| sumcost = tsumcost; | |||||
| } | |||||
| int x; | |||||
| int y; | |||||
| int prex; | |||||
| int prey; | |||||
| int cost; | |||||
| int sumcost; | |||||
| inline int32_t operator-(Point& p) | |||||
| { | |||||
| return (this->x - p.x) * (this->x - p.x) + (this->y - p.y) * (this->y - p.y); | |||||
| } | |||||
| }; | |||||
| int direction[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} }; | |||||
| int moveStep = 50; | |||||
| std::vector<Point> path; | |||||
| int iPath = 0; // 指向下一目标位置的指针 | |||||
| const int x = 50; | |||||
| const int y = 50; | |||||
| Point map[x][y]; | |||||
| bool hasLearned[10] = { false, false, false, false, false, false, false, false, false, false }; | |||||
| using Place = THUAI6::PlaceType; | |||||
| const std::vector<std::vector<THUAI6::PlaceType>> Map = | |||||
| { | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Chest, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Gate, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Door5, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Door3, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Window, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Window, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Door5, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Chest, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::ClassRoom, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Window, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Window, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Door3, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::ClassRoom, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Door6, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Chest, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Grass, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Window, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Grass, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::ClassRoom, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Grass, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Door6, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Chest, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Window, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Grass, Place::Grass, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Wall, Place::Grass, Place::Grass, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Door6, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Land, Place::Wall, Place::Wall, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Grass, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::ClassRoom, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Wall, Place::Grass, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Window, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Wall, Place::Wall, Place::Grass, Place::Grass, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Window, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Gate, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Grass, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Wall, Place::Wall, Place::Grass, | |||||
| Place::Chest, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Land, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Land, Place::Wall, Place::Land, | |||||
| Place::Land, Place::Land, Place::Grass, Place::Grass, Place::Wall, | |||||
| Place::Wall, Place::Land, Place::Land, Place::Land, Place::Wall}, | |||||
| {Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall, | |||||
| Place::Wall, Place::Wall, Place::Wall, Place::Wall, Place::Wall}, | |||||
| }; | |||||
| std::vector<Point> classrooms = { | |||||
| {18, 5}, {30, 7}, {22, 18}, // Building 3 | |||||
| {8, 31}, {10, 38}, {19, 41}, // Building 5 | |||||
| {28, 26}, {33, 40}, {40, 12}, {44, 32} // Building 6 | |||||
| }; | |||||
| void initmap() | |||||
| { | |||||
| for (int i = 0; i < x; i++) | |||||
| { | |||||
| for (int j = 0; j < y; j++) | |||||
| { | |||||
| map[i][j].x = i; | |||||
| map[i][j].y = j; | |||||
| switch (Map[i][j]) | |||||
| { | |||||
| case Place::Wall: | |||||
| map[i][j].cost = 1000; | |||||
| break; | |||||
| default: | |||||
| map[i][j].cost = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void findPath(Point begin, Point end) | |||||
| { | |||||
| path.clear(); | |||||
| iPath = 0; | |||||
| std::vector<Point> extending; | |||||
| std::vector<Point> unextended; | |||||
| std::vector<Point> extended; | |||||
| int min = 0; | |||||
| extended.push_back(begin); | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extended[0].x + direction[i][0] >= 0 && extended[0].x + direction[i][0] < x && extended[0].y + direction[i][1] < y && extended[0].y + direction[i][1] >= 0) | |||||
| { | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].sumcost = extended[0].sumcost + map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].cost; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prex = begin.x; | |||||
| map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]].prey = begin.y; | |||||
| extending.push_back(map[extended[0].x + direction[i][0]][extended[0].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| while (!extending.empty()) | |||||
| { | |||||
| if (extending.size() > 1) | |||||
| { | |||||
| min = 0; | |||||
| for (int i = 1; i < extending.size(); i++) | |||||
| { | |||||
| //find the point in extending whose sumcost is least | |||||
| if (extending[i].sumcost < extending[min].sumcost) | |||||
| { | |||||
| min = i; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| min = 0; | |||||
| } | |||||
| int flag = 0; | |||||
| int flag1 = 0; | |||||
| for (int i = 0; i < 4; i++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] >= 0 && extending[min].x + direction[i][0] < x && extending[min].y + direction[i][1] < y && extending[min].y + direction[i][1] >= 0) | |||||
| { | |||||
| for (int j = 0; j < extended.size(); j++) | |||||
| { | |||||
| if (extending[min].x + direction[i][0] == extended[j].x && extending[min].y + direction[i][1] == extended[j].y) | |||||
| { | |||||
| flag++; //the point has added in extended | |||||
| } | |||||
| } | |||||
| if (flag == 0) //the point is not extended | |||||
| { | |||||
| for (int k = 0; k < extending.size(); k++) | |||||
| { | |||||
| if (map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].x == extending[k].x && map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].y == extending[k].y) | |||||
| { | |||||
| if (extending[k].sumcost > extending[min].sumcost + extending[k].cost) | |||||
| { | |||||
| extending[k].sumcost = extending[min].sumcost + extending[k].cost; | |||||
| extending[k].prex = extending[min].x; | |||||
| extending[k].prey = extending[min].y; | |||||
| } | |||||
| flag1++; | |||||
| } | |||||
| } | |||||
| if (flag1 == 0) | |||||
| { | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].sumcost = extending[min].sumcost + map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].cost; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prex = extending[min].x; | |||||
| map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]].prey = extending[min].y; | |||||
| extending.push_back(map[extending[min].x + direction[i][0]][extending[min].y + direction[i][1]]); | |||||
| } | |||||
| } | |||||
| } | |||||
| flag = 0; | |||||
| flag1 = 0; | |||||
| } | |||||
| extended.push_back(extending[min]); | |||||
| extending.erase(extending.begin() + min); | |||||
| } | |||||
| Point temp; | |||||
| for (int i = 0; i < extended.size(); i++) | |||||
| { | |||||
| if (extended[i].x == end.x && extended[i].y == end.y) | |||||
| { | |||||
| temp = extended[i]; | |||||
| while (1) | |||||
| { | |||||
| if (temp.x != begin.x || temp.y != begin.y) | |||||
| { | |||||
| path.push_back(temp); | |||||
| temp = map[temp.prex][temp.prey]; | |||||
| } | |||||
| else | |||||
| { | |||||
| path.push_back(begin); | |||||
| return; | |||||
| } | |||||
| } | |||||
| while (temp.prex != begin.x || temp.prey != begin.y); | |||||
| { | |||||
| } | |||||
| path.push_back(begin); | |||||
| } | |||||
| } | |||||
| } | |||||
| void avoidObstacles(IStudentAPI& api, double dirc) { | |||||
| const int selfRadius = 450; | |||||
| const int moveStep = 10; // 待定 | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto selfX = selfInfo->x; | |||||
| auto selfY = selfInfo->y; | |||||
| // 定义左右点 | |||||
| int leftPointX = selfX - (int)(selfRadius * sin(dirc)); | |||||
| int leftPointY = selfY + (int)(selfRadius * cos(dirc)); | |||||
| int rightPointX = selfX + (int)(selfRadius * sin(dirc)); | |||||
| int rightPointY = selfY - (int)(selfRadius * cos(dirc)); | |||||
| auto gridX = api.GridToCell(leftPointX + (int)(1000 * cos(dirc))); | |||||
| auto gridY = api.GridToCell(leftPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc - M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| return; | |||||
| } | |||||
| gridX = api.GridToCell(rightPointX + (int)(1000 * cos(dirc))); | |||||
| gridY = api.GridToCell(rightPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc + M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| } | |||||
| return; | |||||
| } | |||||
| Point GetNearestClass(int xCell, int yCell) | |||||
| { | |||||
| Point res, now{ xCell, yCell }; | |||||
| int32_t min = 0x3f3f3f3f; | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| Point temp = classrooms[i]; | |||||
| if (!hasLearned[i]) | |||||
| { | |||||
| int32_t dist = temp - now; | |||||
| if (dist < min) | |||||
| { | |||||
| min = dist; | |||||
| res = temp; | |||||
| } | |||||
| } | |||||
| } | |||||
| return res; | |||||
| } | |||||
| Point GoToClass(IStudentAPI& api) | |||||
| { | |||||
| auto self = api.GetSelfInfo(); | |||||
| auto target = GetNearestClass(api.GridToCell(self->x), api.GridToCell(self->y)); | |||||
| double deg = atan2(target.y - api.GridToCell(self->y), target.x - api.GridToCell(self->x)); | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| avoidObstacles(api, deg); // 尝试避障 | |||||
| return target; | |||||
| } | |||||
| void EscapeFromTricker(IStudentAPI& api) | |||||
| { | |||||
| auto tricker = api.GetTrickers(); | |||||
| if (!tricker.size()) return; | |||||
| auto player = api.GetSelfInfo(); | |||||
| double deg = atan2(player->y - tricker[0]->y, player->x - tricker[0]->x); | |||||
| int xCell = api.GridToCell(player->x), yCell = api.GridToCell(player->y); | |||||
| auto gridType = api.GetPlaceType(xCell, yCell); | |||||
| //switch (gridType) | |||||
| //{ | |||||
| // case THUAI6::PlaceType::Land: | |||||
| // api.Move(Constants::frameDuration, deg); | |||||
| // break; | |||||
| //} | |||||
| api.Move(Constants::frameDuration, deg); | |||||
| } | |||||
| void AI::play(IStudentAPI& api) | |||||
| { | |||||
| // 公共操作 | |||||
| if (this->playerID == 0) | |||||
| { | |||||
| // 玩家0执行操作 | |||||
| } | |||||
| else if (this->playerID == 1) | |||||
| { | |||||
| // 玩家1执行操作 | |||||
| } | |||||
| else if (this->playerID == 2) | |||||
| { | |||||
| } | |||||
| else if (this->playerID == 3) | |||||
| { | |||||
| // 玩家3执行操作 | |||||
| } | |||||
| //当然可以写成if (this->playerID == 2||this->playerID == 3)之类的操作 | |||||
| // 公共操作 | |||||
| if (api.GetFrameCount() == 1) { | |||||
| initmap(); // 初始化地图 | |||||
| api.Print("地图初始化完毕"); | |||||
| } | |||||
| EscapeFromTricker(api); | |||||
| Point classroom = GoToClass(api); | |||||
| api.StartLearning(); | |||||
| if (api.GetClassroomProgress(classroom.x, classroom.y) == Constants::maxClassroomProgress) | |||||
| { | |||||
| for (int i = 0; i < classrooms.size(); ++i) | |||||
| { | |||||
| if (classrooms[i].x == classroom.x && classrooms[i].y == classroom.y) | |||||
| { | |||||
| hasLearned[i] = true; | |||||
| for (int j = 0; j < 4; ++j) | |||||
| { | |||||
| api.SendMessage(j, fmt::format("{}", i)); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (api.HaveMessage()) | |||||
| { | |||||
| auto msg = api.GetMessage(); | |||||
| int classroomID = msg.second[0] - '0'; | |||||
| hasLearned[classroomID] = true; | |||||
| } | |||||
| api.UseSkill(0); | |||||
| api.UseSkill(1); | |||||
| api.UseSkill(2); | |||||
| } | |||||
| /*鬼相关函数*/ | |||||
| // 视野内有人 | |||||
| int isStudentinView(ITrickerAPI& api) { | |||||
| auto info = api.GetStudents(); | |||||
| return (info.size() != 0); | |||||
| } | |||||
| void avoidObstacles(ITrickerAPI& api, double dirc) { | |||||
| const int selfRadius = 450; | |||||
| const int moveStep = 10; // 待定 | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto selfX = selfInfo->x; | |||||
| auto selfY = selfInfo->y; | |||||
| // 定义左右点 | |||||
| int leftPointX = selfX - (int)(selfRadius * sin(dirc)); | |||||
| int leftPointY = selfY + (int)(selfRadius * cos(dirc)); | |||||
| int rightPointX = selfX + (int)(selfRadius * sin(dirc)); | |||||
| int rightPointY = selfY - (int)(selfRadius * cos(dirc)); | |||||
| auto gridX = api.GridToCell(leftPointX + (int)(1000 * cos(dirc))); | |||||
| auto gridY = api.GridToCell(leftPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc - M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| return; | |||||
| } | |||||
| gridX = api.GridToCell(rightPointX + (int)(1000 * cos(dirc))); | |||||
| gridY = api.GridToCell(rightPointY + (int)(1000 * sin(dirc))); | |||||
| if (api.GetPlaceType(gridX, gridY) == THUAI6::PlaceType::Wall) { | |||||
| api.Move(moveStep, dirc + M_PI / 2); | |||||
| avoidObstacles(api, dirc + M_PI / 10); | |||||
| } | |||||
| return; | |||||
| } | |||||
| void AI::play(ITrickerAPI& api) | |||||
| { | |||||
| api.UseSkill(0); | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto myPos = Point(selfInfo->x, selfInfo->y); | |||||
| api.PrintSelfInfo(); | |||||
| if (isStudentinView(api)) { | |||||
| // 追击 | |||||
| auto studentInfo = api.GetStudents(); | |||||
| auto selfInfo = api.GetSelfInfo(); | |||||
| auto dx = studentInfo[0]->x - selfInfo->x; | |||||
| auto dy = studentInfo[0]->y - selfInfo->y; | |||||
| auto dirc = atan2(dy, dx); | |||||
| avoidObstacles(api, dirc); | |||||
| api.Move(moveStep, dirc); | |||||
| double distance = sqrt(dx * dx + dy + dy); | |||||
| if (distance < Constants::basicAttackShortRange * 13 && api.UseSkill(1).get() == true) { // 飞刀 | |||||
| api.Attack(dirc); | |||||
| } | |||||
| if (distance < Constants::basicAttackShortRange) { | |||||
| api.Attack(dirc); | |||||
| } | |||||
| } | |||||
| } | |||||