查看“Tetris”的源代码
←
Tetris
跳转至:
导航
,
搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
俄罗斯方块(Tetris)是[[Botzone]]平台上的双人回合制[[游戏]]。 == 作者 == 裁判程序 ''zys'' 播放器 ''zhouhy'' == 故事 == (尚未完成,稍安勿躁) 书接上文,自那[[Pacman#故事|琼柰派入门考校]]之后,四位敝衣少年经历种种<del>玄学</del>坎坷,终于拜得上仙为师,却哪知,道门也不是一个远离纷争的所在。他们上了山入了门,没想到师门给他们的第一件任务,竟是远渡重洋查探魔门。 「本派密探报,有海外倭岛名唤『贾巴沃克』者,以望气之法查探,魔气四溢,其中必有蹊跷。尔等初入本派,须有作为,这便是你们立功的大好时机!」 「可是,我们…还什么都…」 「噫,莫急,为师自有秘宝可保你们平安~」 说着,少年们的面前,四只小方块闪着奇异的光芒,带着流光飘进了他们的衣带。 「记得,若有危难,切莫逞能,四宝合一,方有生机!」 …… 风和日丽的贾巴沃克岛上,似乎有着一股奇怪的气场,让人感到说不出的烦闷。 「唔噗噗噗……成天与代码之类的死物打交道也真是无聊,不过看来,有人正带着有趣的风物来玩了呢」 (未完待续) == 人类玩家操作说明 == 两种操作方式任选其一即可。 *鼠标 **点击对方场地上的图标可以给对手指定方块 **拖动块可以移动位置,也可以穿墙(仅限可以直接从顶部落到的地方) **左键点击块可以使之坠落到底 **右键点击屏幕可以提交操作(此时方块必须落地) *键盘 **数字1~7可以给对手指定方块 **上下左右键可以移动位置 **英文/键可以将方块坠落到底 **回车键可以提交操作(此时方块必须落地) **英文.键可以枚举方块的下一个可能的初始状态(用于在场地顶部转不过来之类的情况) == 游戏规则 == 本游戏为'''双人回合制'''游戏,每个玩家在独立的矩阵场地上进行游戏。 每回合,双方玩家同时决策,既需要控制自己的方块落地,又需要为对手指定下一回合的方块。 玩家消去行后,方块会转移到对方场地底部,从而给对方增加难度。 === 地图 === 每个玩家的场地是高20格、宽10格的矩阵。在程序中,坐标是(x, y)的形式,其中x是横坐标,y是纵坐标。最左下方的格子的坐标是(1, 1)。 === 方块 === 游戏中,方块共有七种类型,每种类型的方块有四种姿态(即方向)。 方块只能逆时针旋转(即上左下右的顺序进行旋转),每次旋转时需要保证中间经过的姿态和最终的姿态都是合法的。旋转时旋转中心不动。 [[File:Tetris.Blocks.png]] 上图的方块在程序中,从左到右的类型编号分别是数字0~6,上左下右四种方向分别对应着数字0~3。 方块的坐标即其旋转中心的坐标。 === 给对手指定方块 === 第一回合,双方的方块类型由<del>玄学</del>系统指定,而且是相同的类型。 除了第一回合之外,每个回合的方块类型都是由对方指定的。因此,每回合玩家都要给出对方下一个方块的类型。 指定方块时,必须保证对方的各类方块的数目的极差(即最大值减最小值)不超过2,否则将会被视为非法行为。 === 描述自己的方块落地点 === 每回合,玩家可以得知自己本回合控制的方块类型。 (20日前发布的规则中的落地序列'''依然有效''',你可以选择不修改以前的程序而正常进行游戏,我们会取最后一个元组作为实际输入,但是我们提供了更简单的交互方式) 你只需要给出你的块的最终状态的横坐标x、纵坐标y、姿态o,系统会'''自动寻找'''一个能够从场地顶端到目标位置的、纵坐标单调不增的路径,其中动作包括'''逆时针旋转、水平移动和垂直向下移动'''。 * 系统寻找的路径的起点会保证整个块都在场地内,且可以从y=20的地方“畅通无阻”地落到起点(所谓「从y=20的地方畅通无阻地落到起点」等价于对起点应用样例的checkDirectDropTo函数) * 如果找不到满足条件的路径,则属于非法输入,并会被判负。 === 方块的消除与转移 === 每回合方块落地后,如果有行已满,那么这些行将会被“消除”,其它的行下落补上空行。 被消除的行并不会消失,而是会去除最后一个落地的方块(即本回合刚刚造成这几行消除的方块),然后按照同样的顺序堆叠起来,放置在对方的场地底部。对方场地原有的方块就会被“顶起来”。 === 积分 === 玩家具有积分,但是仅用于平局时的处理。 对于每一回合,玩家一次性消去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#简化交互]]。 对于[https://www.botzone.org/match/58f8aad833b28604c34b9693 样例对局],第四回合'''红'''色玩家的样例输入和样例输出如下: '''输入''' <pre class="mw-collapsible mw-collapsed"> 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 </pre> '''输出''' <pre class="mw-collapsible mw-collapsed"> 2 3 2 2 </pre> ===[[JSON]]交互=== 每回合[[Bot]]收到的request'''不是字符串''',而是一个[[JSON]]对象,格式如下: '''第一回合''' <syntaxhighlight lang="javascript"> { "block": Number, // 表示玩家本回合块类型 "color": Number // 表示玩家颜色 } </syntaxhighlight> '''其他回合''' <syntaxhighlight lang="javascript"> { "x": Number, // x和y表示对方方块落在的位置坐标,o表示对方方块的姿态 "y": Number, "o": Number, "block": Number // 表示给对手的下回合块类型 } </syntaxhighlight> Bot所需要输出的response也是[[JSON]]对象,格式如下: <syntaxhighlight lang="javascript"> { "x": Number, // x和y表示自己方块最后的位置坐标,o表示这时方块的姿态 "y": Number, "o": Number, "block": Number // 表示给对手的下回合块类型 } </syntaxhighlight> 对于[https://www.botzone.org/match/58f8aad833b28604c34b9693 样例对局],第四回合'''蓝'''色玩家的样例输入和样例输出如下:(实际是单行紧缩的) '''输入''' <pre class="mw-collapsible mw-collapsed"> { "requests": [ { "block": 1, "color": 1 }, { "block": 3, "o": 2, "x": 2, "y": 1 }, { "block": 5, "o": 2, "x": 5, "y": 1 }, { "block": 2, "o": 2, "x": 8, "y": 1 } ], "responses": [ { "x": 2, "y": 1, "o": 2, "block": 1 }, { "x": 1, "y": 4, "o": 1, "block": 2 }, { "x": 4, "y": 2, "o": 1, "block": 2 } ] } </pre> '''输出''' <pre class="mw-collapsible mw-collapsed"> { "response": { "x": 6, "y": 2, "o": 0, "block": 3 } } </pre> == 样例程序 == 更新历史: * 简化交互方式 * 修正transfer函数的大于等于号 * 修正elimBonus数组的定义 * 修正JSON样例的rotation函数 '''请注意展开按钮在右侧!'''-----=====≡≡≡≡≡> === 简单交互样例程序 === <syntaxhighlight lang="cpp" class="mw-collapsible mw-collapsed"> /** * Tetris 简单交互样例程序 * https://wiki.botzone.org/index.php?title=Tetris * 更新于2017年4月20日: * 修正了rotation函数、将交互方式修改为新规则的格式,还有transfer函数里`if (h2 >= MAPHEIGHT)`改为`if (h2 > MAPHEIGHT)` */ // 注意: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[] = { 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 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(-1, -1, fromO)) 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 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; } </syntaxhighlight> === [[JSON]]交互样例程序 === 本地编译和调试的方式请查看 [[JSONCPP]]。 <syntaxhighlight lang="cpp" class="mw-collapsible mw-collapsed"> /** * Tetris JSON交互样例程序 * https://wiki.botzone.org/index.php?title=Tetris * 更新于2017年4月20日: * 修正了rotation函数、将交互方式修改为新规则的格式,还有transfer函数里`if (h2 >= MAPHEIGHT)`改为`if (h2 > MAPHEIGHT)` */ // 注意: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[] = { 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 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(-1, -1, fromO)) 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(); x = myOutput["x"].asInt(); y = myOutput["y"].asInt(); o = myOutput["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, 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; } // 决策结束,输出结果(你只需修改以上部分) Json::Value output; Json::FastWriter writer; output["response"]["block"] = blockForEnemy; output["response"]["x"] = finalX; output["response"]["y"] = finalY; output["response"]["o"] = finalO; cout << writer.write(output); return 0; } </syntaxhighlight>
返回至
Tetris
。
导航菜单
个人工具
中文(中国大陆)
创建账户
登录
命名空间
页面
讨论
变种
视图
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
帮助
工具
链入页面
相关更改
特殊页面
页面信息