C++ ADK User Guide
0.预备工作
请先前往Saiblo官网下载ADK包,确认内含adk.hpp、main.cpp、CMakeList.txt/Makefile。
adk.hpp为你的AI提供的功能有:与Judger进行通讯,发送你的操作、读取对手的操作等;游戏状态维护,提供逻辑维护代码,能为你处理玩家的操作并提供当前局面信息。
main.cpp为一个已经实现的简易AI的代码,其中的框架可以沿用至你的AI程序中,或者你也可以直接在其上进行修改。
CMakeList.txt与Makefile负责将你的AI与ADK进行编译和链接,请选择其中之一与adk.hpp和main.cpp共同压缩为zip压缩包,并在saiblo平台选择对应语言创建AI并提交。注意,如果你修改了main.cpp的文件名或新创建文件编写你的AI,请在CMakeList.txt或Makefile中同步修改。
1.开始!
首先来一波include:
再写一个main函数:
注意如果你要在main里面做其他的初始化工作,请一定要在SnakeGoAI run(argc, argv);这一行之前写,要不然你的AI就真的开始run起来了。
最后实现两个需要的函数:
Operation make_your_decision( const Snake &snake_to_operate, const Context &ctx ) {}
void game_over( int gameover_type, int winner, int p0_score, int p1_score ) {}
顾名思义,第一个函数代表决定snake_to_operate的操作Operation,可用的操作有:
| 操作类型 | 返回值 | 
|---|---|
| 向右移动 | OP_RIGHT | 
| 向上移动 | OP_UP | 
| 向左移动 | OP_LEFT | 
| 向下移动 | OP_DOWN | 
| 分裂 | OP_SPLIT | 
| 使用融化射线道具 | OP_RAILGUN | 
所以你可以直接写一个
这样你的所有蛇都会~~莫名其妙地~~向上跑,这就是一个合法的最小AI。
第二个函数是汇报游戏结束后的输赢与得分情况,各个参数顾名思义。
这两个函数的参数名称可以随意改变,如果你觉得snake_to_operate太长的话。
ADK会帮你处理建立stdio或socket通讯、读取游戏配置、读取物品列表、读取与处理你和对手的操作、维护游戏数据等操作,Context ctx内应该有你需要的一切信息,具体的使用方法请参考第三节API Reference。
2.样例AI的优化方向
供参考
- 第8-11行:何时使用融化射线?考虑使用后的收益
- 第14-23行:何时分裂更优?考虑分裂时长度以及其它因素
- 第77-111行:如何才能找到最近的道具的最优解,考虑安全性、可能的收益以及寻找的范围
- 第115-140行:怎样使蛇固化后围成的区域能够较大?在何处、以怎样的路径固化?
- 更多其他策略,如主动去围对手、从对手处逃跑
3. API Reference
3.0 结构体是啥
~~其实你可以翻一下程序设计基础的课件~~
为了更加清楚的组织一大堆状态和数据,我们使用结构体来进行层次化的组织,你可以使用{} 内依次提供数据值来创建一个结构体,如:
创建了一个x坐标为0,y坐标为1的Coord、即“坐标”结构体。
然后你就可以使用.来读取和修改内部的信息:
当然为了确保ADK内数据的正确性,提供给大家的结构体一般都只支持读取哦~
3.1 Coord结构体
简单的包含x和y的结构体,并且重载了== 用于方便地判断相等,如:
等价于
3.2 Snake结构体
对于Snake snake对象,支持以下操作:
- snake.length():长度
- snake[i]:从蛇头开始,第- i个位置的坐标,下标从零开始
- 进一步可以获得xy坐标:snake[0].x
- snake.coord_list:坐标列表,是一个- std::vector<Coord>
- snake.id:编号
- snake.length_bank:长度银行
- snake.camp:归属的玩家
- snake.railgun_item:拥有的融化射线道具
- 若没有融化射线,则这个道具的id小于零,即snake.railgun_item.id < 0为ture代表没有融化射线
- s1 == s2:可以判断两条蛇是否是同一条,其实就是包装了判断其id是否相等
3.3 Item结构体
一个道具Item item包含以下数据:
- item.x- item.y:坐标
- item.id:编号
- item.time:道具生成的时间/回合数
- item.type:类型,0为长度道具,2为融化射线~~(不要问1是啥,问就是游戏平衡性调整)~~
- item.param:参数,只对于长度道具有效,代表蛇吃了以后变长/给长度银行增加的数量
3.4 TwoDimArray
顾名思义,二维数组,和你想的用法一模一样:
第一行是构造一个二维数组,三个数字分别代表第一维长度,第二维长度和初始值。
支持拷贝赋值/构造,~~说人话~~就是把它赋值给另一个二维数组代表把内部元素都复制一遍:
不清楚那个尖括号是啥?可以理解为尖括号里面是什么类型那么数组的元素就是什么类型,比如上面的都是
int的二维数组,TwoDimArray<double>就是double的二维数组
3.5 Context结构体
对于一个游戏上下文环境Context ctx来说,支持以下操作(注意都带着一个小括号哦):
- ctx.wall_map():- int二维数组,当前回合的墙的分布情况,-1 / 0 / 1 代表无墙 / 0号选手的墙 / 1号选手的墙
- ctx.snake_map():- int二维数组,当前回合的蛇的分布情况,-1 / id 代表无 / id号蛇占据
- ctx.item_map():- int二维数组,当前回合的道具分布情况,-1 / id 代表无 / id号道具占据
- ctx.item_list():- Item的- std::vector,所有道具的列表
- ctx.snake_list_0()- ctx.snake_list_1():- Snake的- std::vector,分别为0号玩家和1号玩家的蛇的列表
- ctx.length()- ctx.width():地图的长度和宽度
- ctx.current_round()- ctx.max_round():当前回合与最大回合
- ctx.current_player():当前正在操作的玩家
- ctx.my_snakes()- ctx.opponents_snakes():获取我方蛇的列表和对方蛇的列表,免去了选手通过- current_player()判断的麻烦
- ctx.find_item(item_id)- ctx.find_snake(snake_id):通过- id获取道具- Item结构体或蛇- Snake结构体