Ataxx
作者
裁判程序:zhouhy zys
播放器:zhouhy
游戏规则
这里的同化棋游戏与国际通行的同化棋规则基本一致。
同化棋是一种二人棋盘游戏,黑白双方交替在7x7的棋盘上移动棋子。
玩家每次可以移动的位置是有限的,具体步骤解释如下:
- 选取现在在棋盘上己方的一枚棋子,再选择一个落子位置。要求落子处为空,既没有对方也没有己方的棋子。
- 选择的落子位置可以是以下二者之一:
- 落子位置在以选取的棋子为中心的3x3的范围内。此时选取的棋子会复制自身到落子位置。一共有8个位置可以选择。
- 落子位置在以选取的棋子为中心的3x3的范围外、5x5的范围内,此时选取的棋子会移动自身到落子位置,一共有16个位置可以选择。
- 总的来说,移动一格是“复制粘贴”,移动两格是“剪切粘贴”。
每次落子后,新位置的落子会将落子处紧邻的8个位置的棋子(如果有)都变成自己的棋子。
若没有上述这种位置则游戏结束,将所有无子的地方算作有子可下一方的棋子,然后数出双方棋子数,棋多者胜。
任意时刻对方棋子数为0时,己方胜利。
新规则:如果局面发生循环,即有一方的一步使得局面回到了曾经出现过的一个局面,那么对方将不能走出和当时一样的一步。否则会被判为不合法输入。
游戏回合数到400时,游戏会直接结束,数出双方棋子数,棋多者胜。
棋盘初始状态为
● | ○ | |||||
○ | ● |
黑方为先手。
请注意程序是有计算时间的限制的,每步要在1秒内完成!
游戏交互方式
提示
如果你不能理解以下交互方式,可以直接看#游戏样例程序,按照说明填写代码,并修改其中
// 做出决策(你只需修改以下部分)
到
// 决策结束,输出结果(你只需修改以上部分)
之间的部分即可!
本游戏与Botzone上其他游戏一样,使用相同的交互方式:Bot#交互
具体交互内容
简单交互
每条request是一行四个数字,用空格分隔,分别表示对手刚刚提子点的横纵坐标和落子点的横纵坐标。
第一回合如果收到-1 -1 -1 -1
,说明自己是黑方。其他回合不可能收到。
每次Bot需要给出的response也是一行四个数字,表示自己提子点横纵坐标和落子点的横纵坐标。
JSON交互
每回合Bot收到的request不是字符串,而是一个JSON对象,表示对方落子位置。格式如下:
{
"x0": Number, // 起点横坐标
"x1": Number, // 终点横坐标
"y0": Number // 起点纵坐标
"y1": Number // 终点纵坐标
}
Bot所需要输出的response也是格式相同的JSON对象,表示自己要落子的位置。
如果request是{"x0": -1, "x1": -1, "y0": -1, "y1": -1},则说明这是黑方的第一回合。
简单交互游戏样例程序
以下是C++编写的同化棋样例程序。落子策略采用随机方式。
ProcStep 函数用于模拟从(x0, y0)到(x1, y1)的一步棋。
// 同化棋(Ataxx)简易交互样例程序
// 随机策略
// 作者:zhouhy zys
// 游戏信息:http://www.botzone.org/games#Ataxx
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
int currBotColor; // 我所执子颜色(1为黑,-1为白,棋盘状态亦同)
int gridInfo[7][7] = { 0 }; // 先x后y,记录棋盘状态
int blackPieceCount = 2, whitePieceCount = 2;
int startX, startY, resultX, resultY;
static int delta[24][2] = { { 1,1 },{ 0,1 },{ -1,1 },{ -1,0 },
{ -1,-1 },{ 0,-1 },{ 1,-1 },{ 1,0 },
{ 2,0 },{ 2,1 },{ 2,2 },{ 1,2 },
{ 0,2 },{ -1,2 },{ -2,2 },{ -2,1 },
{ -2,0 },{ -2,-1 },{ -2,-2 },{ -1,-2 },
{ 0,-2 },{ 1,-2 },{ 2,-2 },{ 2,-1 } };
// 判断是否在地图内
inline bool inMap(int x, int y)
{
if (x < 0 || x > 6 || y < 0 || y > 6)
return false;
return true;
}
// 向Direction方向改动坐标,并返回是否越界
inline bool MoveStep(int &x, int &y, int Direction)
{
x = x + delta[Direction][0];
y = y + delta[Direction][1];
return inMap(x, y);
}
// 在坐标处落子,检查是否合法或模拟落子
bool ProcStep(int x0, int y0, int x1, int y1, int color)
{
if (color == 0)
return false;
if (x1 == -1) // 无路可走,跳过此回合
return true;
if (!inMap(x0, y0) || !inMap(x1, y1)) // 超出边界
return false;
if (gridInfo[x0][y0] != color)
return false;
int dx, dy, x, y, currCount = 0, dir;
int effectivePoints[8][2];
dx = abs((x0 - x1)), dy = abs((y0 - y1));
if ((dx == 0 && dy == 0) || dx > 2 || dy > 2) // 保证不会移动到原来位置,而且移动始终在5×5区域内
return false;
if (gridInfo[x1][y1] != 0) // 保证移动到的位置为空
return false;
if (dx == 2 || dy == 2) // 如果走的是5×5的外围,则不是复制粘贴
gridInfo[x0][y0] = 0;
else
{
if (color == 1)
blackPieceCount++;
else
whitePieceCount++;
}
gridInfo[x1][y1] = color;
for (dir = 0; dir < 8; dir++) // 影响邻近8个位置
{
x = x1 + delta[dir][0];
y = y1 + delta[dir][1];
if (!inMap(x, y))
continue;
if (gridInfo[x][y] == -color)
{
effectivePoints[currCount][0] = x;
effectivePoints[currCount][1] = y;
currCount++;
gridInfo[x][y] = color;
}
}
if (currCount != 0)
{
if (color == 1)
{
blackPieceCount += currCount;
whitePieceCount -= currCount;
}
else
{
whitePieceCount += currCount;
blackPieceCount -= currCount;
}
}
return true;
}
int main()
{
istream::sync_with_stdio(false);
int x0, y0, x1, y1;
// 初始化棋盘
gridInfo[0][0] = gridInfo[6][6] = 1; //|黑|白|
gridInfo[6][0] = gridInfo[0][6] = -1; //|白|黑|
// 分析自己收到的输入和自己过往的输出,并恢复状态
int turnID;
currBotColor = -1; // 第一回合收到坐标是-1, -1,说明我是黑方
cin >> turnID;
for (int i = 0; i < turnID - 1; i++)
{
// 根据这些输入输出逐渐恢复状态到当前回合
cin >> x0 >> y0 >> x1 >> y1;
if (x1 >= 0)
ProcStep(x0, y0, x1, y1, -currBotColor); // 模拟对方落子
else
currBotColor = 1;
cin >> x0 >> y0 >> x1 >> y1;
if (x1 >= 0)
ProcStep(x0, y0, x1, y1, currBotColor); // 模拟己方落子
}
// 看看自己本回合输入
cin >> x0 >> y0 >> x1 >> y1;
if (x1 >= 0)
ProcStep(x0, y0, x1, y1, -currBotColor); // 模拟对方落子
else
currBotColor = 1;
// 找出合法落子点
int beginPos[1000][2], possiblePos[1000][2], posCount = 0, choice, dir;
for (y0 = 0; y0 < 7; y0++)
for (x0 = 0; x0 < 7; x0++)
{
if (gridInfo[x0][y0] != currBotColor)
continue;
for (dir = 0; dir < 24; dir++)
{
x1 = x0 + delta[dir][0];
y1 = y0 + delta[dir][1];
if (!inMap(x1, y1))
continue;
if (gridInfo[x1][y1] != 0)
continue;
beginPos[posCount][0] = x0;
beginPos[posCount][1] = y0;
possiblePos[posCount][0] = x1;
possiblePos[posCount][1] = y1;
posCount++;
}
}
// 做出决策(你只需修改以下部分)
int startX, startY, resultX, resultY;
if (posCount > 0)
{
srand(time(0));
choice = rand() % posCount;
startX = beginPos[choice][0];
startY = beginPos[choice][1];
resultX = possiblePos[choice][0];
resultY = possiblePos[choice][1];
}
else
{
startX = -1;
startY = -1;
resultX = -1;
resultY = -1;
}
// 决策结束,输出结果(你只需修改以上部分)
cout << startX << " " << startY << " " << resultX << " " << resultY;
return 0;
}
JSON交互游戏样例程序
以下是C++编写的同化棋样例程序。落子策略采用随机方式。
ProcStep 函数用于模拟从(x0, y0)到(x1, y1)的一步棋。
本地编译的方式请查看 JSONCPP。
// 同化棋(Ataxx)样例程序
// 随机策略
// 作者:zhouhy zys
// 游戏信息:http://www.botzone.org/games#Ataxx
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include "jsoncpp/json.h" // C++编译时默认包含此库
using namespace std;
int currBotColor; // 我所执子颜色(1为黑,-1为白,棋盘状态亦同)
int gridInfo[7][7] = { 0 }; // 先x后y,记录棋盘状态
int blackPieceCount = 2, whitePieceCount = 2;
static int delta[24][2] = { { 1,1 },{ 0,1 },{ -1,1 },{ -1,0 },
{ -1,-1 },{ 0,-1 },{ 1,-1 },{ 1,0 },
{ 2,0 },{ 2,1 },{ 2,2 },{ 1,2 },
{ 0,2 },{ -1,2 },{ -2,2 },{ -2,1 },
{ -2,0 },{ -2,-1 },{ -2,-2 },{ -1,-2 },
{ 0,-2 },{ 1,-2 },{ 2,-2 },{ 2,-1 } };
// 判断是否在地图内
inline bool inMap(int x, int y)
{
if (x < 0 || x > 6 || y < 0 || y > 6)
return false;
return true;
}
// 向Direction方向改动坐标,并返回是否越界
inline bool MoveStep(int &x, int &y, int Direction)
{
x = x + delta[Direction][0];
y = y + delta[Direction][1];
return inMap(x, y);
}
// 在坐标处落子,检查是否合法或模拟落子
bool ProcStep(int x0, int y0, int x1, int y1, int color)
{
if (color == 0)
return false;
if (x1 == -1) // 无路可走,跳过此回合
return true;
if (!inMap(x0, y0) || !inMap(x1, y1)) // 超出边界
return false;
if (gridInfo[x0][y0] != color)
return false;
int dx, dy, x, y, currCount = 0, dir;
int effectivePoints[8][2];
dx = abs((x0 - x1)), dy = abs((y0 - y1));
if ((dx == 0 && dy == 0) || dx > 2 || dy > 2) // 保证不会移动到原来位置,而且移动始终在5×5区域内
return false;
if (gridInfo[x1][y1] != 0) // 保证移动到的位置为空
return false;
if (dx == 2 || dy == 2) // 如果走的是5×5的外围,则不是复制粘贴
gridInfo[x0][y0] = 0;
else
{
if (color == 1)
blackPieceCount++;
else
whitePieceCount++;
}
gridInfo[x1][y1] = color;
for (dir = 0; dir < 8; dir++) // 影响邻近8个位置
{
x = x1 + delta[dir][0];
y = y1 + delta[dir][1];
if (!inMap(x, y))
continue;
if (gridInfo[x][y] == -color)
{
effectivePoints[currCount][0] = x;
effectivePoints[currCount][1] = y;
currCount++;
gridInfo[x][y] = color;
}
}
if (currCount != 0)
{
if (color == 1)
{
blackPieceCount += currCount;
whitePieceCount -= currCount;
}
else
{
whitePieceCount += currCount;
blackPieceCount -= currCount;
}
}
return true;
}
int main()
{
int x0, y0, x1, y1;
// 初始化棋盘
gridInfo[0][0] = gridInfo[6][6] = 1; //|黑|白|
gridInfo[6][0] = gridInfo[0][6] = -1; //|白|黑|
// 读入JSON
string str;
getline(cin, str);
Json::Reader reader;
Json::Value input;
reader.parse(str, input);
// 分析自己收到的输入和自己过往的输出,并恢复状态
int turnID = input["responses"].size();
currBotColor = input["requests"][(Json::Value::UInt) 0]["x0"].asInt() < 0 ? 1 : -1; // 第一回合收到坐标是-1, -1,说明我是黑方
for (int i = 0; i < turnID; i++)
{
// 根据这些输入输出逐渐恢复状态到当前回合
x0 = input["requests"][i]["x0"].asInt();
y0 = input["requests"][i]["y0"].asInt();
x1 = input["requests"][i]["x1"].asInt();
y1 = input["requests"][i]["y1"].asInt();
if (x1 >= 0)
ProcStep(x0, y0, x1, y1, -currBotColor); // 模拟对方落子
x0 = input["responses"][i]["x0"].asInt();
y0 = input["responses"][i]["y0"].asInt();
x1 = input["responses"][i]["x1"].asInt();
y1 = input["responses"][i]["y1"].asInt();
if (x1 >= 0)
ProcStep(x0, y0, x1, y1, currBotColor); // 模拟己方落子
}
// 看看自己本回合输入
x0 = input["requests"][turnID]["x0"].asInt();
y0 = input["requests"][turnID]["y0"].asInt();
x1 = input["requests"][turnID]["x1"].asInt();
y1 = input["requests"][turnID]["y1"].asInt();
if (x1 >= 0)
ProcStep(x0, y0, x1, y1, -currBotColor); // 模拟对方落子
// 找出合法落子点
int beginPos[1000][2], possiblePos[1000][2], posCount = 0, choice, dir;
for (y0 = 0; y0 < 7; y0++)
for (x0 = 0; x0 < 7; x0++)
{
if (gridInfo[x0][y0] != currBotColor)
continue;
for (dir = 0; dir < 24; dir++)
{
x1 = x0 + delta[dir][0];
y1 = y0 + delta[dir][1];
if (!inMap(x1, y1))
continue;
if (gridInfo[x1][y1] != 0)
continue;
beginPos[posCount][0] = x0;
beginPos[posCount][1] = y0;
possiblePos[posCount][0] = x1;
possiblePos[posCount][1] = y1;
posCount++;
}
}
// 做出决策(你只需修改以下部分)
int startX, startY, resultX, resultY;
if (posCount > 0)
{
srand(time(0));
choice = rand() % posCount;
startX = beginPos[choice][0];
startY = beginPos[choice][1];
resultX = possiblePos[choice][0];
resultY = possiblePos[choice][1];
}
else
{
startX = -1;
startY = -1;
resultX = -1;
resultY = -1;
}
// 决策结束,输出结果(你只需修改以上部分)
Json::Value ret;
ret["response"]["x0"] = startX;
ret["response"]["y0"] = startY;
ret["response"]["x1"] = resultX;
ret["response"]["y1"] = resultY;
Json::FastWriter writer;
cout << writer.write(ret) << endl;
return 0;
}