该版本为“努力少女戏尔危”最终提交于EESAST的版本。
“努力少女戏尔危”在清华大学第六届人工智能挑战赛深度“学习”——毕业吧少女中荣获三等奖。
清华大学第六届人工智能挑战赛电子系赛道(原电子系第 24 届队式程序设计大赛 teamstyle24)
GitHub 镜像地址:THUAI6: GitHub Mirror
Gitee 镜像地址:THUAI6: Gitee Mirror
GitLink 镜像地址:THUAI6: GitLink Mirror
关于本届及历届清华大学人工智能挑战赛与队式程序设计大赛的更多内容参见:THUAI6 GitHub Wiki
已经整合为单文件 AI.cpp。
每个 Student/Tricker 都有三个好朋友,地理学家 Alice、预言家 Bob 和鸽子 Gugu。他们能力各异,但是都很热心地帮助 S/T。
在这次合作中,他们组成了一个指挥所,S/T 作为指挥所 Center 的长官,将不时向三个朋友提出疑问或请求,并且采纳朋友们的建议来做出更好的决策。
类 CommandPost(指挥所)保存了所有已知信息,并且具有 Geographer(地理学家)类对象 Alice、Pigeon(鸽子)类对象 Gugu 和 Predictor(预言家)类对象 Bob。
简单操作对应的函数在类 CommandPost 下直接定义。涉及寻路等复杂地图操作的函数由 Geographer 实现。涉及通信的操作由 Pigeon 实现。涉及位置记忆和预测的操作由 Predictor 实现。
由 CommandPost 基类派生了 CommandPostStudent 和 CommandPostTricker,用于增加定义涉及角色控制的函数(如攻击)。
程序区别了三种坐标 Cell Grid Geop。
Cell 是格子,范围是 0~49 的整数;Grid 是像素,范围是 0~49999 的整数;Geop 是实坐标。
提供了三种坐标相互转换的方法,例如 Grid Cell::ToGrid();。
#define USE_NEW_ASTAR 0 不启用新寻路算法,#define USE_NEW_ASTAR 1 将启用新寻路算法。
新的寻路算法基于原来寻路算法的路径,给出斜线直达的方案。目前新寻路算法尚未完善,因此启用将会导致编译不通过。
vector<vector<THUAI6::PlaceType>> Map;
50x50的数组,在CommandPost构造时确定,储存内容与api.GetFullMap()完全一致。
unsigned char Access[50][50];
随时更新。储存了所有格子的可访问性。有四个取值:
0U表示不可进入的格子;1U表示窗;2U表示可进入的格子(非草地);3U表示草地。可能导致变化产生的更新内容包括:
- 门的开和锁;
- 隐藏校门出现。(未实现)
std::vector<Cell> Classroom;std::vector<Cell> Gate;std::vector<Cell> HiddenGate;std::vector<Cell> Chest;std::vector<Cell> Grass;std::vector<Doors> Door;
在CommandPost构造时确定,不会变更。储存了所有对应类型格子所在的坐标。(issue: 事实上Door的DoorStatus原意应当更新,但未实现,且可以被上面的Access替代。)
int InfoMem[50][50];
随时更新。储存格子的值。可能储存了以下值:
- 对于
Classroom,储存了学习进度,取值0~10000000;- 对于
Chest,储存了开箱进度,取值0~10000000;- 对于
Gate,储存了开门进度,取值0~18000;- 对于
HiddenGate,应当储存开门进度,但未实现;- 对于其他类型的地块,未定义。
int LastUpdateFrame[50][50];
表示
InfoMem中记录的信息的最近更新时间。在进行队内信息传输的更新时需要比较这个数组的值以确保InfoMem仅在得到最新的信息时才发生变化。如果是自己在当前帧可以直接获取的信息(可以直接看到),那么值应当为当前帧数。
Geographer Alice;Predictor Bob;Pigeon Gugu;
三位专职。
-
CommandPost(IFooAPI &api); -
void Update(MapUpdateInfo upinfo, int t_);//更新地图信息,比如门和隐藏校门,需要约定info的格式 -
std::vector<THUAI6::PropType> GetInventory() { return Inventory; }// 查看背包 -
void OrganizeInventory(std::vector<unsigned char>Priority);// 整理背包 -
bool IsAccessible(int x, int y, bool WithWindows);
返回该格子是否为可进入的格子。如果
WithWindows则视窗户为可进入的格子。
-
bool MoveTo(Cell Dest, bool WithWindows);
往目的地移动一次。
WithWindows决定路线是否经过窗。如果已经在目的地旁边就返回false,否则返回true。
-
bool MoveToNearestClassroom(bool WithWindows);
往最近的作业移动一次。
WithWindows决定路线是否经过窗。如果有没写完的作业就返回true,否则返回false。
-
bool MoveToNearestGate(bool WithWindows);
往最近的关闭的校门移动一次。
WithWindows决定路线是否经过窗。如果有没开启的校门就返回true,否则返回false。
-
bool MoveToNearestOpenGate(bool WithWindows);
往最近的开启的校门移动一次。
WithWindows决定路线是否经过窗。如果有开启的校门就返回true,否则返回false。
-
bool MoveToNearestChest(bool WithWindows);
往最近的箱子移动一次。
WithWindows决定路线是否经过窗。如果有未开启的箱子就返回true,否则返回false。
-
bool NearCell(Cell P, int level = 1);
判断是否在格子P周围。level的不同取值决定范围大小。
level = 0判断当前是否在该格子上;level = 1判断是否在格子上或周围4格;level = 2判断是否在格子上或周围8格;level = 3判断直线距离是否不超过3格;level = 4判断直线距离是否不超过4格。
-
bool NearClassroom(bool checkProgress);
判断是否在作业旁边。如果
checkProgress = true则判断是否在没写完的作业旁边。
-
bool NearGate();
已经在关闭的校门旁边了吗?
-
bool NearOpenGate();
已经在开启的校门旁边了吗?
-
bool NearChest();
已经在没开过的箱子旁边了吗?
-
bool NearWindow();
已经在窗户旁边了吗?
-
bool InGrass();
已经在草丛里了吗?
-
void DirectLearning(bool WithWindows);
前往最近的作业并学习。如果
WithWindows = true则允许路线经过窗户。
-
void DirectOpeningChest(bool WithWindows);
前往最近的没开过的箱子并开箱。如果
WithWindows = true则允许路线经过窗户。
-
void DirectOpeningGate(bool WithWindows, bool CanDirectGraduate);
前往最近的关闭的校门并开门。如果
WithWindows = true则允许路线经过窗户。
-
void DirectGraduate(bool WithWindows);
前往最近的开启的校门并毕业。如果
WithWindows = true则允许路线经过窗户。
-
void DirectGrass(bool WithWindows);
前往最近的草丛并躲避。如果
WithWindows = true则允许路线经过窗户。
-
void DirectHide(Cell TrickerLocation, int TrickerViewRange, bool WithWindows);
前往最适合的草丛并躲避。
-
void DirectProp(std::vector<unsigned char>Priority, int DistanceInfluence, int PropInfluence, bool WithWindows);
前往已知价值最高的道具并捡道具。
-
void DirectUseProp(std::vector<unsigned char>Priority);
使用道具。
-
int CountFinishedClassroom() const; -
int CountNonemptyChest() const; -
int CountHiddenGate() const; -
int CountClosedGate() const; -
int CountOpenGate() const;
对相应状态的格子计数。
-
int GetChestProgress(int cellx, int celly); -
int GetGateProgress(int cellx, int celly); -
int GetClassroomProgress(int cellx, int celly); -
int GetDoorProgress(int cellx, int celly); -
int GetChestProgress(Cell cell) const; -
int GetGateProgress(Cell cell) const; -
int GetClassroomProgress(Cell cell) const; -
int GetDoorProgress(Cell cell) const;
返回相应格子的值。
-
CommandPostStudent(IStudentAPI& api) : CommandPost(api) {} -
void AutoUpdate(); -
void TeacherPunish(); -
double TeacherPunishCD(); -
void StraightAStudentWriteAnswers(); -
double StraightAStudentWriteAnswersCD(); -
void AtheleteCanBeginToCharge(); -
double AtheleteCanBeginToChargeCD(); -
void SunshineRouse(); -
void SunshineEncourage(); -
void SunshineInspire(); -
double SunshineRouseCD(); -
double SunshineEncourageCD(); -
double SunshineInspireCD();
-
CommandPostTricker(ITrickerAPI& api) : CommandPost(api) {} -
void AutoUpdate(); -
void AssassinDefaultAttack(int stux, int stuy);// 刺客普通攻击,传入学生坐标(stux,stuy) -
bool AssassinDefaultAttackOver(int rank);//判断能否稳定命中,传入目前能观察到的学生列表的第几个,从0开始计数 -
void AssassinBecomeInvisible(); -
void AssassinFlyingKnife(int stux, int stuy); -
double AssassinFlyingKnifeCD();
要发送信息,只需要调用对应的函数,传入目标角色ID和具体信息。
要接收信息,需要先调用receiveMessage()让鸽子尝试代收,如果有信息就会返回对应的信息种类,否则会返回NoMessage常量。鸽子会缓存信息字符串,之后再由用户调用读取对应信息的函数就可以返回相应的信息。
struct MapUpdateInfo
{
THUAI6::PlaceType type;
int x, y, val;
};
typedef std::shared_ptr<const THUAI6::Student> NeedHelpInfo;
typedef std::vector<std::shared_ptr<const THUAI6::Tricker>> TrickerInfo_t;
以下函数中dest参数表示发送目标的PlayerID。
-
void sendMapUpdate(int64_t dest, MapUpdateInfo muinfo); -
void sendMapUpdate(int64_t dest, THUAI6::PlaceType type, int x, int y, int val); -
void sendTrickerInfo(int64_t dest, TrickerInfo_t tricker); -
void sendNeedHelp(int64_t dest, NeedHelpInfo self);// TODO: 由于队友信息透明,所以不用传数据
以下接收具体信息的函数中,返回值的第一个值是信息发送时的帧数(等价于时间)。
-
int receiveMessage();// 返回接收到的信息类型 -
std::pair<int, MapUpdateInfo> receiveMapUpdate(); -
std::pair<int, TrickerInfo_t> receiveTrickerInfo(); -
std::pair<int, int> receiveNeedHelp();
Geographer主要处理寻路和复杂的地图信息。
-
Geographer(IFooAPI& api, CommandPost<IFooAPI>& Center_); -
bool IsValidWithoutWindows(int x, int y); -
bool IsValidWithWindows(int x, int y); -
bool IsDestination(int x, int y, Node dest); -
double CalculateH(int x, int y, Node dest); -
std::vector<Node> MakePath(std::array<std::array<Node, 50>, 50> map, Node dest); -
std::vector<Node> AStar(Node src, Node dest, bool WithWindows); -
int EstimateTime(Cell Dest);// 去目的地的预估时间 -
bool IsViewable(Cell Src, Cell Dest, int ViewRange);// 判断两个位置是否可视 -
Cell GetNearestGate(); -
Cell GetNearestClassroom();// 仅在没写完的作业中找 -
Cell GetNearestOpenGate();
定义角色可能处于的状态(已经定义了一些)。
限定每一帧要么维持原状态,要么变换到另一个状态,不能在一帧内多次变化状态(待定)。
具体实现上,使用一个switch-case进行不同状态的处理。
每一帧应当按照以下顺序执行:
- 调用
Center.AutoUpdate()进行信息更新; - 让
Center.Gugu接收信息,并对Gugu接收的信息进行处理,直到没有新信息,此时可以依据接收的信息进行状态变更的准备; - 决定变更状态或维持状态;
- 执行状态对应的行为。
之后会加入使用道具这一步骤。
-
Student太弱