“Tetris2”的版本间的差异

来自Botzone Wiki
跳转至: 导航搜索
2个标签移动版编辑移动网页编辑
简单交互样例程序
第200行: 第200行:
  
 
=== 简单交互样例程序 ===
 
=== 简单交互样例程序 ===
 +
 +
20171027更新:将trans数组的第二维长度从4加大到6,感谢kczno1用户的指正。
  
 
<syntaxhighlight lang="cpp" class="mw-collapsible mw-collapsed">
 
<syntaxhighlight lang="cpp" class="mw-collapsible mw-collapsed">
 
/**
 
/**
 
  * Tetris2 样例程序
 
  * Tetris2 样例程序
 +
* 20171027更新:将trans数组的第二维长度从4加大到6,感谢kczno1用户的指正。
 
  * https://wiki.botzone.org/index.php?title=Tetris2
 
  * https://wiki.botzone.org/index.php?title=Tetris2
 
  */
 
  */
第230行: 第233行:
 
   
 
   
 
// 代表分别向对方转移的行
 
// 代表分别向对方转移的行
int trans[2][4][MAPWIDTH + 2] = { 0 };
+
int trans[2][6][MAPWIDTH + 2] = { 0 };
 
   
 
   
 
// 转移行数
 
// 转移行数

2017年10月27日 (五) 20:11的版本

俄罗斯方块·改(Tetris2)是Botzone平台上的双人回合制游戏。与Tetris相比,多出了关于旋转和连消奖励的两条额外规则,详情可以参看#方块#方块的消除与转移

如有需求,裁判程序源码可以参考Tetris2Judge

作者

裁判程序 zys 播放器 zhouhy

故事

(尚未完成,稍安勿躁)

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

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

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

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

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

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

……

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

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

(未完待续)

人类玩家操作说明

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

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

游戏规则

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

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

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

地图

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

方块

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

Tetris.Blocks.png

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

旋转中心用o进行了标记,方块的坐标即其旋转中心的坐标。

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

新规则:旋转时,每次旋转时需要保证中间经过的姿态和最终的姿态都是合法的,同时还需要保证中途经过的一些格点为空:

Tetris2.Blocks.BlankRequirement.png

上图中,红色、深灰色、浅灰色格子均需要为空,方可进行旋转。

给对手指定方块

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

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

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

描述自己的方块落地点

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

Tetris routeExplained.png

【常见问题】

我需要给出什么?

  • 你需要给出最终状态坐标,系统帮你算路径和路径起点,存在合法路径就算合法输入

最终状态有什么要求?

  • 从起点通过若干水平平移、竖直下降、旋转可达

起点有什么要求?

  • 可以从顶端“畅通无阻”落入
  • 起点也是系统帮你找的

这是不是意味着我的方块只能垂直落到场地里?

  • 不,这只意味着你的方块的运动路径需要从能垂直落到的地方开始,之后可以有平移旋转

……所以,什么样的最终状态才算合法?

  • 简单来说,就是“能从顶端出发、经过若干非升操作可达的状态”


【具体解释】

你只需要给出你的块的最终状态的横坐标x、纵坐标y、姿态o,系统会自动寻找一个能够从场地顶端到目标位置的、纵坐标单调不增的路径,其中动作包括逆时针旋转、水平移动和垂直向下移动

  • 系统寻找的路径的起点会保证整个块都在场地内,且可以从y=20的地方“畅通无阻”地落到起点。(所谓「从y=20的地方畅通无阻地落到起点」等价于对起点应用样例的checkDirectDropTo函数)
  • 路径起点的所有可能姿态都会被枚举,存在一种得到合法路径的可能性即可。
  • 如果找不到满足条件的路径,则属于非法输入,并会被判负。

方块的消除与转移

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

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

新规则:如果前两个回合己方均消除过行,那么本回合消除的行给对方时,将会多给对方一行(将最靠下的一行复制一遍放在下方),即“连消奖励”。

比如,第5、6、7、8、9回合己方均消去了行,那么第7、8、9回合消去时,给对方的行将会多一行。

积分

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

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

总积分是上述积分的和。

胜负

一切非法行为会被立即判负,包括程序崩溃、超时、坐标越界、格式错误、找不到到落点的路径等。双方同时判负会被认定为平局。

在互相给出方块后,如果场地中无法容纳该方块,则会被判负。

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

如果双方同时超过边界,或者同时放不下方块,那么积分高者胜。

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

游戏交互方式

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

如果希望在回合间传递数据,请参阅Bot#交互

本游戏仅提供简单交互的样例程序和介绍。

提示

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

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

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

之间的部分即可!

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

调试时可以通过拖动进度条来快速调试。

简单交互

request
第一回合的request是一行两个数字t和c,空格分隔,t表示自己拿到的方块类型,n表示自己的颜色(0红1蓝)
以后的request是一行四个数字t、x、y、o,空格分隔,t表示自己拿到的方块类型,x和y表示对方方块落在的位置坐标,o表示对方方块的姿态
response
每个回合的response都是一行四个数字t、x、y、o,空格分隔,t表示给对方下回合的方块类型,x和y表示自己方块最终点的位置坐标,o表示最终点方块的姿态

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

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

输入

4
1 0
3 2 1 2
1 2 1 2
5 5 1 2
2 1 4 1
2 8 1 2
2 4 2 1


输出

2 3 2 2

样例程序

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

简单交互样例程序

20171027更新:将trans数组的第二维长度从4加大到6,感谢kczno1用户的指正。

/**
 * Tetris2 样例程序
 * 20171027更新:将trans数组的第二维长度从4加大到6,感谢kczno1用户的指正。
 * https://wiki.botzone.org/index.php?title=Tetris2
 */
// 注意: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][6][MAPWIDTH + 2] = { 0 };
 
// 转移行数
int transCount[2] = { 0 };
 
// 运行eliminate后的当前高度
int maxHeight[2] = { 0 };
 
// 总消去行数的分数之和
int elimTotal[2] = { 0 };

// 连续几回合发生过消去了
int elimCombo[2] = { 0 };
 
// 一次性消去行数对应分数
const int elimBonus[] = { 0, 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

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:
	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;
		int i, blankX, blankY;
		while (true)
		{
			if (!isValid(-1, -1, fromO))
				return false;
 
			if (fromO == o)
				break;
	            
	        // 检查旋转碰撞
	        for (i = 0; i < 5; i++) {
	            blankX = blockX + rotateBlank[blockType][fromO][2 * i];
	            blankY = blockY + rotateBlank[blockType][fromO][2 * i + 1];
	            if (blankX == blockX && blankY == blockY)
	                break;
	            if (gridInfo[color][blankY][blankX] != 0)
	                return false;
	        }
 
			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, 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 ? 1 : gridInfo[color][i][j];
					if (count)
						gridInfo[color][i][j] = 0;
				}
		}
		if (count == 0)
			elimCombo[color] = 0;
		maxHeight[color] -= count - hasBonus;
		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 x, y, o;
		// 根据这些输入输出逐渐恢复状态到当前回合
 
		// 先读自己的输出,也就是自己的行为
		// 自己的输出是自己的最后一步
		// 然后模拟最后一步放置块
		cin >> blockType >> 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, finalX, finalY, finalO;
 
	// 做出决策(你只需修改以下部分)
 
	// 遇事不决先输出(平台上编译不会输出)
	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))
				{
					finalX = x;
					finalY = y;
					finalO = o;
					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 << " " << finalX << " " << finalY << " " << finalO;
 
	return 0;
}