“裁判”的版本间的差异
来自Botzone Wiki
Administrator(讨论 | 贡献) (→资源限制) |
(此版本已被标记将进行翻译) |
||
(未显示2个用户的3个中间版本) | |||
第1行: | 第1行: | ||
− | 裁判是[[Botzone]]进行[[对局]]评测的后台程序,同时也是每个[[游戏]]必不可少的部分。 | + | <languages/> |
+ | <translate> | ||
+ | <!--T:1--> | ||
+ | 裁判是[[Special:MyLanguage/Botzone|Botzone]]进行[[Special:MyLanguage/对局|对局]]评测的后台程序,同时也是每个[[Special:MyLanguage/游戏|游戏]]必不可少的部分。 | ||
− | 裁判与[[Bot]]一样可以用各种语言编写,主要作用是通过[[控制器]]接收各个玩家的输入并根据规则进行判断,然后给出下一回合的指令。 | + | <!--T:2--> |
+ | 裁判与[[Special:MyLanguage/Bot|Bot]]一样可以用各种语言编写,主要作用是通过[[Special:MyLanguage/控制器|控制器]]接收各个玩家的输入并根据规则进行判断,然后给出下一回合的指令。 | ||
− | |||
− | + | == 交互 == <!--T:3--> | |
+ | <!--T:4--> | ||
+ | 裁判每回合开始时会被运行一次,每次生命周期均为一次输入、一次输出,每次输入会包括裁判和所有玩家以往跟平台的所有交互内容。交互格式为'''单行[[Special:MyLanguage/JSON|JSON]]'''。 | ||
+ | |||
+ | <!--T:5--> | ||
因此,裁判的程序运行流程应当是: | 因此,裁判的程序运行流程应当是: | ||
*启动程序 | *启动程序 | ||
第14行: | 第21行: | ||
**根据玩家的上回合response演算下一局面 | **根据玩家的上回合response演算下一局面 | ||
***如果游戏结束,给出玩家分数 | ***如果游戏结束,给出玩家分数 | ||
− | *** | + | ***如果游戏继续,则确定哪些玩家应当进行动作,并给这些玩家发送request(只有被发送request的玩家才会启动) |
*关闭程序 | *关闭程序 | ||
− | |||
− | 同[[Bot#资源限制]]。 | + | ===资源限制=== <!--T:6--> |
+ | |||
+ | <!--T:7--> | ||
+ | 同[[Special:MyLanguage/Bot#资源限制|Bot#资源限制]]。 | ||
+ | |||
− | ===交互格式=== | + | ===交互格式=== <!--T:8--> |
− | ===='''裁判得到的输入'''==== | + | |
+ | ===='''裁判得到的输入'''==== <!--T:9--> | ||
+ | |||
+ | <!--T:10--> | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
{ | { | ||
第51行: | 第64行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
+ | ===='''裁判应该给出的输出'''==== <!--T:11--> | ||
+ | |||
+ | <!--T:12--> | ||
如果游戏继续: | 如果游戏继续: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
第64行: | 第79行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | <!--T:13--> | ||
如果游戏结束: | 如果游戏结束: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
第76行: | 第92行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
− | 本地编译的方式请查看 [[JSONCPP]]。 | + | ====关于 initdata==== <!--T:14--> |
+ | |||
+ | <!--T:15--> | ||
+ | initdata 是指裁判程序的初始化数据,通常用于指定随机种子、地图大小、初始手牌等参数。 | ||
+ | |||
+ | <!--T:16--> | ||
+ | 在第一回合,裁判收到的 initdata 可能是一个字符串或者对象,为用户或比赛开启对局时所指定的 initdata。 | ||
+ | |||
+ | <!--T:17--> | ||
+ | 在第一回合,裁判有机会在输出对象中的 initdata 域里写入初始化数据(字符串或对象)并保存,此后回合收到的 initdata 均为这次输出的 initdata。通常用于当用户没有指定随机种子的时候保存自己产生的随机种子,或者保存一个通过随机种子产生的地图地形或初始手牌信息等。 | ||
+ | |||
+ | |||
+ | ==样例程序== <!--T:18--> | ||
+ | |||
+ | <!--T:19--> | ||
+ | 以下给出C++的[[Special:MyLanguage/JSON|JSON]]裁判样例程序: | ||
+ | |||
+ | <!--T:20--> | ||
+ | 本地编译的方式请查看 [[Special:MyLanguage/JSONCPP|JSONCPP]]。 | ||
+ | <!--T:21--> | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include <iostream> | #include <iostream> | ||
第89行: | 第122行: | ||
+ | <!--T:22--> | ||
// 注意黑方为0号玩家 | // 注意黑方为0号玩家 | ||
int currBotColor; // 正在等待输出结果的BOT | int currBotColor; // 正在等待输出结果的BOT | ||
第95行: | 第129行: | ||
string blackName = "", whiteName = ""; | string blackName = "", whiteName = ""; | ||
+ | <!--T:23--> | ||
static bool MoveStep(int &x, int &y, int Direction) | static bool MoveStep(int &x, int &y, int Direction) | ||
{ | { | ||
第110行: | 第145行: | ||
} | } | ||
+ | <!--T:24--> | ||
bool ProcStep(int xPos, int yPos, int color, bool checkOnly = false) | bool ProcStep(int xPos, int yPos, int color, bool checkOnly = false) | ||
{ | { | ||
第186行: | 第222行: | ||
} | } | ||
+ | <!--T:25--> | ||
bool CheckIfHasValidMove(int color) | bool CheckIfHasValidMove(int color) | ||
{ | { | ||
第196行: | 第233行: | ||
} | } | ||
+ | <!--T:26--> | ||
int main() | int main() | ||
{ | { | ||
第206行: | 第244行: | ||
− | gridInfo[3][4] = gridInfo[4][3] = 1; //|白|黑| | + | <!--T:27--> |
+ | gridInfo[3][4] = gridInfo[4][3] = 1; //|白|黑| | ||
gridInfo[3][3] = gridInfo[4][4] = -1; //|黑|白| | gridInfo[3][3] = gridInfo[4][4] = -1; //|黑|白| | ||
currBotColor = 1; // 先手为黑 | currBotColor = 1; // 先手为黑 | ||
第292行: | 第331行: | ||
{ | { | ||
− | output["display"]["x"] = currX; | + | <!--T:28--> |
+ | output["display"]["x"] = currX; | ||
output["display"]["y"] = currY; | output["display"]["y"] = currY; | ||
output["command"] = "request"; | output["command"] = "request"; | ||
第398行: | 第438行: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | </translate> |
2020年2月10日 (一) 15:53的最新版本
裁判是Botzone进行对局评测的后台程序,同时也是每个游戏必不可少的部分。
裁判与Bot一样可以用各种语言编写,主要作用是通过控制器接收各个玩家的输入并根据规则进行判断,然后给出下一回合的指令。
交互
裁判每回合开始时会被运行一次,每次生命周期均为一次输入、一次输出,每次输入会包括裁判和所有玩家以往跟平台的所有交互内容。交互格式为单行JSON。
因此,裁判的程序运行流程应当是:
- 启动程序
- 从平台获取以往的交互内容和上回合玩家输出的response
- 根据获得的交互内容(自己以前发出的request + 玩家输出的response)进行计算
- 恢复局面到最新状态
- 根据玩家的上回合response演算下一局面
- 如果游戏结束,给出玩家分数
- 如果游戏继续,则确定哪些玩家应当进行动作,并给这些玩家发送request(只有被发送request的玩家才会启动)
- 关闭程序
资源限制
同Bot#资源限制。
交互格式
裁判得到的输入
{
"log": [
{
"memory": 2,
"output": {
"command": "request",
"content": ...,
"display": ...
}, // 这里是裁判输出的request的主体
"time": 2,
"verdict": "OK"
}, // 第0、2、4、...项,表示裁判第1、2、3...回合的输出
{
"0": {
"memory": 2,
"time": 9,
"verdict": "OK",
"response": ... // 这里是玩家输出的response的主体
}
}, // 第1、3、5、...项,表示玩家们第1、2、3...回合的输出
...
],
"initdata": "" // 初始化数据
}
裁判应该给出的输出
如果游戏继续:
{
"command": "request",
"content": {
"0": ... // 要运行的玩家和给它的request
},
"display": ... // 给播放器的信息,可以是JSON对象
}
如果游戏结束:
{
"command": "finish",
"content": {
"0": 1, // 每个玩家的分数
"1": 2
},
"display": ... // 给播放器的信息,可以是JSON对象
}
关于 initdata
initdata 是指裁判程序的初始化数据,通常用于指定随机种子、地图大小、初始手牌等参数。
在第一回合,裁判收到的 initdata 可能是一个字符串或者对象,为用户或比赛开启对局时所指定的 initdata。
在第一回合,裁判有机会在输出对象中的 initdata 域里写入初始化数据(字符串或对象)并保存,此后回合收到的 initdata 均为这次输出的 initdata。通常用于当用户没有指定随机种子的时候保存自己产生的随机种子,或者保存一个通过随机种子产生的地图地形或初始手牌信息等。
样例程序
以下给出C++的JSON裁判样例程序:
本地编译的方式请查看 JSONCPP。
#include <iostream>
#include <sstream>
#include <string>
#include "jsoncpp/json.h"
using namespace std;
// 注意黑方为0号玩家
int currBotColor; // 正在等待输出结果的BOT
int gridInfo[8][8] = { 0 }; // 先x后y
int blackPieceCount = 2, whitePieceCount = 2;
string blackName = "", whiteName = "";
static bool MoveStep(int &x, int &y, int Direction)
{
if (Direction == 0 || Direction == 6 || Direction == 7)
x++;
if (Direction == 0 || Direction == 1 || Direction == 2)
y++;
if (Direction == 2 || Direction == 3 || Direction == 4)
x--;
if (Direction == 4 || Direction == 5 || Direction == 6)
y--;
if (x < 0 || x > 7 || y < 0 || y > 7)
return false;
return true;
}
bool ProcStep(int xPos, int yPos, int color, bool checkOnly = false)
{
if (xPos == -1)
return true;
if (xPos < 0 || xPos > 7 || yPos < 0 || yPos > 7)
return false;
int effectivePoints[8][2];
int dir, x, y, currCount;
bool isValidMove = false;
if (gridInfo[xPos][yPos] != 0)
return false;
for (dir = 0; dir < 8; dir++)
{
x = xPos;
y = yPos;
currCount = 0;
while (1)
{
if (!MoveStep(x, y, dir))
{
currCount = 0;
break;
}
if (gridInfo[x][y] == -color)
{
currCount++;
effectivePoints[currCount][0] = x;
effectivePoints[currCount][1] = y;
}
else if (gridInfo[x][y] == 0)
{
currCount = 0;
break;
}
else
{
break;
}
}
if (currCount != 0)
{
isValidMove = true;
if (checkOnly)
return true;
if (color == 1)
{
blackPieceCount += currCount;
whitePieceCount -= currCount;
}
else
{
whitePieceCount += currCount;
blackPieceCount -= currCount;
}
while (currCount > 0)
{
x = effectivePoints[currCount][0];
y = effectivePoints[currCount][1];
gridInfo[x][y] *= -1;
currCount--;
}
}
}
if (isValidMove)
{
gridInfo[xPos][yPos] = color;
if (color == 1)
blackPieceCount++;
else
whitePieceCount++;
return true;
}
else
return false;
}
bool CheckIfHasValidMove(int color)
{
int x, y;
for (y = 0; y < 8; y++)
for (x = 0; x < 8; x++)
if (ProcStep(x, y, color, true))
return true;
return false;
}
int main()
{
string str;
getline(cin, str);
Json::Reader reader;
Json::Value input, output;
reader.parse(str, input);
input = input["log"];
gridInfo[3][4] = gridInfo[4][3] = 1; //|白|黑|
gridInfo[3][3] = gridInfo[4][4] = -1; //|黑|白|
currBotColor = 1; // 先手为黑
if (input.size() == 0)
{
output["display"] = "";
output["command"] = "request";
output["content"]["0"]["x"] = -1;
output["content"]["0"]["y"] = -1;
}
else
{
for (int i = 1; i < input.size(); i += 2)
{
bool isLast = i == input.size() - 1;
Json::Value content;
Json::Value display;
if (currBotColor == 1) // 0号玩家 / 黑方
{
Json::Value answer = input[i]["0"]["response"].isNull() ? input[i]["0"]["content"] : input[i]["0"]["response"];
if (((answer.isString() &&
reader.parse(answer.asString(), content)) ||
(answer.isObject() &&
(content = answer, true))) &&
content["x"].isInt() && content["y"].isInt()) // 保证输入格式正确
{
int currX = content["x"].asInt();
int currY = content["y"].asInt();
if (currX == -1 && isLast) // bot选择PASS
{
if (!CheckIfHasValidMove(currBotColor)) // 他应该PASS
{
output["display"]["x"] = -1;
output["display"]["y"] = -1;
output["command"] = "request";
output["content"]["1"]["x"] = -1;
output["content"]["1"]["y"] = -1;
}
else // 他不应该PASS
{
// Todo: Bug here--
output["display"]["err"] = "HE_THOUGHT_THAT_HE_COULDNOT_MOVE_BUT_IN_FACT_HE_CAN";
output["display"]["winner"] = 1;
output["command"] = "finish"; // 判输
output["content"]["0"] = 0;
output["content"]["1"] = 2;
}
}
else if (!ProcStep(currX, currY, currBotColor) && isLast) // 不合法棋步!
{
output["display"]["err"] = "INVALIDMOVE";
output["display"]["winner"] = 1;
output["command"] = "finish"; // 判输
output["content"]["0"] = 0;
output["content"]["1"] = 2;
}
else if (isLast) // 正常棋步
{
output["display"]["x"] = currX;
output["display"]["y"] = currY;
if (!CheckIfHasValidMove(currBotColor) && !CheckIfHasValidMove(-currBotColor)) // 游戏结束
{
if (blackPieceCount > whitePieceCount)
{
output["display"]["winner"] = 0;
output["command"] = "finish";
output["content"]["0"] = 2;
output["content"]["1"] = 0;
}
else if (blackPieceCount < whitePieceCount)
{
output["display"]["winner"] = 1;
output["command"] = "finish";
output["content"]["0"] = 0;
output["content"]["1"] = 2;
}
else
{
output["command"] = "finish";
output["content"]["0"] = 1;
output["content"]["1"] = 1;
}
}
else
{
output["display"]["x"] = currX;
output["display"]["y"] = currY;
output["command"] = "request";
output["content"]["1"]["x"] = currX;
output["content"]["1"]["y"] = currY;
}
}
}
else if (isLast)
{
output["display"]["err"] = "INVALIDMOVE";
output["display"]["winner"] = 1;
output["command"] = "finish"; // 判输
output["content"]["0"] = 0;
output["content"]["1"] = 2;
}
}
else
{
Json::Value answer = input[i]["1"]["response"].isNull() ? input[i]["1"]["content"] : input[i]["1"]["response"];
if (((answer.isString() &&
reader.parse(answer.asString(), content)) ||
(answer.isObject() &&
(content = answer, true))) &&
content["x"].isInt() && content["y"].isInt()) // 保证输入格式正确
{
int currX = content["x"].asInt();
int currY = content["y"].asInt();
if (currX == -1 && isLast) // bot选择PASS
{
if (!CheckIfHasValidMove(currBotColor)) // 他应该PASS
{
output["display"]["x"] = -1;
output["display"]["y"] = -1;
output["command"] = "request";
output["content"]["0"]["x"] = -1;
output["content"]["0"]["y"] = -1;
}
else // 他不应该PASS
{
output["display"]["err"] = "HE_THOUGHT_THAT_HE_COULDNOT_MOVE_BUT_IN_FACT_HE_CAN";
output["display"]["winner"] = 0;
output["command"] = "finish"; // 判输
output["content"]["0"] = 2;
output["content"]["1"] = 0;
}
}
else if (!ProcStep(currX, currY, currBotColor) && isLast) // 不合法棋步!
{
output["display"]["err"] = "INVALIDMOVE";
output["display"]["winner"] = 0;
output["command"] = "finish"; // 判输
output["content"]["0"] = 2;
output["content"]["1"] = 0;
}
else if (isLast) // 正常棋步
{
output["display"]["x"] = currX;
output["display"]["y"] = currY;
if (!CheckIfHasValidMove(currBotColor) && !CheckIfHasValidMove(-currBotColor)) // 游戏结束
{
if (blackPieceCount > whitePieceCount)
{
output["display"]["winner"] = 0;
output["command"] = "finish";
output["content"]["0"] = 2;
output["content"]["1"] = 0;
}
else if (blackPieceCount < whitePieceCount)
{
output["display"]["winner"] = 1;
output["command"] = "finish";
output["content"]["0"] = 0;
output["content"]["1"] = 2;
}
else
{
output["command"] = "finish";
output["content"]["0"] = 1;
output["content"]["1"] = 1;
}
}
else
{
output["command"] = "request";
output["content"]["0"]["x"] = currX;
output["content"]["0"]["y"] = currY;
}
}
}
else if (isLast)
{
output["display"]["err"] = "INVALIDMOVE";
output["display"]["winner"] = 0;
output["command"] = "finish"; // 判输
output["content"]["0"] = 2;
output["content"]["1"] = 0;
}
}
currBotColor *= -1;
}
}
Json::FastWriter writer;
cout << writer.write(output) << endl;
}