“Tetris”的版本间的差异

来自Botzone Wiki
跳转至: 导航搜索
描述自己的方块落地序列
1个标签移动版编辑
第83行: 第83行:
 
记元组(横坐标,纵坐标,姿态)构成了一个'''纵坐标单调递减'''的序列[[File:Tetris.B.png]],该序列有如下要求:
 
记元组(横坐标,纵坐标,姿态)构成了一个'''纵坐标单调递减'''的序列[[File:Tetris.B.png]],该序列有如下要求:
 
* 每个元组指定的方块状态必须是合法的(即方块可以放置在指定位置)
 
* 每个元组指定的方块状态必须是合法的(即方块可以放置在指定位置)
 +
* 最后一个元组表示方块的最终状态
 
* [[File:Tetris.C.png]]必须是不合法的(即方块最后停在了不能继续下落的位置)
 
* [[File:Tetris.C.png]]必须是不合法的(即方块最后停在了不能继续下落的位置)
 
* 必须保证方块能从[[File:Tetris.D.png]]“畅通无阻”地、'''没有任何旋转和水平移动地'''落到[[File:Tetris.E.png]]
 
* 必须保证方块能从[[File:Tetris.D.png]]“畅通无阻”地、'''没有任何旋转和水平移动地'''落到[[File:Tetris.E.png]]

2017年4月20日 (四) 11:05的版本

俄罗斯方块(Tetris)是Botzone平台上的双人回合制游戏

作者

裁判程序 zys 播放器 zhouhy

故事

(尚未完成,稍安勿躁)

书接上文,自那琼柰派入门考校之后,四位敝衣少年经历种种玄学坎坷,终于拜得上仙为师,却哪知,道门也不是一个远离纷争的所在。他们上了山入了门,没想到师门给他们的第一件任务,竟是远渡重洋查探魔门。

「本派密探报,有海外倭岛名唤『贾巴沃克』者,以望气之法查探,魔气四溢,其中必有蹊跷。尔等初入本派,须有作为,这便是你们立功的大好时机!」

「可是,我们…还什么都…」

「噫,莫急,为师自有秘宝可保你们平安~」

说着,少年们的面前,四只小方块闪着奇异的光芒,带着流光飘进了他们的衣带。

「记得,若有危难,切莫逞能,四宝合一,方有生机!」

……

风和日丽的贾巴沃克岛上,似乎有着一股奇怪的气场,让人感到说不出的烦闷。

「唔噗噗噗……成天与代码之类的死物打交道也真是无聊,不过看来,有人正带着有趣的风物来玩了呢」

(未完待续)

人类玩家操作说明

两种操作方式任选其一即可。

  • 鼠标
    • 点击对方场地上的图标可以给对手指定方块
    • 拖动块可以移动位置,也可以穿墙(仅限可以直接从顶部落到的地方)
    • 左键点击块可以使之坠落到底
    • 右键点击屏幕可以提交操作(此时方块必须落地)
  • 键盘
    • 数字1~7可以给对手指定方块
    • 上下左右键可以移动位置
    • 英文/键可以将方块坠落到底
    • 回车键可以提交操作(此时方块必须落地)
    • 英文.键可以枚举方块的下一个可能的初始状态(用于在场地顶部转不过来之类的情况)

游戏规则

本游戏为双人回合制游戏,每个玩家在独立的矩阵场地上进行游戏。

每回合,双方玩家同时决策,既需要控制自己的方块落地,又需要为对手指定下一回合的方块。

玩家消去行后,方块会转移到对方场地底部,从而给对方增加难度。

地图

每个玩家的场地是高20格、宽10格的矩阵。在程序中,坐标是(x, y)的形式,其中x是横坐标,y是纵坐标。最左下方的格子的坐标是(1, 1)。

方块

游戏中,方块共有七种类型,每种类型的方块有四种姿态(即方向)。

方块只能逆时针旋转(即上左下右的顺序进行旋转)。旋转时旋转中心不动。

Tetris.Blocks.png

上图的方块在程序中,从左到右的类型编号分别是数字0~6,上左下右四种方向分别对应着数字0~3。

方块的坐标即其旋转中心的坐标。

给对手指定方块

第一回合,双方的方块类型由玄学系统指定,而且是相同的类型。

除了第一回合之外,每个回合的方块类型都是由对方指定的。因此,每回合玩家都要给出对方下一个方块的类型。

指定方块时,必须保证对方的各类方块的数目的极差(即最大值减最小值)不超过2,否则将会被视为非法行为。

描述自己的方块落地序列

每回合,玩家可以得知自己本回合控制的方块类型。

为了保证方块能够穿过重重障碍来到地面,我们要求玩家给出方块在落地的必经之路中,在各个纵坐标下的某一时刻的姿态,即方块落地序列。

记元组(横坐标,纵坐标,姿态)构成了一个纵坐标单调递减的序列Tetris.B.png,该序列有如下要求:

  • 每个元组指定的方块状态必须是合法的(即方块可以放置在指定位置)
  • 最后一个元组表示方块的最终状态
  • Tetris.C.png必须是不合法的(即方块最后停在了不能继续下落的位置)
  • 必须保证方块能从Tetris.D.png“畅通无阻”地、没有任何旋转和水平移动地落到Tetris.E.png
    • 这里如果方块里有的块的高度超过了20,也视为合法。具体判定方式可以参看样例的checkDirectDropTo函数。
  • 对于相邻的一对元组Tetris.F.png
    • 必须保证Tetris.G.new.png
    • 如果Tetris.H.new.png,那么必须保证Tetris.I.pngTetris.J.png,而且这中间的路径“畅通无阻”,没有任何旋转和水平移动
    • 否则Tetris.Z.png,此时对这两个状态无要求,但是中间需要存在一条能够通过若干旋转、平移操作到达下一状态的路径。系统会自动枚举该路径。

注意,虽然序列中要求纵坐标单调递减,但是并不意味着不能够水平移动或者原地旋转,需要作出这种动作的时候只要保证纵坐标相差是1即可。我们只是要求每个纵坐标的状态最多出现一次,以避免冗余。

当且仅当纵坐标相差是1的时候,中间省略的部分会由系统自动枚举,有任何一种可能性就算通过。

示例

Tetris.Demo.png

一个z型方块以“左”姿态开始的下落路径如图,其中E点发生了一次逆时针旋转(变成了“下”姿态)。记旋转前的E为“E”,旋转后的E为“e”。

为了描述该路径,所产生的序列可以是以下任意一个:(当然此处没有列出所有可能性)

  • 对于每一个y任选一个
    • 喜欢靠左的,比如 XACDEJKN
    • 喜欢靠右的,比如 XBCDGHMN
  • 省略同形态的下落,比如ACEHKN或XBEHKN
  • 就选靠左的,比如ACEJKN
  • 除此之外还有可能有XBDeIMN这种谜之组合等

方块的消除与转移

每回合方块落地后,如果有行已满,那么这些行将会被“消除”,其它的行下落补上空行。

被消除的行并不会消失,而是会去除最后一个落地的方块(即本回合刚刚造成这几行消除的方块),然后按照同样的顺序堆叠起来,放置在对方的场地底部。对方场地原有的方块就会被“顶起来”。

积分

玩家具有积分,但是仅用于平局时的处理。

对于每一回合,玩家一次性消去1、2、3、4行的积分分别是1、3、5、7。

总积分是上述积分的和。

胜负

一切非法行为会被立即判负,包括程序崩溃、超时、坐标越界、格式错误等。

在发生方块转移后,如果场地的最高方块高度超过边界,则会被判负。

如果双方同时超过边界,那么积分高者胜。

如果双方积分相同,那么游戏平局。

游戏交互方式

Botzone上其他游戏一样,本游戏每步(每次程序运行)限时1秒。

提示

如果你不能理解以下交互方式,可以直接看#样例程序,按照说明填写代码,并修改其中

// 做出决策(你只需修改以下部分)

// 决策结束,输出结果(你只需修改以上部分)

之间的部分即可!

本游戏与Botzone上其他游戏一样,使用相同的交互方式:Bot#交互

简单交互

request
第一回合的request是一行两个数字t和c,空格分隔,t表示自己拿到的方块类型,n表示自己的颜色(0红1蓝)
以后的request是一行四个数字t、x、y、o,空格分隔,t表示自己拿到的方块类型,x和y表示对方方块落在的位置坐标,o表示对方方块的姿态
response
每个回合的response都是一行若干数字,用空格分隔。第一个数字是t,表示给对方下一回合的方块,第二个数字是n,表示方块落地序列的长度,此后有3n个数字表示序列中的元组,每相邻的三个分别是x、y、o。

定义了request和response后,玩家的输入总体格式可以参看Bot#简化交互

对于样例对局,第四回合蓝色玩家的样例输入和样例输出如下:

输入

4
1 1
4 1 2 1 2
6 2 1 2
4 1 4 1 3
4 5 1 0
0 1 7 1 0
4 8 1 0

输出

0 1 9 2 0

JSON交互

每回合Bot收到的request不是字符串,而是一个JSON对象,格式如下:

第一回合

{
	"block": Number, // 表示玩家本回合块类型
	"color": Number  // 表示玩家颜色
}

其他回合

{
	"x": Number,    // x和y表示对方方块落在的位置坐标,o表示对方方块的姿态
	"y": Number,
	"o": Number,
	"block": Number // 表示给对手的下回合块类型
}

Bot所需要输出的response也是JSON对象,格式如下:

{
	"seq": [ // 方块落地序列
		{
			"x": Number,
			"y": Number,
			"o": Number
		}
	],
	"block": Number // 表示给对手的下回合块类型
}

对于样例对局,第四回合蓝色玩家的样例输入和样例输出如下:(实际是单行紧缩的)

输入

{
	"requests": [
		{
			"block": 1,
			"color": 1
		},
		{
			"block": 6,
			"o": 2,
			"x": 2,
			"y": 1
		},
		{
			"block": 4,
			"o": 0,
			"x": 5,
			"y": 1
		},
		{
			"block": 4,
			"o": 0,
			"x": 8,
			"y": 1
		}
	],
	"responses": [
		{
			"seq": [
				{
					"x": 2,
					"y": 1,
					"o": 2
				}
			],
			"block": 4
		},{
			"seq": [
				{
					"x": 4,
					"y": 1,
					"o": 3
				}
			],
			"block": 4
		},{
			"seq": [
				{
					"x": 7,
					"y": 1,
					"o": 0
				}
			],
			"block": 0
		}
	]
}

输出

{
	"response": {
		"seq": [
			{
				"x": 9,
				"y": 2,
				"o": 0
			}
		],
		"block": 0
	}
}

样例程序

请注意展开按钮在右侧!-----=====≡≡≡≡≡>

简单交互样例程序

/**
 * Tetris 简单交互样例程序
 * https://wiki.botzone.org/index.php?title=Tetris
 */
// 注意:x的范围是1~MAPWIDTH,y的范围是1~MAPHEIGHT
// 数组是先行(y)后列(c)
// 坐标系:原点在左下角

#include <iostream>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;

#define MAPWIDTH 10
#define MAPHEIGHT 20

// 我所在队伍的颜色(0为红,1为蓝,仅表示队伍,不分先后)
int currBotColor;
int enemyColor;

// 先y后x,记录地图状态,0为空,1为以前放置,2为刚刚放置,负数为越界
// (2用于在清行后将最后一步撤销再送给对方)
int gridInfo[2][MAPHEIGHT + 2][MAPWIDTH + 2] = { 0 };

// 代表分别向对方转移的行
int trans[2][4][MAPWIDTH + 2] = { 0 };

// 转移行数
int transCount[2] = { 0 };

// 运行eliminate后的当前高度
int maxHeight[2] = { 0 };

// 总消去行数的分数之和
int elimTotal[2] = { 0 };

// 一次性消去行数对应分数
const int elimBonus[4] = { 1, 3, 5, 7 };

// 给对应玩家的各类块的数目总计
int typeCountForColor[2][7] = { 0 };

const int blockShape[7][4][8] = {
	{ { 0,0,1,0,-1,0,-1,-1 },{ 0,0,0,1,0,-1,1,-1 },{ 0,0,-1,0,1,0,1,1 },{ 0,0,0,-1,0,1,-1,1 } },
	{ { 0,0,-1,0,1,0,1,-1 },{ 0,0,0,-1,0,1,1,1 },{ 0,0,1,0,-1,0,-1,1 },{ 0,0,0,1,0,-1,-1,-1 } },
	{ { 0,0,1,0,0,-1,-1,-1 },{ 0,0,0,1,1,0,1,-1 },{ 0,0,-1,0,0,1,1,1 },{ 0,0,0,-1,-1,0,-1,1 } },
	{ { 0,0,-1,0,0,-1,1,-1 },{ 0,0,0,-1,1,0,1,1 },{ 0,0,1,0,0,1,-1,1 },{ 0,0,0,1,-1,0,-1,-1 } },
	{ { 0,0,-1,0,0,1,1,0 },{ 0,0,0,-1,-1,0,0,1 },{ 0,0,1,0,0,-1,-1,0 },{ 0,0,0,1,1,0,0,-1 } },
	{ { 0,0,0,-1,0,1,0,2 },{ 0,0,1,0,-1,0,-2,0 },{ 0,0,0,1,0,-1,0,-2 },{ 0,0,-1,0,1,0,2,0 } },
	{ { 0,0,0,1,-1,0,-1,1 },{ 0,0,-1,0,0,-1,-1,-1 },{ 0,0,0,-1,1,-0,1,-1 },{ 0,0,1,0,0,1,1,1 } }
};// 7种形状(长L| 短L| 反z| 正z| T| 直一| 田格),4种朝向(上左下右),8:每相邻的两个分别为x,y


class Tetris
{
public:
	const int blockType;   // 标记方块类型的序号 0~6
	int blockX;            // 旋转中心的x轴坐标
	int blockY;            // 旋转中心的y轴坐标
	int orientation;       // 标记方块的朝向 0~3
	const int(*shape)[8]; // 当前类型方块的形状定义

	int color;

	Tetris(int t, int color) : blockType(t), shape(blockShape[t]), color(color)
	{ }

	inline Tetris &set(int x = -1, int y = -1, int o = -1)
	{
		blockX = x == -1 ? blockX : x;
		blockY = y == -1 ? blockY : y;
		orientation = o == -1 ? orientation : o;
		return *this;
	}

	// 判断当前位置是否合法
	inline bool isValid(int x = -1, int y = -1, int o = -1)
	{
		x = x == -1 ? blockX : x;
		y = y == -1 ? blockY : y;
		o = o == -1 ? orientation : o;
		if (o < 0 || o > 3)
			return false;

		int i, tmpX, tmpY;
		for (i = 0; i < 4; i++)
		{
			tmpX = x + shape[o][2 * i];
			tmpY = y + shape[o][2 * i + 1];
			if (tmpX < 1 || tmpX > MAPWIDTH ||
				tmpY < 1 || tmpY > MAPHEIGHT ||
				gridInfo[color][tmpY][tmpX] != 0)
				return false;
		}
		return true;
	}

	// 判断是否落地
	inline bool onGround()
	{
		if (isValid() && !isValid(-1, blockY - 1))
			return true;
		return false;
	}

	// 将方块放置在场地上
	inline bool place()
	{
		if (!onGround())
			return false;

		int i, tmpX, tmpY;
		for (i = 0; i < 4; i++)
		{
			tmpX = blockX + shape[orientation][2 * i];
			tmpY = blockY + shape[orientation][2 * i + 1];
			gridInfo[color][tmpY][tmpX] = 2;
		}
		return true;
	}

	// 检查能否逆时针旋转自己到o
	inline bool rotation(int o)
	{
		if (o < 0 || o > 3)
			return false;

		if (orientation == o)
			return true;

		int fromO = orientation;
		while (true)
		{
			if (!isValid())
				return false;

			if (fromO == o)
				break;

			fromO = (fromO + 1) % 4;
		}
		return true;
	}
};

// 围一圈护城河
void init()
{
	int i;
	for (i = 0; i < MAPHEIGHT + 2; i++)
	{
		gridInfo[1][i][0] = gridInfo[1][i][MAPWIDTH + 1] = -2;
		gridInfo[0][i][0] = gridInfo[0][i][MAPWIDTH + 1] = -2;
	}
	for (i = 0; i < MAPWIDTH + 2; i++)
	{
		gridInfo[1][0][i] = gridInfo[1][MAPHEIGHT + 1][i] = -2;
		gridInfo[0][0][i] = gridInfo[0][MAPHEIGHT + 1][i] = -2;
	}
}

namespace Util
{

	// 检查能否从场地顶端直接落到当前位置
	inline bool checkDirectDropTo(int color, int blockType, int x, int y, int o)
	{
		auto &def = blockShape[blockType][o];
		for (; y <= MAPHEIGHT; y++)
			for (int i = 0; i < 4; i++)
			{
				int _x = def[i * 2] + x, _y = def[i * 2 + 1] + y;
				if (_y > MAPHEIGHT)
					continue;
				if (_y < 1 || _x < 1 || _x > MAPWIDTH || gridInfo[color][_y][_x])
					return false;
			}
		return true;
	}

	// 消去行
	void eliminate(int color)
	{
		int &count = transCount[color] = 0;
		int i, j, emptyFlag, fullFlag;
		maxHeight[color] = MAPHEIGHT;
		for (i = 1; i <= MAPHEIGHT; i++)
		{
			emptyFlag = 1;
			fullFlag = 1;
			for (j = 1; j <= MAPWIDTH; j++)
			{
				if (gridInfo[color][i][j] == 0)
					fullFlag = 0;
				else
					emptyFlag = 0;
			}
			if (fullFlag)
			{
				for (j = 1; j <= MAPWIDTH; j++)
				{
					// 注意这里只转移以前的块,不包括最后一次落下的块(“撤销最后一步”)
					trans[color][count][j] = gridInfo[color][i][j] == 1 ? 1 : 0;
					gridInfo[color][i][j] = 0;
				}
				count++;
			}
			else if (emptyFlag)
			{
				maxHeight[color] = i - 1;
				break;
			}
			else
				for (j = 1; j <= MAPWIDTH; j++)
				{
					gridInfo[color][i - count][j] =
						gridInfo[color][i][j] > 0 ? 1 : gridInfo[color][i][j];
					if (count)
						gridInfo[color][i][j] = 0;
				}
		}
		maxHeight[color] -= count;
		elimTotal[color] += elimBonus[count];
	}

	// 转移双方消去的行,返回-1表示继续,否则返回输者
	int transfer()
	{
		int color1 = 0, color2 = 1;
		if (transCount[color1] == 0 && transCount[color2] == 0)
			return -1;
		if (transCount[color1] == 0 || transCount[color2] == 0)
		{
			if (transCount[color1] == 0 && transCount[color2] > 0)
				swap(color1, color2);
			int h2;
			maxHeight[color2] = h2 = maxHeight[color2] + transCount[color1];
			if (h2 >= MAPHEIGHT)
				return color2;
			int i, j;

			for (i = h2; i > transCount[color1]; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = gridInfo[color2][i - transCount[color1]][j];

			for (i = transCount[color1]; i > 0; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = trans[color1][i - 1][j];
			return -1;
		}
		else
		{
			int h1, h2;
			maxHeight[color1] = h1 = maxHeight[color1] + transCount[color2];//从color1处移动count1去color2
			maxHeight[color2] = h2 = maxHeight[color2] + transCount[color1];

			if (h1 > MAPHEIGHT) return color1;
			if (h2 > MAPHEIGHT) return color2;

			int i, j;
			for (i = h2; i > transCount[color1]; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = gridInfo[color2][i - transCount[color1]][j];

			for (i = transCount[color1]; i > 0; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = trans[color1][i - 1][j];

			for (i = h1; i > transCount[color2]; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color1][i][j] = gridInfo[color1][i - transCount[color2]][j];

			for (i = transCount[color2]; i > 0; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color1][i][j] = trans[color2][i - 1][j];

			return -1;
		}
	}

	// 颜色方还能否继续游戏
	inline bool canPut(int color, int blockType)
	{
		Tetris t(blockType, color);
		for (int y = MAPHEIGHT; y >= 1; y--)
			for (int x = 1; x <= MAPWIDTH; x++)
				for (int o = 0; o < 4; o++)
				{
					t.set(x, y, o);
					if (t.isValid() && checkDirectDropTo(color, blockType, x, y, o))
						return true;
				}
		return false;
	}

	// 打印场地用于调试
	inline void printField()
	{
#ifndef _BOTZONE_ONLINE
		static const char *i2s[] = {
			"~~",
			"~~",
			"  ",
			"[]",
			"##"
		};
		cout << "~~:墙,[]:块,##:新块" << endl;
		for (int y = MAPHEIGHT + 1; y >= 0; y--)
		{
			for (int x = 0; x <= MAPWIDTH + 1; x++)
				cout << i2s[gridInfo[0][y][x] + 2];
			for (int x = 0; x <= MAPWIDTH + 1; x++)
				cout << i2s[gridInfo[1][y][x] + 2];
			cout << endl;
		}
#endif
	}
}


int main()
{
	// 加速输入
	istream::sync_with_stdio(false);
	srand(time(NULL));
	init();

	int turnID, blockType;
	int nextTypeForColor[2];
	cin >> turnID;

	// 先读入第一回合,得到自己的颜色
	// 双方的第一块肯定是一样的
	cin >> blockType >> currBotColor;
	enemyColor = 1 - currBotColor;
	nextTypeForColor[0] = blockType;
	nextTypeForColor[1] = blockType;
	typeCountForColor[0][blockType]++;
	typeCountForColor[1][blockType]++;

	// 然后分析以前每回合的输入输出,并恢复状态
	// 循环中,color 表示当前这一行是 color 的行为
	// 平台保证所有输入都是合法输入
	for (int i = 1; i < turnID; i++)
	{
		int currTypeForColor[2] = { nextTypeForColor[0], nextTypeForColor[1] };
		int n, x, y, o;
		// 根据这些输入输出逐渐恢复状态到当前回合

		// 先读自己的输出,也就是自己的行为
		// 自己的输出是一个序列,但是只有最后一步有用
		// 所以只保留最后一步
		// 然后模拟最后一步放置块
		cin >> blockType >> n;
		for (int j = 0; j < n; j++)
			cin >> x >> y >> o;

		// 我当时把上一块落到了 x y o!
		Tetris myBlock(currTypeForColor[currBotColor], currBotColor);
		myBlock.set(x, y, o).place();

		// 我给对方什么块来着?
		typeCountForColor[enemyColor][blockType]++;
		nextTypeForColor[enemyColor] = blockType;

		// 然后读自己的输入,也就是对方的行为
		// 裁判给自己的输入只有对方的最后一步
		cin >> blockType >> x >> y >> o;

		// 对方当时把上一块落到了 x y o!
		Tetris enemyBlock(currTypeForColor[enemyColor], enemyColor);
		enemyBlock.set(x, y, o).place();

		// 对方给我什么块来着?
		typeCountForColor[currBotColor][blockType]++;
		nextTypeForColor[currBotColor] = blockType;

		// 检查消去
		Util::eliminate(0);
		Util::eliminate(1);

		// 进行转移
		Util::transfer();
	}


	int blockForEnemy, seqX[MAPHEIGHT + 1], seqY[MAPHEIGHT + 1], seqO[MAPHEIGHT + 1], seqLength = 0;

	// 做出决策(你只需修改以下部分)

	// 遇事不决先输出(平台上编译不会输出)
	Util::printField();

	// 贪心决策
	// 从下往上以各种姿态找到第一个位置,要求能够直着落下
	Tetris block(nextTypeForColor[currBotColor], currBotColor);
	for (int y = 1; y <= MAPHEIGHT; y++)
		for (int x = 1; x <= MAPWIDTH; x++)
			for (int o = 0; o < 4; o++)
			{
				if (block.set(x, y, o).isValid() &&
					Util::checkDirectDropTo(currBotColor, block.blockType, x, y, o))
				{
					seqX[0] = x;
					seqY[0] = y;
					seqO[0] = o;
					seqLength = 1;
					goto determined;
				}
			}

determined:
	// 再看看给对方什么好

	int maxCount = 0, minCount = 99;
	for (int i = 0; i < 7; i++)
	{
		if (typeCountForColor[enemyColor][i] > maxCount)
			maxCount = typeCountForColor[enemyColor][i];
		if (typeCountForColor[enemyColor][i] < minCount)
			minCount = typeCountForColor[enemyColor][i];
	}
	if (maxCount - minCount == 2)
	{
		// 危险,找一个不是最大的块给对方吧
		for (blockForEnemy = 0; blockForEnemy < 7; blockForEnemy++)
			if (typeCountForColor[enemyColor][blockForEnemy] != maxCount)
				break;
	}
	else
	{
		blockForEnemy = rand() % 7;
	}

	// 决策结束,输出结果(你只需修改以上部分)

	cout << blockForEnemy << " " << seqLength;
	for (int i = 0; i < seqLength; i++)
		cout << " " << seqX[i] << " " << seqY[i] << " " << seqO[i];

	return 0;
}

JSON交互样例程序

本地编译和调试的方式请查看 JSONCPP

/**
 * Tetris JSON交互样例程序
 * https://wiki.botzone.org/index.php?title=Tetris
 */
// 注意:x的范围是1~MAPWIDTH,y的范围是1~MAPHEIGHT
// 数组是先行(y)后列(c)
// 坐标系:原点在左下角

#include <iostream>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include "jsoncpp/json.h"
using namespace std;

#define MAPWIDTH 10
#define MAPHEIGHT 20

// 我所在队伍的颜色(0为红,1为蓝,仅表示队伍,不分先后)
int currBotColor;
int enemyColor;

// 先y后x,记录地图状态,0为空,1为以前放置,2为刚刚放置,负数为越界
// (2用于在清行后将最后一步撤销再送给对方)
int gridInfo[2][MAPHEIGHT + 2][MAPWIDTH + 2] = { 0 };

// 代表分别向对方转移的行
int trans[2][4][MAPWIDTH + 2] = { 0 };

// 转移行数
int transCount[2] = { 0 };

// 运行eliminate后的当前高度
int maxHeight[2] = { 0 };

// 总消去行数的分数之和
int elimTotal[2] = { 0 };

// 一次性消去行数对应分数
const int elimBonus[4] = { 1, 3, 5, 7 };

// 给对应玩家的各类块的数目总计
int typeCountForColor[2][7] = { 0 };

const int blockShape[7][4][8] = {
	{ { 0,0,1,0,-1,0,-1,-1 },{ 0,0,0,1,0,-1,1,-1 },{ 0,0,-1,0,1,0,1,1 },{ 0,0,0,-1,0,1,-1,1 } },
	{ { 0,0,-1,0,1,0,1,-1 },{ 0,0,0,-1,0,1,1,1 },{ 0,0,1,0,-1,0,-1,1 },{ 0,0,0,1,0,-1,-1,-1 } },
	{ { 0,0,1,0,0,-1,-1,-1 },{ 0,0,0,1,1,0,1,-1 },{ 0,0,-1,0,0,1,1,1 },{ 0,0,0,-1,-1,0,-1,1 } },
	{ { 0,0,-1,0,0,-1,1,-1 },{ 0,0,0,-1,1,0,1,1 },{ 0,0,1,0,0,1,-1,1 },{ 0,0,0,1,-1,0,-1,-1 } },
	{ { 0,0,-1,0,0,1,1,0 },{ 0,0,0,-1,-1,0,0,1 },{ 0,0,1,0,0,-1,-1,0 },{ 0,0,0,1,1,0,0,-1 } },
	{ { 0,0,0,-1,0,1,0,2 },{ 0,0,1,0,-1,0,-2,0 },{ 0,0,0,1,0,-1,0,-2 },{ 0,0,-1,0,1,0,2,0 } },
	{ { 0,0,0,1,-1,0,-1,1 },{ 0,0,-1,0,0,-1,-1,-1 },{ 0,0,0,-1,1,-0,1,-1 },{ 0,0,1,0,0,1,1,1 } }
};// 7种形状(长L| 短L| 反z| 正z| T| 直一| 田格),4种朝向(上左下右),8:每相邻的两个分别为x,y


class Tetris
{
public:
	const int blockType;   // 标记方块类型的序号 0~6
	int blockX;            // 旋转中心的x轴坐标
	int blockY;            // 旋转中心的y轴坐标
	int orientation;       // 标记方块的朝向 0~3
	const int(*shape)[8]; // 当前类型方块的形状定义

	int color;

	Tetris(int t, int color) : blockType(t), shape(blockShape[t]), color(color)
	{ }

	inline Tetris &set(int x = -1, int y = -1, int o = -1)
	{
		blockX = x == -1 ? blockX : x;
		blockY = y == -1 ? blockY : y;
		orientation = o == -1 ? orientation : o;
		return *this;
	}

	// 判断当前位置是否合法
	inline bool isValid(int x = -1, int y = -1, int o = -1)
	{
		x = x == -1 ? blockX : x;
		y = y == -1 ? blockY : y;
		o = o == -1 ? orientation : o;
		if (o < 0 || o > 3)
			return false;

		int i, tmpX, tmpY;
		for (i = 0; i < 4; i++)
		{
			tmpX = x + shape[o][2 * i];
			tmpY = y + shape[o][2 * i + 1];
			if (tmpX < 1 || tmpX > MAPWIDTH ||
				tmpY < 1 || tmpY > MAPHEIGHT ||
				gridInfo[color][tmpY][tmpX] != 0)
				return false;
		}
		return true;
	}

	// 判断是否落地
	inline bool onGround()
	{
		if (isValid() && !isValid(-1, blockY - 1))
			return true;
		return false;
	}

	// 将方块放置在场地上
	inline bool place()
	{
		if (!onGround())
			return false;

		int i, tmpX, tmpY;
		for (i = 0; i < 4; i++)
		{
			tmpX = blockX + shape[orientation][2 * i];
			tmpY = blockY + shape[orientation][2 * i + 1];
			gridInfo[color][tmpY][tmpX] = 2;
		}
		return true;
	}

	// 检查能否逆时针旋转自己到o
	inline bool rotation(int o)
	{
		if (o < 0 || o > 3)
			return false;

		if (orientation == o)
			return true;

		int fromO = orientation;
		while (true)
		{
			if (!isValid())
				return false;

			if (fromO == o)
				break;

			fromO = (fromO + 1) % 4;
		}
		return true;
	}
};

// 围一圈护城河
void init()
{
	int i;
	for (i = 0; i < MAPHEIGHT + 2; i++)
	{
		gridInfo[1][i][0] = gridInfo[1][i][MAPWIDTH + 1] = -2;
		gridInfo[0][i][0] = gridInfo[0][i][MAPWIDTH + 1] = -2;
	}
	for (i = 0; i < MAPWIDTH + 2; i++)
	{
		gridInfo[1][0][i] = gridInfo[1][MAPHEIGHT + 1][i] = -2;
		gridInfo[0][0][i] = gridInfo[0][MAPHEIGHT + 1][i] = -2;
	}
}

namespace Util
{

	// 检查能否从场地顶端直接落到当前位置
	inline bool checkDirectDropTo(int color, int blockType, int x, int y, int o)
	{
		auto &def = blockShape[blockType][o];
		for (; y <= MAPHEIGHT; y++)
			for (int i = 0; i < 4; i++)
			{
				int _x = def[i * 2] + x, _y = def[i * 2 + 1] + y;
				if (_y > MAPHEIGHT)
					continue;
				if (_y < 1 || _x < 1 || _x > MAPWIDTH || gridInfo[color][_y][_x])
					return false;
			}
		return true;
	}

	// 消去行
	void eliminate(int color)
	{
		int &count = transCount[color] = 0;
		int i, j, emptyFlag, fullFlag;
		maxHeight[color] = MAPHEIGHT;
		for (i = 1; i <= MAPHEIGHT; i++)
		{
			emptyFlag = 1;
			fullFlag = 1;
			for (j = 1; j <= MAPWIDTH; j++)
			{
				if (gridInfo[color][i][j] == 0)
					fullFlag = 0;
				else
					emptyFlag = 0;
			}
			if (fullFlag)
			{
				for (j = 1; j <= MAPWIDTH; j++)
				{
					// 注意这里只转移以前的块,不包括最后一次落下的块(“撤销最后一步”)
					trans[color][count][j] = gridInfo[color][i][j] == 1 ? 1 : 0;
					gridInfo[color][i][j] = 0;
				}
				count++;
			}
			else if (emptyFlag)
			{
				maxHeight[color] = i - 1;
				break;
			}
			else
				for (j = 1; j <= MAPWIDTH; j++)
				{
					gridInfo[color][i - count][j] =
						gridInfo[color][i][j] > 0 ? 1 : gridInfo[color][i][j];
					if (count)
						gridInfo[color][i][j] = 0;
				}
		}
		maxHeight[color] -= count;
		elimTotal[color] += elimBonus[count];
	}

	// 转移双方消去的行,返回-1表示继续,否则返回输者
	int transfer()
	{
		int color1 = 0, color2 = 1;
		if (transCount[color1] == 0 && transCount[color2] == 0)
			return -1;
		if (transCount[color1] == 0 || transCount[color2] == 0)
		{
			if (transCount[color1] == 0 && transCount[color2] > 0)
				swap(color1, color2);
			int h2;
			maxHeight[color2] = h2 = maxHeight[color2] + transCount[color1];
			if (h2 >= MAPHEIGHT)
				return color2;
			int i, j;

			for (i = h2; i > transCount[color1]; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = gridInfo[color2][i - transCount[color1]][j];

			for (i = transCount[color1]; i > 0; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = trans[color1][i - 1][j];
			return -1;
		}
		else
		{
			int h1, h2;
			maxHeight[color1] = h1 = maxHeight[color1] + transCount[color2];//从color1处移动count1去color2
			maxHeight[color2] = h2 = maxHeight[color2] + transCount[color1];

			if (h1 > MAPHEIGHT) return color1;
			if (h2 > MAPHEIGHT) return color2;

			int i, j;
			for (i = h2; i > transCount[color1]; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = gridInfo[color2][i - transCount[color1]][j];

			for (i = transCount[color1]; i > 0; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color2][i][j] = trans[color1][i - 1][j];

			for (i = h1; i > transCount[color2]; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color1][i][j] = gridInfo[color1][i - transCount[color2]][j];

			for (i = transCount[color2]; i > 0; i--)
				for (j = 1; j <= MAPWIDTH; j++)
					gridInfo[color1][i][j] = trans[color2][i - 1][j];

			return -1;
		}
	}

	// 颜色方还能否继续游戏
	inline bool canPut(int color, int blockType)
	{
		Tetris t(blockType, color);
		for (int y = MAPHEIGHT; y >= 1; y--)
			for (int x = 1; x <= MAPWIDTH; x++)
				for (int o = 0; o < 4; o++)
				{
					t.set(x, y, o);
					if (t.isValid() && checkDirectDropTo(color, blockType, x, y, o))
						return true;
				}
		return false;
	}

	// 打印场地用于调试
	inline void printField()
	{
#ifndef _BOTZONE_ONLINE
		static const char *i2s[] = {
			"~~",
			"~~",
			"  ",
			"[]",
			"##"
		};
		cout << "~~:墙,[]:块,##:新块" << endl;
		for (int y = MAPHEIGHT + 1; y >= 0; y--)
		{
			for (int x = 0; x <= MAPWIDTH + 1; x++)
				cout << i2s[gridInfo[0][y][x] + 2];
			for (int x = 0; x <= MAPWIDTH + 1; x++)
				cout << i2s[gridInfo[1][y][x] + 2];
			cout << endl;
		}
#endif
	}
}


int main()
{
	// 加速输入
	istream::sync_with_stdio(false);
	srand(time(NULL));
	init();

	int turnID, blockType;
	int nextTypeForColor[2];

	// 读入JSON
	string str;
	getline(cin, str);
	Json::Reader reader;
	Json::Value input;
	reader.parse(str, input);

	// 先读入第一回合,得到自己的颜色
	// 双方的第一块肯定是一样的
	turnID = input["responses"].size() + 1;
	auto &first = input["requests"][(Json::UInt) 0];

	blockType = first["block"].asInt();
	currBotColor = first["color"].asInt();

	enemyColor = 1 - currBotColor;
	nextTypeForColor[0] = blockType;
	nextTypeForColor[1] = blockType;
	typeCountForColor[0][blockType]++;
	typeCountForColor[1][blockType]++;


	// 然后分析以前每回合的输入输出,并恢复状态
	// 循环中,color 表示当前这一行是 color 的行为
	// 平台保证所有输入都是合法输入
	for (int i = 1; i < turnID; i++)
	{
		int currTypeForColor[2] = { nextTypeForColor[0], nextTypeForColor[1] };
		int x, y, o;
		// 根据这些输入输出逐渐恢复状态到当前回合

		// 先读自己的输出,也就是自己的行为
		// 自己的输出是一个序列,但是只有最后一步有用
		// 所以只保留最后一步
		// 然后模拟最后一步放置块
		auto &myOutput = input["responses"][i - 1];
		blockType = myOutput["block"].asInt();
		auto &seq = myOutput["seq"];
		auto &lastPos = seq[seq.size() - 1];
		x = lastPos["x"].asInt();
		y = lastPos["y"].asInt();
		o = lastPos["o"].asInt();

		// 我当时把上一块落到了 x y o!
		Tetris myBlock(currTypeForColor[currBotColor], currBotColor);
		myBlock.set(x, y, o).place();

		// 我给对方什么块来着?
		typeCountForColor[enemyColor][blockType]++;
		nextTypeForColor[enemyColor] = blockType;

		// 然后读自己的输入,也就是对方的行为
		// 裁判给自己的输入只有对方的最后一步
		auto &myInput = input["requests"][i];
		blockType = myInput["block"].asInt();
		x = myInput["x"].asInt();
		y = myInput["y"].asInt();
		o = myInput["o"].asInt();

		// 对方当时把上一块落到了 x y o!
		Tetris enemyBlock(currTypeForColor[enemyColor], enemyColor);
		enemyBlock.set(x, y, o).place();

		// 对方给我什么块来着?
		typeCountForColor[currBotColor][blockType]++;
		nextTypeForColor[currBotColor] = blockType;

		// 检查消去
		Util::eliminate(0);
		Util::eliminate(1);

		// 进行转移
		Util::transfer();
	}


	int blockForEnemy, seqX[MAPHEIGHT + 1], seqY[MAPHEIGHT + 1], seqO[MAPHEIGHT + 1], seqLength = 0;

	// 做出决策(你只需修改以下部分)

	// 遇事不决先输出(平台上运行不会输出)
	Util::printField();

	// 贪心决策
	// 从下往上以各种姿态找到第一个位置,要求能够直着落下
	Tetris block(nextTypeForColor[currBotColor], currBotColor);
	for (int y = 1; y <= MAPHEIGHT; y++)
		for (int x = 1; x <= MAPWIDTH; x++)
			for (int o = 0; o < 4; o++)
			{
				if (block.set(x, y, o).isValid() &&
					Util::checkDirectDropTo(currBotColor, block.blockType, x, y, o))
				{
					seqX[0] = x;
					seqY[0] = y;
					seqO[0] = o;
					seqLength = 1;
					goto determined;
				}
			}

determined:
	// 再看看给对方什么好

	int maxCount = 0, minCount = 99;
	for (int i = 0; i < 7; i++)
	{
		if (typeCountForColor[enemyColor][i] > maxCount)
			maxCount = typeCountForColor[enemyColor][i];
		if (typeCountForColor[enemyColor][i] < minCount)
			minCount = typeCountForColor[enemyColor][i];
	}
	if (maxCount - minCount == 2)
	{
		// 危险,找一个不是最大的块给对方吧
		for (blockForEnemy = 0; blockForEnemy < 7; blockForEnemy++)
			if (typeCountForColor[enemyColor][blockForEnemy] != maxCount)
				break;
	}
	else
	{
		blockForEnemy = rand() % 7;
	}

	// 决策结束,输出结果(你只需修改以上部分)

	Json::Value output;
	Json::FastWriter writer;

	output["response"]["block"] = blockForEnemy;

	auto &seq = output["response"]["seq"];
	for (int i = 0; i < seqLength; i++)
	{
		seq[i]["x"] = seqX[i];
		seq[i]["y"] = seqY[i];
		seq[i]["o"] = seqO[i];
	}

	cout << writer.write(output);

	return 0;
}