diff --git a/Tetris.cpp b/Tetris.cpp new file mode 100644 index 0000000..06314ed --- /dev/null +++ b/Tetris.cpp @@ -0,0 +1,287 @@ +/* Tetris.cpp -- file */ +#include "Tetris.h" + +bool printNextBoxVal = true; // 是否更新下一个方块的输出区 +bool printMapVal = true; // 是否更新地图的输出区 +bool isGameOver = false; // 是否游戏失败 +char Map[mapWidth][mapHeight] = {0}; +int speed = 400; + +HANDLE hStdOut; +Box thisBox, nextBox; + +// 检查该方块在地图上是否可行 +bool isFeasible(const Box &target) +{ + for(int YOffset = 0; YOffset < 4; ++YOffset) + { + for(int XOffset = 0; XOffset < 4; ++XOffset) + { + if(target.getShape() & (0x8000 >> (YOffset*4 + XOffset))) + { + // 如果该位置越界 返回false + if(target.x + XOffset < 0 || target.x + XOffset >= mapWidth + || target.y + YOffset < 0 || target.y + YOffset >= mapHeight) + return false; + + // 如果该位置上已经有方块 + if(Map[target.x + XOffset][target.y + YOffset]) + return false; + } + } + } + return true; +} + +// 将方块固定在地图上 +void fixedPos(const Box &target) +{ + for(int YOffset = 0; YOffset < 4; ++YOffset) + { + for(int XOffset = 0; XOffset < 4; ++XOffset) + { + // 如果判断的位置越界 + if(target.x + XOffset < 0 || target.x + XOffset >= mapWidth + || target.y + YOffset < 0 || target.y + YOffset >= mapHeight) + continue; + + if(target.getShape() & (0x8000 >> (YOffset*4 + XOffset))) + { + Map[target.x + XOffset][target.y + YOffset] = 1; + clearCompleted(); + } + } + } +} + +bool move(Box &A, char XOffset, char YOffset) +{ + char oldX = A.x, oldY = A.y; + printBox(A, " "); + A.x += XOffset, A.y += YOffset; + if(!isFeasible(A)) + { + A.x = oldX, A.y = oldY; + printBox(A, "▓"); + if(YOffset > 0){ // 方块落下 + fixedPos(A); + A = nextBox; + printNextBoxVal = true; // 通知 下一个方块显示区域更新 + if(!isFeasible(A)) // 如果生成的方块的位置上已经有方块,判断游戏失败 + { + isGameOver = true; + } + } + return false; + } + printBox(A, "▓"); + return true; +} + +bool rotate(Box &A) +{ + char oldDir = A.dir; + A.dir = (A.dir + 1) % 4; + if(!isFeasible(A)) + { + A.dir = oldDir; + return false; + } + A.dir = oldDir; + printBox(A, " "); + A.dir = (A.dir + 1) % 4; + printBox(A, "▓"); + return true; +} + +void clearCompleted() +{ + for(int y = mapHeight-1, x = 0; y >= 0; ) + { + for(x = 0; x < mapWidth; ++x) + { + if(!Map[x][y]) + break; + } + // 达成条件 + if(x == mapWidth) + { + // 把该层以上的所有方块往下移动一格 + for(int j = y; j > 0; --j) + { + for(int i = 0; i < mapWidth; ++i) + { + Map[i][j] = Map[i][j-1]; + } + } + // 清空顶层 + for(int i = 0; i < mapWidth; ++i) + Map[i][0] = 0; + + if(speed > 200) + speed -= 10; + printMapVal = true; // 通知更新地图 + }else{ //未达成条件就接着往上检查 + --y; + } + } +} + +void restart() +{ + for(int i = 0; i < mapWidth; ++i) + for(int j = 0; j < mapHeight; ++j) + Map[i][j] = 0; + printMapVal = true; + thisBox.randGenerateNewBox(); + printNextBoxVal = true; + speed = 400; + isGameOver = false; +} + +void printXY(COORD coord, std::string str) +{ + SetConsoleCursorPosition(hStdOut, coord); + WriteConsole(hStdOut, str.c_str(), str.length(), NULL, NULL); +} + +// 在指定位置输出方块,x,y缺省为0,如果为0则按照Box坐标输出 +void printBox(const Box &target, std::string printChar, short x, short y) +{ + short posX = x?x:target.x * 2 + 2; // +2是因为边框占了2格宽 + short posY = y?y:target.y; + for(int YOffset = 0; YOffset < 4; ++YOffset) + { + for(int XOffset = 0; XOffset < 4; ++XOffset) + { + // 如果输出的位置不合法 + if(posX + XOffset < 0 || posY + YOffset < 0) + continue; + + if(target.getShape() & (0x8000 >> (YOffset*4 + XOffset))) + printXY({posX + XOffset*2, posY + YOffset}, printChar); + } + } +} + +void showMap(short _x, short _y) +{ + for(short x = 0; x <= mapWidth+1; ++x) + { + printXY({_x + x*2, _y + mapHeight}, "█"); + } + for(short y = 0; y < mapHeight; ++y) + { + printXY({_x, _y + y}, "█"); + printXY({_x + mapWidth * 2 + 2, _y + y}, "█"); + for(short x = 0; x < mapWidth; ++x) + { + printXY({_x + 2 + x*2, _y + y}, (Map[x][y] ? "▓" : " ")); + } + } +} + +void input() +{ + char tempPos = 0; + switch(getch()) + { + case 'w': + case 'W': + rotate(thisBox); + break; + case 'a': + case 'A': + move(thisBox, -1, 0); + break; + case 'd': + case 'D': + move(thisBox, 1, 0); + break; + case 's': + case 'S': + move(thisBox, 0, 1); + break; + case ' ': + while(move(thisBox, 0, 1)); + break; + case 'z': + case 'Z': + for(int i = 0; i < mapWidth; ++i) + Map[i][mapHeight-1] = 1; + clearCompleted(); + break; + case 'r': + case 'R': + restart(); + break; + case '\n': + case '\r': + printXY({30, 14}, "暂停中..."); + printXY({30, 15}, "按任意键继续"); + getch(); + printXY({30, 14}, " "); + printXY({30, 15}, " "); + break; + case 'q': + case 'Q': + exit(1); + break; + } +} + +void GameLoop() +{ + thisBox.randGenerateNewBox(); + nextBox.randGenerateNewBox(); + system("cls"); + printXY({30, 5}, "w 旋转"); + printXY({30, 6}, "s 下落一格"); + printXY({30, 7}, "a 左移 d 右移"); + printXY({30, 8}, "空格 直接落下"); + printXY({30, 9}, "r 重新开始"); + printXY({30, 10}, "回车键暂停"); + printXY({30, 11}, "q 退出"); + while(!isGameOver) + { + if(printMapVal) + { + showMap(0, 0); + printMapVal = false; + } + if(printNextBoxVal) + { + printXY({30, 0}, "next:"); + printBox(nextBox, " ", 30, 1); + nextBox.randGenerateNewBox(); + printBox(nextBox, "▓", 30, 1); + printNextBoxVal = false; + } + printBox(thisBox, "▓"); + + move(thisBox, 0, 1); + + while(kbhit()) + input(); + + if(isGameOver) + { + printXY({6, 10}, "游戏结束!"); + printXY({4, 11}, "重新开始请按R"); + char ch = getch(); + if(ch == 'r' || ch == 'R') + restart(); + } + + Sleep(speed); + } +} + +int main() +{ + hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + srand(std::time(NULL)); + GameLoop(); + + return 0; +} \ No newline at end of file diff --git a/Tetris.h b/Tetris.h new file mode 100644 index 0000000..947c5c6 --- /dev/null +++ b/Tetris.h @@ -0,0 +1,66 @@ +/* Tetris.h -- file */ +#ifndef _TETRIS_H_ +#define _TETRIS_H_ +#include +#include +#include +#include +#include + + +const char mapWidth = 10; +const char mapHeight = 20; +const char startX = mapWidth / 2 - 2; +const char startY = 0; + +const char shapeListMax = 7; +const uint16_t shapeList[shapeListMax][4] = { + {0xcc00, 0xcc00, 0xcc00, 0xcc00}, // O + {0x4444, 0xf00, 0x4444, 0xf00 }, // I + {0x88c0, 0xe800, 0xc440, 0x2e00}, // L + {0x44c0, 0x8e00, 0xc880, 0xe200}, // J + {0x4c80, 0xc600, 0x4c80, 0xc600}, // Z + {0x8c40, 0x6c00, 0x8c40, 0x6c00}, // S + {0x4c40, 0x4e00, 0x8c80, 0xe400} // T +}; +class Box { +public: + Box(char _index = 0, char _dir = 0, char _x = 0, char _y = 0) + :index(index), dir(_dir), x(_x), y(_y){}; + uint16_t getShape() const {return shapeList[index][dir];}; + void randGenerateNewBox() + { + index = rand() % shapeListMax; + dir = 1; // 起始方向 + x = startX; // 起始x坐标 + y = startY; // 起始y坐标 + } +public: + char index; + char dir; + char x; + char y; +}; + +// 检查该方块在地图上是否可行 +bool isFeasible(const Box &target); +// 将方块固定在地图上 +void fixedPos(const Box &target); +// 移动方块到指定偏移量上 +bool move(Box &A, char XOffset, char YOffset); +// 旋转方块 +bool rotate(Box &A); +// 清除达成目标的方块 +void clearCompleted(); +// 在指定坐标上输出字符串 +void printXY(COORD coord, std::string str); +// 在指定坐标上输出指定字符的方块 +void printBox(const Box &target, std::string printChar, short x = 0, short y = 0); +// 展示地图 +void showMap(COORD coord); +// 获取用户输入并判断 +void input(); +// 游戏循环 +void GameLoop(); + +#endif \ No newline at end of file diff --git a/a.exe b/a.exe new file mode 100644 index 0000000..6a27eb8 Binary files /dev/null and b/a.exe differ diff --git a/data.txt b/data.txt new file mode 100644 index 0000000..2d4364b --- /dev/null +++ b/data.txt @@ -0,0 +1,40 @@ +1100110000000000 +O = 0xcc00 + +0100 0000 +0100 1111 +0100 0000 +0100 0000 +I = 0x44 0xf00 + +1000 1110 1100 0010 +1000 1000 0100 1110 +1100 0000 0100 0000 +0000 0000 0000 0000 +L = 0x88c0 0xe800 0xc440 0x2e00 + +0100 1000 1100 1110 +0100 1110 1000 0010 +1100 0000 1000 0000 +0000 0000 0000 0000 +J = 0x44c0 0x8e00 0xc880 0xe200 + +1100 0100 +0110 1100 +0000 1000 +0000 0000 +Z = 0xc600 0x4c80 + +0110 1000 +1100 1100 +0000 0100 +0000 0000 +S = 0x6c00 0x8c40 + +1110 0100 0100 1000 +0100 1100 1110 1100 +0000 0100 0000 1000 +0000 0000 0000 0000 +T = 0xe400 0x4c40 0x4e00 0x8c80 + +▓▓██ \ No newline at end of file diff --git a/tetris.exe b/tetris.exe new file mode 100644 index 0000000..08aba98 Binary files /dev/null and b/tetris.exe differ