Tetris2Judge

来自Botzone Wiki
跳转至: 导航搜索
#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include "jsoncpp/json.h"
using namespace std;

#define MAPWIDTH 10
#define MAPHEIGHT 20

int currBotColor; // 我所在队伍的颜色(0为黑,1为白,仅表示队伍,不分先后)
int gridInfo[2][MAPHEIGHT + 2][MAPWIDTH + 2] = { 0 }; // 先y后x,记录地图状态
int trans[2][6][MAPWIDTH + 2] = { 0 }; // 代表分别向对方传输的行
int maxHeight[2] = { 0 };
int transCount[2] = { 0 };
int elimTotal[2] = { 0 };
int elimCombo[2] = { 0 };
int typeCountByColor[2][7] = { 0 };

const int elimBonus[] = { 0, 1, 3, 5, 7 };
const int deltaBlock[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

  // 旋转的时候需要为空的块相对于旋转中心的坐标
const int rotateBlank[7][4][10] = {
	{ { 1,1,0,0 },{ -1,1,0,0 },{ -1,-1,0,0 },{ 1,-1,0,0 } },
	{ { -1,-1,0,0 },{ 1,-1,0,0 },{ 1,1,0,0 },{ -1,1,0,0 } },
	{ { 1,1,0,0 },{ -1,1,0,0 },{ -1,-1,0,0 },{ 1,-1,0,0 } },
	{ { -1,-1,0,0 },{ 1,-1,0,0 },{ 1,1,0,0 },{ -1,1,0,0 } },
	{ { -1,-1,-1,1,1,1,0,0 },{ -1,-1,-1,1,1,-1,0,0 },{ -1,-1,1,1,1,-1,0,0 },{ -1,1,1,1,1,-1,0,0 } },
	{ { 1,-1,-1,1,-2,1,-1,2,-2,2 } ,{ 1,1,-1,-1,-2,-1,-1,-2,-2,-2 } ,{ -1,1,1,-1,2,-1,1,-2,2,-2 } ,{ -1,-1,1,1,2,1,1,2,2,2 } },
	{ { 0,0 },{ 0,0 } ,{ 0,0 } ,{ 0,0 } }
};


class Tetris
{
public:
	int blockType;//标记方块类型的序号 0~6
	int blockX;     //旋转中心的x轴坐标
	int blockY;     //旋转中心的y轴坐标
	int orientation; //标记方块的朝向 0~3

	Tetris(int t, int x, int y, int o)
	{
		blockType = t;
		blockX = x;
		blockY = y;
		orientation = o;
	}
	void set(int t = -1, int x = -1, int y = -1, int o = -1)
	{
		blockType = t == -1 ? blockType : t;
		blockX = x == -1 ? blockX : x;
		blockY = y == -1 ? blockY : y;
		orientation = o == -1 ? orientation : o;
	}
	bool isValid(int color, int x = -1, int y = -1, int o = -1) const
	{
		x = x == -1 ? blockX : x;
		y = y == -1 ? blockY : y;
		o = o == -1 ? orientation : o;
		if (o < 0 || o > 3 || blockType < 0 || blockType > 6)
			return false;

		int i, tmpX, tmpY;
		for (i = 0; i < 4; i++)
		{
			tmpX = x + deltaBlock[blockType][o][2 * i];
			tmpY = y + deltaBlock[blockType][o][2 * i + 1];
			if (tmpX < 1 || tmpX > MAPWIDTH || tmpY < 1 || tmpY > MAPHEIGHT)
				return false;
			if (gridInfo[color][tmpY][tmpX] != 0)
				return false;
		}
		return true;
	}
	bool checkRotationBlank(int color) const
	{
		for (int i = 0; i < 5; i++)
		{
			int blankX = blockX + rotateBlank[blockType][orientation][2 * i];
			int blankY = blockY + rotateBlank[blockType][orientation][2 * i + 1];
			if (blankX == blockX && blankY == blockY)
				break;
			if (gridInfo[color][blankY][blankX] != 0)
				return false;
		}
		return true;
	}

	// 已改 相邻的y值移动
	bool movable(int color, int curX, int curY, int curO)
	{
		if (!isValid(color) || !isValid(color, curX, curY, curO) || (blockY - curY) != 1)
			return false;

		int befX = blockX, befY = blockY, befO = orientation;

		if (blockX == curX)
		{
			if (befO == curO)
				return true;
			else
			{
				// 先下落后旋转
				while (befO != curO)
				{
					if (!isValid(color, curX, curY, befO))
						break;
					befO = (befO + 1) % 4;
				}
				if (befO == curO)
					return true;

				// 先旋转后下落
				befO = orientation;
				while (befO != curO)
				{
					if (!isValid(color, befX, befY, befO))
						break;
					befO = (befO + 1) % 4;
				}
				if (befO == curO && isValid(color, befX, befY, befO))
					return true;

				// 先旋转后下落再旋转
				befO = orientation;
				for (int tmpO = 0; tmpO < 4; tmpO++)
				{
					if (tmpO == curO || tmpO == befO)
						continue;
					while (befO != tmpO)
					{
						if (!isValid(color, befX, befY, befO))
							break;
						befO = (befO + 1) % 4;
					}
					if (befO != tmpO)
						continue;
					while (befO != curO)
					{
						if (!isValid(color, curX, curY, befO))
							break;
						befO = (befO + 1) % 4;
					}
					if (befO == curO && isValid(color, curX, curY, befO))
						return true;
				}
				return false;
			}
		}
		befX = blockX, befY = blockY, befO = orientation;

		int di = 1, ni, i, j, flag, possibleO[MAPWIDTH + 2] = { 0 }, tmpO, knee; // di: delta i, ni: next i

		if (befX > curX) di = -1;
		for (knee = befX; knee != (curX + di); knee += di)
		{ // 遍历下落点的位置

		  // i为本次测试的横坐标,ni为紧邻的横坐标
		  // 每次都是先旋转再平移,possibleO数组里存的是平移之后的状态,尚未旋转移动

		  // 先判断从knee处可以有什么状态下落
			for (i = 0; i < 4; i++)
			{
				for (j = 0; j < 4; j++)
				{
					if (!isValid(color, knee, curY, j)) continue;
					if (!rotation(color, knee, befY, i, j)) continue;
					if (sway(color, befX, knee, befY, befO, i)) break;
				}
				if (j == 4)
					continue;
				if (sway(color, knee, curX, curY, i, j))
					return true;
			}

		}
		return false;
	}

	// 已改
	bool isGround(int color) const
	{
		if (isValid(color) && !isValid(color, -1, blockY - 1))
			return true;
		return false;
	}

	// 已改
	bool paste(int color) const
	{
		if (!isGround(color))
			return false;

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

	// 已改 rotation会测量方块是否可以从befO逆时针旋转到o
	bool rotation(int color, int x = -1, int y = -1, int befO = -1, int o = -1)
	{
		x = x == -1 ? blockX : x;
		y = y == -1 ? blockY : y;
		befO = befO == -1 ? orientation : befO;
		o = o == -1 ? orientation : o;
		if (o < 0 || o > 3 || blockType < 0 || blockType > 6)
			return false;

		if (befO == o)
			return true;

		int i, blankX, blankY;
		while (true)
		{
			if (!isValid(color, x, y, befO))
				return false;

			if (befO == o)
				break;

			// 检查旋转碰撞
			for (i = 0; i < 5; i++) {
				blankX = blockX + rotateBlank[blockType][befO][2 * i];
				blankY = blockY + rotateBlank[blockType][befO][2 * i + 1];
				if (blankX == blockX && blankY == blockY)
					break;
				if (gridInfo[color][blankY][blankX] != 0)
					return false;
			}

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

	// 已改 水平平移
	bool sway(int color, int befX, int curX, int befY, int befO, int curO)
	{
		if (befX == curX)
			return rotation(color, befX, befY, befO, curO);

		int di = 1, ni, i, j, possibleO[MAPWIDTH + 2] = { 0 }; // di: delta i, ni: next i

		if (befX > curX) di = -1;

		for (i = befX; i != curX; i += di)
		{
			ni = i + di;
			for (possibleO[ni] = 0; possibleO[ni] < 4; possibleO[ni]++)
			{ // possibleO[ni]是第i个位置需要转到的位置
				if (!isValid(color, ni, befY, possibleO[ni]))
					continue;
				if (rotation(color, i, befY, (possibleO[i] + befO) % 4), (possibleO[ni] + befO) % 4)
					break;
			}
			while (possibleO[ni] == 4)
			{
				if (ni <= befX)
					return false;
				possibleO[ni] = 0;
				possibleO[i]++;
				i -= di;
				ni -= di;
			}
		}
		return true;
	}
} tmpBlock(0, 0, 0, 0);

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;
	}
}


bool checkDirectDropTo(int color, int blockType, int x, int y, int o)
{
	if (o < 0 || o > 3 || blockType < 0 || blockType > 6)
		return false;

	auto &def = deltaBlock[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;
}

int moveSeq(int color, int blockType, int n, int x[MAPHEIGHT], int y[MAPHEIGHT], int o[MAPHEIGHT])
{
	if (n == 0)
		return false;
	int flag = 0, i, j;
	if (!checkDirectDropTo(color, blockType, x[0], y[0], o[0]))
		return false;

	for (i = 0; i < n - 1; i++)
		if (y[i] <= y[i + 1])
			return false;

	for (i = 0; i < n - 1; i++)
		if ((y[i] - y[i + 1]) != 1)
		{
			if (o[i] != o[i + 1] || x[i] != x[i + 1])
				return false;
			for (j = y[i]; j >= y[i + 1]; j--)
			{
				tmpBlock.set(blockType, x[i], j, o[i]);
				if (!tmpBlock.isValid(color))
					return false;
			}
		}

	tmpBlock.set(blockType, x[0], y[0], o[0]);
	for (i = 1; i < n; i++)
	{
		if ((y[i - 1] - y[i]) == 1 && !tmpBlock.movable(color, x[i], y[i], o[i]))
			return false;
		tmpBlock.set(blockType, x[i], y[i], o[i]);
		if (!tmpBlock.isValid(color))
			return false;
	}

	if (!tmpBlock.isGround(color))
		return false;
	return tmpBlock.paste(color);
}

// 存储搜索过程中上一个点(即按方块运动时间排序的下一个点)
const Tetris *fromWhere[MAPHEIGHT + 2][MAPWIDTH + 2][4];
const Tetris *searchBegin = (Tetris *)0xffffffff;
vector<Tetris *> newlyAllocated;
int currTetrisIndex = 0;

inline void deAllocTetris()
{
	currTetrisIndex--;
}

inline Tetris *allocTetris()
{
	if (newlyAllocated.size() > currTetrisIndex)
		return newlyAllocated[currTetrisIndex++];

	Tetris *ret = new Tetris(0, 0, 0, 0);
	currTetrisIndex++;
	newlyAllocated.push_back(ret);
	return ret;
}

inline Tetris *addNode(queue<Tetris *> &q, const Tetris *parent, int color, int type, int x, int y, int o)
{
	if (fromWhere[y][x][o])
		return NULL;
	Tetris *checker = allocTetris();
	checker->set(type, x, y, o);
	if (!checker->isValid(color) ||
		(parent && parent != searchBegin && parent->orientation != o && !checker->checkRotationBlank(color)))
	{
		deAllocTetris();
		return NULL;
	}
	fromWhere[y][x][o] = parent;
	if (checkDirectDropTo(color, type, x, y, o))
		return checker;
	q.push(checker);
	return NULL;
}

// 倒着搜索能够直接掉落到的起点
Tetris *checkIfReachable(int color, int type, int x, int y, int o)
{
	currTetrisIndex = 0;
	memset(fromWhere, NULL, sizeof(fromWhere));

	queue<Tetris *> q;

	auto ret = addNode(q, searchBegin, color, type, x, y, o);
	if (ret)
		return ret;
	while (!q.empty())
	{
		auto node = q.front();
		q.pop();
		// 写到一半才发现不是js不能写得特别爽……
		ret = addNode(q, node, color, type, node->blockX, node->blockY, (node->orientation + 3) % 4);
		if (ret)
			return ret;
		ret = addNode(q, node, color, type, node->blockX, node->blockY + 1, node->orientation);
		if (ret)
			return ret;
		ret = addNode(q, node, color, type, node->blockX - 1, node->blockY, node->orientation);
		if (ret)
			return ret;
		ret = addNode(q, node, color, type, node->blockX + 1, node->blockY, node->orientation);
		if (ret)
			return ret;
	}
	return NULL;
}

int eliminate(int color)
{
	int &count = transCount[color] = 0;
	int i, j, emptyFlag, fullFlag, firstFull = 1, hasBonus = 0;
	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)
		{
			if (firstFull && ++elimCombo[color] >= 3)
			{
				for (j = 1; j <= MAPWIDTH; j++)
					trans[color][count][j] = gridInfo[color][i][j] == 1 ? 1 : 0;
				count++;
				hasBonus = 1;
			}
			firstFull = 0;
			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 + hasBonus][j] = gridInfo[color][i][j] == 0 ? 0 : 1;
				if (count)
					gridInfo[color][i][j] = 0;
			}
	}
	if (count == 0)
		elimCombo[color] = 0;
	maxHeight[color] -= count - hasBonus;
	elimTotal[color] += elimBonus[count - hasBonus];
	return true;
}

int transfer() // 返回-1表示继续,否则返回输者
{
	int color1 = 0, color2 = 1;
	if (transCount[color1] == 0 || transCount[color2] == 0)
	{
		if (transCount[color1] == 0 && transCount[color2] > 0)
			swap(color1, color2);
		int 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;
		h1 = maxHeight[color1] + transCount[color2];//从color1处移动count1去color2
		h2 = maxHeight[color2] + transCount[color1];

		if (h1 > MAPHEIGHT && h2 > MAPHEIGHT) return -2;
		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;
	}
}

bool canPut(int color, int blockType)
{
	Tetris t(blockType, 0, 0, 0);
	for (int y = MAPHEIGHT; y > 1; y--)
		for (int x = 1; x <= MAPWIDTH; x++)
			for (int o = 0; o < 4; o++)
			{
				t.set(blockType, x, y, o);
				if (t.isValid(color) && checkDirectDropTo(color, blockType, x, y, o))
					return true;
			}
	return false;
}

void printField()
{
	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;
	}
}

int main()
{
	string str;
	getline(cin, str);
	Json::Reader reader;
	Json::Value raw, input, output;
	reader.parse(str, raw);
	input = raw["log"];

	init();
	currBotColor = 0;
	if (input.size() == 0)
	{
		Json::Value initdata = raw["initdata"], temp;
		int block;

		if (initdata.isString())
			reader.parse(initdata.asString(), initdata);
		if (initdata.isString())
			reader.parse("{}", initdata);

		temp = initdata["firstBlock"];
		if (temp.isInt())
			block = temp.asUInt();
		else
		{
			srand(time(0));
			block = rand() % 7;
		}
		output["display"]["0"]["block"] = block;
		output["display"]["1"]["block"] = block;
		output["command"] = "request";

		output["content"]["0"]["color"] = 0;
		output["content"]["1"]["color"] = 1;
		output["content"]["0"]["block"] = output["content"]["1"]["block"] = block;
		//output["content"]["0"]["blockType"] = -1;

		//auto array = output["content"]["0"]["actions"];
		//array[0]["blockX"] = -1;
		//array[0]["blockY"] = -1;
		//array[0]["orientation"] = -1;
	}
	else
	{
		int lastBlocks[2];
		int lastMove[2][3];
		lastBlocks[0] = input[(Json::UInt) 0]["output"]["content"]["0"]["block"].asInt();
		lastBlocks[1] = input[(Json::UInt) 0]["output"]["content"]["1"]["block"].asInt();
		typeCountByColor[1][lastBlocks[0]]++;
		typeCountByColor[0][lastBlocks[1]]++;
		for (int i = 1; i < input.size(); i += 2)
		{
			bool isLast = i == input.size() - 1;
			//bool isLast = true;
			int currBlocks[2];

			auto raw = input[i];
			Json::Value content[2];
			Json::Value calculatedRoute[2];
			content[0] = raw["0"]["response"].isNull() ? raw["0"]["content"] : raw["0"]["response"];
			content[1] = raw["1"]["response"].isNull() ? raw["1"]["content"] : raw["1"]["response"];
			string verdict[2] = {
				raw["0"]["verdict"].asString(),
				raw["1"]["verdict"].asString()
			};
			int maxH[2];
			bool bad[2] = { false, false };
			string error[2] = { "OVERFLOW", "OVERFLOW" };

			for (int color = 0; color < 2; color++)
			{
				auto in = content[color];
				if (verdict[color] == "OK" && in.isObject())
				{
					if (in["block"].isInt())
					{
						int b = currBlocks[1 - color] = in["block"].asInt();
						if (b < 0 || b >= 7)
						{
							bad[color] = true;
							error[color] = "BLOCK_FORMAT_ERROR";
							continue;
						}
						typeCountByColor[color][b]++;
						int max = 0, min = 99;
						for (int j = 0; j < 7; j++)
						{
							if (typeCountByColor[color][j] > max)
								max = typeCountByColor[color][j];
							if (typeCountByColor[color][j] < min)
								min = typeCountByColor[color][j];
						}
						if (max - min > 2)
						{
							bad[color] = true;
							error[color] = "BLOCK_RESTRICTION_VIOLATED";
							continue;
						}
					}
					else
					{
						bad[color] = true;
						error[color] = "BLOCK_FORMAT_ERROR";
						continue;
					}

					auto seq = in["seq"];
					int x, y, o;
					if (!seq.isNull() && seq.isArray())
					{
						int _x[MAPHEIGHT + 1], _y[MAPHEIGHT + 1], _o[MAPHEIGHT + 1], n = seq.size();
						if (n > MAPHEIGHT)
						{
							bad[color] = true;
							error[color] = "SEQ_TOO_LONG";
							continue;
						}
						else if (n == 0)
						{
							bad[color] = true;
							error[color] = "BAD_SEQ";
							goto next;
						}
						for (int j = 0; j < n; j++)
						{
							if (seq[j]["x"].isInt() && seq[j]["y"].isInt() &&
								seq[j]["o"].isInt())
							{
								_x[j] = seq[j]["x"].asInt();
								_y[j] = seq[j]["y"].asInt();
								_o[j] = seq[j]["o"].asInt();
							}
							else
							{
								bad[color] = true;
								error[color] = "SEQ_FORMAT_ERROR";
								goto next;
							}
						}
						x = _x[n - 1];
						y = _y[n - 1];
						o = _o[n - 1];
					}
					else if (in["x"].isInt() && in["y"].isInt() &&
						in["o"].isInt())
					{
						x = in["x"].asInt();
						y = in["y"].asInt();
						o = in["o"].asInt();
					}
					else
					{
						bad[color] = true;
						error[color] = "LAST_POS_FORMAT_ERROR";
						continue;
					}
					//if (moveSeq(color, lastBlocks[color], n, x, y, o))
					Tetris finalPos(lastBlocks[color], x, y, o);
					if (isLast)
					{
						if (!finalPos.isValid(color))
						{
							bad[color] = true;
							error[color] = "LAST_POS_INVALID";
							continue;
						}

						const Tetris *r;
						if ((r = checkIfReachable(color, lastBlocks[color], x, y, o)))
						{
							int j = 0;
							while (r != searchBegin)
							{
								calculatedRoute[color][j]["x"] = r->blockX;
								calculatedRoute[color][j]["y"] = r->blockY;
								calculatedRoute[color][j]["o"] = r->orientation;
								r = fromWhere[r->blockY][r->blockX][r->orientation];
								j++;
							}
						}
						else
						{
							bad[color] = true;
							error[color] = "NO_ROUTE";
							continue;
						}
					}
					if (!finalPos.isGround(color) || !finalPos.paste(color))
					{
						bad[color] = true;
						error[color] = "LAST_POS_NOT_ON_GROUND";
						continue;
					}
					lastMove[color][0] = x;
					lastMove[color][1] = y;
					lastMove[color][2] = o;
					eliminate(color);
				}
				else
				{
					bad[color] = true;
					error[color] = "INVALID_INPUT_VERDICT_" + verdict[color];
				}
			next:
				;
			}
			int result;
			if (!bad[0] && !bad[1])
			{
				lastBlocks[0] = currBlocks[0];
				lastBlocks[1] = currBlocks[1];

				if (transCount[0] == 0 && transCount[1] == 0)
					result = -1;
				else
					result = transfer();

				if (result == -1)
				{
					bool able[2] = { canPut(0, lastBlocks[0]), canPut(1, lastBlocks[1]) };
					if (able[0] && !able[1])
						result = 1;
					else if (able[1] && !able[0])
						result = 0;
					else if (!able[0] && !able[1])
					{
						if (elimTotal[0] > elimTotal[1])
						{
							result = 1;
							error[1] += "_LOWSCORE";
						}
						else if (elimTotal[0] < elimTotal[1])
						{
							result = 0;
							error[0] += "_LOWSCORE";
						}
						else
							result = -2;
					}
				}
				else if (result == -2)
				{
					if (elimTotal[0] > elimTotal[1])
					{
						result = 1;
						error[1] += "_LOWSCORE";
					}
					else if (elimTotal[0] < elimTotal[1])
					{
						result = 0;
						error[0] += "_LOWSCORE";
					}
				}
			}
			else
			{
				if (bad[0] && !bad[1])
					result = 0;
				else if (bad[1] && !bad[0])
					result = 1;
				else
					result = -2;
			}

			if (isLast)
			{
				output["display"]["0"]["block"] = lastBlocks[1];
				output["display"]["1"]["block"] = lastBlocks[0];
				output["display"]["0"]["route"] = calculatedRoute[0];
				output["display"]["1"]["route"] = calculatedRoute[1];
			}

			if (result != -1)
			{
				//返回的是输了的一方
				output["display"]["result"] = result;
				output["command"] = "finish";
				if (result == 0)
				{
					output["content"]["0"] = 0;
					output["content"]["1"] = 2;
					output["display"]["err"]["0"] = error[0];
				}
				else if (result == 1)
				{
					output["content"]["0"] = 2;
					output["content"]["1"] = 0;
					output["display"]["err"]["1"] = error[1];
				}
				else
				{
					output["content"]["0"] = 1;
					output["content"]["1"] = 1;
					output["display"]["err"]["0"] = error[0];
					output["display"]["err"]["1"] = error[1];
				}
				break;
			}
			else if (isLast)
			{
				output["command"] = "request";
				output["content"]["0"]["x"] = lastMove[1][0];
				output["content"]["0"]["y"] = lastMove[1][1];
				output["content"]["0"]["o"] = lastMove[1][2];
				output["content"]["0"]["block"] = lastBlocks[0];
				output["content"]["1"]["x"] = lastMove[0][0];
				output["content"]["1"]["y"] = lastMove[0][1];
				output["content"]["1"]["o"] = lastMove[0][2];
				output["content"]["1"]["block"] = lastBlocks[1];
			}
		}

	}
	Json::FastWriter writer;
	cout << writer.write(output) << endl;
}