命令行俄罗斯方块

来源:互联网 发布:梦里花落知多少结局 编辑:程序博客网 时间:2024/05/16 06:15

命令行俄罗斯方块

 

         2012 年时候做的一个小游戏,运行在命令行界面下。那时刚学完C++,还不会用图形库,只能用字符来画方块了。

         画面简陋,但仍能体验到这个游戏的魅力。

        运行截图:


       代码不多,开袋即食微笑

//*********************************************////   ////                 Tetris       ////                  2012            ////   ////*********************************************//#include <conio.h>//_kbhit(),_getch()#include <iostream>#include <windows.h>//Sleep()#include <time.h>//clock(),time()#include <stdlib.h>//srand(),rand()using namespace std;#define TRUE1#define FALSE0#define ROW20// 行#define RANK10// 列(应该叫 COLUMN )#define Number_Of_Blocks4#define drop_time_interval700//方块下落时间间隔(ms)。#define char_of_empty' '#define char_of_block'O'#define char_of_container'*'#define char_of_border_corner'#'#define vertical_border'|'#define horizontal_border'-'typedef short unit;typedef short boolean_my;enum BlockType{O, I, S, Z, T, L, J};enum BlockForm{s0, s1, s2, s3};enum MoveDirection{UP, DOWN, LEFT, RIGHT};enum SpinDirection{CW, ACW};class Coordinate{public:unit x;unit y;Coordinate(): x(0), y(0) {}void DisplayF() {cout << "( " << x << ',' << y << " )" << endl;}void Set(unit a = 0, unit b = 0) {x = a; y = b;}}; //class CoordinateCoordinate bbase[7][4][Number_Of_Blocks];char ground[ROW + 2][RANK + 2];unit ContainerHeight = 0;class Block{public:BlockType type;BlockForm form;Coordinate position;public:void Initial();void ResetPosition();void ChangeBlock(char char_change_to);void WipeBlock();void DrawBlock();void FixBlock();BlockTypeTransType();boolean_mySpin(SpinDirection s_d);boolean_myMove(MoveDirection m_d, unit step);boolean_myTestOverlap(BlockForm next_form, unit row_x, unit rank_y);//设3个参数是因为函数要判断的形态,位置不一定是对象当前的形态,位置。}; //class Blockclass Container{private:Coordinate anchor;public:void Initial();void Scan(Block *pblo);boolean_myIsThisRowFull(unit RowNumber);//此函数为void Scan(Block *pblo)函数服务。boolean_myRowDrop(unit RowToDrop);       //此函数为void Scan(Block *pblo)函数服务。boolean_myCheckOver();}; //class Containervoid Container::Initial(){anchor.Set(0,0);ContainerHeight = 0;unit i,j;for(i = 1; i <= ROW; i++)//初始化非边框内容。for(j = 1; j <= RANK; j++)ground[i][j] = char_of_empty;for(i = 1; i <= ROW; i++)//初始化垂直边框。ground[i][0] = ground[i][RANK+1] = vertical_border;for(i = 0; i <= RANK; i++)//初始化水平边框。ground[0][i] = ground[ROW+1][i] = horizontal_border;ground[0][0] = ground[0][RANK+1] = ground[ROW+1][0] = ground[ROW+1][RANK+1] = char_of_border_corner;//四个角。}boolean_my Container::IsThisRowFull(unit RowNumber){unit i = 1;while( ground[RowNumber][i] == char_of_container )i++;if( i < (RANK + 1) )return FALSE;else return TRUE;}boolean_my Container::RowDrop(unit RowToDrop){//注意这个函数【不会】修改容器当前高度ContainerHeight。unit i;if( (RowToDrop > 0) && (RowToDrop < ROW) ) {//要下落的行序号必须在[1,ROW-1]的闭区间范围内。for(i = 1; i <= RANK; i++){//从左到右把要下落的行一个一个字符赋给下一行的对应字符。ground[RowToDrop + 1][i] = ground[RowToDrop][i];ground[RowToDrop][i] = char_of_empty;//清空要下落的格子。循环结束后,整行就会被清空了。}return TRUE;}else return FALSE;}void Container::Scan(Block *pblo){unit j, RowDropUntilTo, RowToJudge;unit NumberOfRowDeleted = 0;//用来记录【已经】有多少行被消除,同时作为RowDelete数组录入被消除的行的行号时的下标。short i;short RowDelete[Number_Of_Blocks];//消除行是从上到下的,下落行也是从上到下,方块模板的坐标顺序也是从上到下(并从左到右,但这里用不上从左到右这个特点)的。故要删除的行的行号也应该从上到下按顺序存放到这个数组中。for( i = 0; i < Number_Of_Blocks; i++ )//初始化RowDelete数组,每个元素都是-1。RowDelete[i] = -1;for(i = 0; i < Number_Of_Blocks; i++) {//多少个方块元素,就判断多少次,循环变量i在循环中有重要意义。RowToJudge = (*pblo).position.x + bbase[(*pblo).type][(*pblo).form][i].y;//计算出要判断的行号。if(IsThisRowFull(RowToJudge)) {for(j = 1; j <= RANK; j++)//清空容器中第RowToJudge行。ground[RowToJudge][j] = char_of_empty;RowDelete[NumberOfRowDeleted++] = RowToJudge;//把满的行(也即被消除的行)的行号录入RowDelete数组,供RowDrop()函数按从上到下的顺序(也就是行号被录入的顺序)下落已满的行的【上一行】,故录入顺序很重要。}}for( i = 0; i < NumberOfRowDeleted; i++ ) {//i是作为RowDelete数组的下标。RowDropUntilTo = (ROW - ContainerHeight + 1);//计算出要下落的行中的最高行。while( RowDelete[i] > RowDropUntilTo ) {//从被消除的行开始,至最高且有内容物的行为止,由下往上逐行下落。容器数组的行号是从上到下递增的。RowDrop(RowDelete[i] - 1);RowDelete[i]--;//每下落完一行,RowDelete[i]中的行号就往上移一行。};ContainerHeight--;//每下落完一行,ContainerHeight就减少1。}}boolean_my Container::CheckOver(){if( ContainerHeight < ROW )return FALSE;else return TRUE;}void Block::Initial(){type=I;form=s0;ResetPosition();}BlockType Block::TransType(){//这个随机数产生得并不完善。form = s0;BlockType rand_type;//srand(unsigned int(time(NULL)));do {rand_type = BlockType(rand() % 10);} while(rand_type > 6);type = rand_type;return type;}void Block::ResetPosition(){if( type == I )position.Set(1,4);else position.Set(1,5);}void Block::ChangeBlock(char char_change_to){short i;for( i = 0; i < Number_Of_Blocks; i++ ) {ground[position.x + (bbase[type][form][i].y)][position.y + (bbase[type][form][i].x)] = char_change_to;}}void Block::WipeBlock(){ChangeBlock(char_of_empty);}void Block::DrawBlock(){ChangeBlock(char_of_block);}void Block::FixBlock(){ChangeBlock(char_of_container);unit PBPH = (ROW - position.x + 1);//PBPH=Present Block Position Height。当前方块高度。if( PBPH > ContainerHeight )//把方块归入容器后,测量一次容器内容物当前高度。ContainerHeight = PBPH;}boolean_my Block::TestOverlap(BlockForm form, unit row_x, unit rank_y){short i;char test_unit;for( i = 0; i < Number_Of_Blocks; i++ ) {test_unit = ground[row_x + (bbase[type][form][i].y)][rank_y + (bbase[type][form][i].x)];if((test_unit != char_of_block) && (test_unit != char_of_empty))return FALSE;}return TRUE;//若for循环中没执行return,则说明通过检测。}boolean_my Block::Spin(SpinDirection s_d){BlockForm nextform = form;switch(s_d) {case CW  :if(nextform == s3) nextform = s0; else nextform = BlockForm(nextform + 1); break;case ACW :if(nextform == s0) nextform = s3; else nextform = BlockForm(nextform - 1); break;default  :break;}if( TestOverlap(nextform, position.x, position.y) ) {WipeBlock();//变形前先擦除容器中当前方块,因为此时Block::form值还没改变。form = nextform;return TRUE;}else return FALSE;}boolean_my Block::Move(MoveDirection m_d, unit step = 1){unit next_row_x = position.x;unit next_rank_y = position.y;switch( m_d ) {case UP   :next_row_x -= step; break;case DOWN :next_row_x += step; break;case LEFT :next_rank_y -= step; break;case RIGHT:next_rank_y += step; break;default   :break;}if( TestOverlap(form, next_row_x, next_rank_y) ) {WipeBlock();//移动前先擦除容器中当前方块,因为此时Block::form值还没改变。position.Set(next_row_x, next_rank_y);return TRUE;}else return FALSE;}void InitialBlockTemplate(){//***** O *******///state0                    //state1bbase[O][s0][0].Set(0,0);bbase[O][s1][0].Set(0,0);bbase[O][s0][1].Set(1,0);bbase[O][s1][1].Set(1,0);bbase[O][s0][2].Set(0,1);bbase[O][s1][2].Set(0,1);bbase[O][s0][3].Set(1,1);bbase[O][s1][3].Set(1,1);//state2//state3bbase[O][s2][0].Set(0,0);bbase[O][s3][0].Set(0,0);bbase[O][s2][1].Set(1,0);bbase[O][s3][1].Set(1,0);bbase[O][s2][2].Set(0,1);bbase[O][s3][2].Set(0,1);bbase[O][s2][3].Set(1,1);bbase[O][s3][3].Set(1,1);//***** O *******************************************///***** I *******///state0                    //state1bbase[I][s0][0].Set(0,0);bbase[I][s1][0].Set(1,0);bbase[I][s0][1].Set(1,0);bbase[I][s1][1].Set(1,1);bbase[I][s0][2].Set(2,0);bbase[I][s1][2].Set(1,2);bbase[I][s0][3].Set(3,0);bbase[I][s1][3].Set(1,3);//state2//state3bbase[I][s2][0].Set(0,0);bbase[I][s3][0].Set(1,0);bbase[I][s2][1].Set(1,0);bbase[I][s3][1].Set(1,1);bbase[I][s2][2].Set(2,0);bbase[I][s3][2].Set(1,2);bbase[I][s2][3].Set(3,0);bbase[I][s3][3].Set(1,3);//***** I *******************************************///***** S *******///state0                    //state1bbase[S][s0][0].Set(1,0);bbase[S][s1][0].Set(0,0);bbase[S][s0][1].Set(2,0);bbase[S][s1][1].Set(0,1);bbase[S][s0][2].Set(0,1);bbase[S][s1][2].Set(1,1);bbase[S][s0][3].Set(1,1);bbase[S][s1][3].Set(1,2);//state2//state3bbase[S][s2][0].Set(1,0);bbase[S][s3][0].Set(0,0);bbase[S][s2][1].Set(2,0);bbase[S][s3][1].Set(0,1);bbase[S][s2][2].Set(0,1);bbase[S][s3][2].Set(1,1);bbase[S][s2][3].Set(1,1);bbase[S][s3][3].Set(1,2);//***** S *******************************************///***** Z *******///state0                    //state1bbase[Z][s0][0].Set(0,0);bbase[Z][s1][0].Set(2,0);bbase[Z][s0][1].Set(1,0);bbase[Z][s1][1].Set(1,1);bbase[Z][s0][2].Set(1,1);bbase[Z][s1][2].Set(2,1);bbase[Z][s0][3].Set(2,1);bbase[Z][s1][3].Set(1,2);//state2//state3bbase[Z][s2][0].Set(0,0);bbase[Z][s3][0].Set(2,0);bbase[Z][s2][1].Set(1,0);bbase[Z][s3][1].Set(1,1);bbase[Z][s2][2].Set(1,1);bbase[Z][s3][2].Set(2,1);bbase[Z][s2][3].Set(2,1);bbase[Z][s3][3].Set(1,2);//***** Z *******************************************///***** T *******///state0                    //state1bbase[T][s0][0].Set(0,0);bbase[T][s1][0].Set(1,0);bbase[T][s0][1].Set(1,0);bbase[T][s1][1].Set(0,1);bbase[T][s0][2].Set(2,0);bbase[T][s1][2].Set(1,1);bbase[T][s0][3].Set(1,1);bbase[T][s1][3].Set(1,2);//state2//state3bbase[T][s2][0].Set(1,0);bbase[T][s3][0].Set(1,0);bbase[T][s2][1].Set(0,1);bbase[T][s3][1].Set(1,1);bbase[T][s2][2].Set(1,1);bbase[T][s3][2].Set(2,1);bbase[T][s2][3].Set(2,1);bbase[T][s3][3].Set(1,2);//***** T *******************************************///***** L *******///state0                    //state1bbase[L][s0][0].Set(0,0);bbase[L][s1][0].Set(0,0);bbase[L][s0][1].Set(1,0);bbase[L][s1][1].Set(1,0);bbase[L][s0][2].Set(2,0);bbase[L][s1][2].Set(1,1);bbase[L][s0][3].Set(0,1);bbase[L][s1][3].Set(1,2);//state2//state3bbase[L][s2][0].Set(2,0);bbase[L][s3][0].Set(0,0);bbase[L][s2][1].Set(0,1);bbase[L][s3][1].Set(0,1);bbase[L][s2][2].Set(1,1);bbase[L][s3][2].Set(0,2);bbase[L][s2][3].Set(2,1);bbase[L][s3][3].Set(1,2);//***** L *******************************************///***** J *******///state0                    //state1bbase[J][s0][0].Set(0,0);bbase[J][s1][0].Set(2,0);bbase[J][s0][1].Set(1,0);bbase[J][s1][1].Set(2,1);bbase[J][s0][2].Set(2,0);bbase[J][s1][2].Set(1,2);bbase[J][s0][3].Set(2,1);bbase[J][s1][3].Set(2,2);//state2//state3bbase[J][s2][0].Set(0,0);bbase[J][s3][0].Set(1,0);bbase[J][s2][1].Set(0,1);bbase[J][s3][1].Set(2,0);bbase[J][s2][2].Set(1,1);bbase[J][s3][2].Set(1,1);bbase[J][s2][3].Set(2,1);bbase[J][s3][3].Set(1,2);//***** J *******************************************/} //void InitialBlockTemplate()void Refresh(){unit i,j;system("CLS");for(i = 0; i < (ROW + 2); i++) {for(j = 0; j < (RANK + 2); j++)switch(ground[i][j]) {case char_of_border_corner: cout << "┼"; break;case vertical_border      : cout << "┼"; break;case horizontal_border    : cout << "┼"; break;case char_of_empty        : cout << "  "; break;case char_of_block        : cout << "■"; break;case char_of_container    : cout << "□"; break;default: break;}cout<<endl;};}void DrawAndRefresh(Block *pblo){(*pblo).DrawBlock();Refresh();}void main(){char key = 'y';//为让最外层循环执行,初值为'y'。time_t begin,now;//time_t类型在time.h中定义。boolean_my refresh_flag = FALSE;Block rock;Container bottle;InitialBlockTemplate();rock.Initial();cout << "左移:A \n" << "右移:D \n" << "加快下落:S \n" << "直接下落:W \n" << "左旋:O \n" << "右旋:P \n" << "退出:按关闭窗口 \n\n" << "按任意键开始" << endl;_getch();while( key != char(27) && (key != 'n') && (key != 'N') ) {//所谓的“大循环”。bottle.Initial();srand( unsigned int(time(NULL)) );while( !bottle.CheckOver() ) {rock.ResetPosition();rock.TransType();do {DrawAndRefresh(&rock);//本循环开头刷新一次,因为最后的循环判断要下落一次方块。 begin = now = clock();while((now - begin) < drop_time_interval ){//捕获键盘指令循环。 if(_kbhit()) { key = _getch(); switch(key) { case 'a': case 'A': if(rock.Move(LEFT))  refresh_flag = TRUE; break; case 'd': case 'D': if(rock.Move(RIGHT)) refresh_flag = TRUE; break; case 'o': case 'O': if(rock.Spin(ACW)) refresh_flag = TRUE; break; case 'p': case 'P': if(rock.Spin(CW))  refresh_flag = TRUE; break; case 's':case 'S': if(rock.Move(DOWN)) {refresh_flag = TRUE; rock.Move(DOWN);}; break;case 'w':case 'W': if(rock.Move(DOWN)) refresh_flag = TRUE; while(rock.Move(DOWN)) {}; now += drop_time_interval; continue;  //w或W是方块直接到底,到底后,为了不用再等待余下的间隔时间,直接使now超过drop_time_interval,同时,为了避免执行下面的now = clock(),选择跳出当前循环周期,直接进行下一次的while(now)判断,这时now的值一定会终止while(now)循环,这时不用担心因为跳过当前循环周期而遗漏了方块改变后要刷新屏幕这一步,因为终止while(now)后,下一次的do-while循环一开始也会刷新一次屏幕,并且终止while(now)后执行的那次while(rock.Move(DOWN)是不会再改变方块的,因为W后方块已经到底了。其实把now += drop_time_interval; continue;这两句改成goto,直接跳到do-while循环的do那里,更直接了当,只是为了不用goto而已。 default : break; };//switch(key) };//if(_kbhit())   now = clock();//捕捉当前时间,放在这里捕捉是保证合理性,避免下面执行的语句占据方块可操作时间。   if(refresh_flag) {//判断是否有改变过方块,有则刷新屏幕。这个if需放在捕获键盘指令循环内,以便尽快输出方块的变化。 DrawAndRefresh(&rock);refresh_flag = FALSE;//刷新屏幕后,刷新标志置FALSE,以免下一次循环无意义地重复刷新屏幕。};  Sleep(16);// 降低 CPU 使用率。 };//while(now)} while(rock.Move(DOWN));rock.FixBlock();bottle.Scan(&rock);};//while( ! bottle.CheckOver() )cout << "辛苦眼睛了。还继续吗? Y/N:";while( (key != 'y') && (key != 'Y') && (key != 'N') && (key != 'n') && (key != char(27)) ) {if(_kbhit())key = _getch();Sleep(26);};cout << endl;};  //while(key)} //main()


0 0
原创粉丝点击