赞
踩
俄罗斯方块是久负盛名的游戏,它也和贪吃蛇,扫雷等游戏位列经典游戏的⾏列。
《俄罗斯方块》(Tetris,俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。
该游戏曾经被多家公司代理过。经过多轮诉讼后,该游戏的代理权最终被任天堂获得。 任天堂对于俄罗斯方块来说意义重大,因为将它与GB搭配在一起后,获得了巨大的成功。
《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。
基本逻辑结构与贪吃蛇一致
我们有了贪吃蛇的知识储备及了解WIN32 API,理解下面内容也就不难了。
- #include"tetris.h"
-
- int main()
- {
- //修改当前地区为本地模式,为了⽀持中⽂宽字符的打印
- setlocale(LC_ALL, "");
- srand((unsigned int)time(NULL));
- char ch=0;
- do
- {
- GameInit();
- GameRun();
- GameOver();
- system("cls");
- SetPos(20, 12);
- printf("再来一局吗?(Y/N):\n");
- SetPos(22, 15);
- ch = getchar();
-
- getchar();//清理\n
- } while (ch == 'y' || ch == 'Y');
- SetPos(0, 29);
-
- return 0;
- }
- //设置控制台窗⼝的⼤⼩,30⾏,30列
- system("mode con cols=60 lines=30");
- //设置cmd窗⼝名称
- system("title 俄罗斯方块");
- //获取标准输出的句柄(⽤来标识不同设备的数值)
- HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- //影藏光标操作
- CONSOLE_CURSOR_INFO CursorInfo;
- GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
- CursorInfo.bVisible = false; //隐藏控制台光标
- SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
- void WelcomeToGame()
- {
- SetPos(20, 10);
- printf("欢迎来到俄罗斯方块!");
- SetPos(20, 23);//让按任意键继续的出现的位置好看点
- system("pause");
- system("cls");
- SetPos(14, 10);
- printf("↑为变换.↓为加速.←为左移.→为右移\n");
-
- SetPos(20, 23);//让按任意键继续的出现的位置好看点
- system("pause");
- system("cls");
- }
- void CreateMap()
- {
- memset(IF.pos, 0, sizeof(int)*30*60);//对数组初始化(如果不初始化,FAIL后第二次循环会IF.pos会保留上次数据)
- int i = 0;
- //墙体轮廓
- SetPos(40, 0);
- for (i = 40; i < 60; i += 2)
- {
-
- wprintf(L"%lc", WALL);
- }
- SetPos(0,29);
- for (i = 0; i < 60; i += 2)
- {
- IF.pos[i][29] = 1;
- wprintf(L"%lc", WALL);
- }
- //x是0,y从1开始增⻓
- for (i = 0; i < 30; i++)
- {
- IF.pos[0][i] = 1;//标记此处有方块
- SetPos(0, i);
- wprintf(L"%c", WALL);
- }
-
- for (i = 0; i < 30; i++)
- {
- IF.pos[38][i] = 1;
- SetPos(38, i);
- wprintf(L"%c", WALL);
- }
- for (i = 0; i < 30; i++)
- {
- IF.pos[58][i] = 1;
- SetPos(58, i);
- wprintf(L"%c", WALL);
- }
- SetPos(40, 10);
- for (i = 40; i < 60; i += 2)
- {
- IF.pos[i][10] = 1;
- wprintf(L"%lc", WALL);
- }
- //提示字体
- SetPos(40, 1);
- printf("下一个方块:");
-
- SetPos(44, 12);
- printf("左移:←");
-
- SetPos(44, 14);
- printf("右移:→");
-
- SetPos(44, 16);
- printf("加速:↓");
-
- SetPos(44, 18);
- printf("变换:↑");
-
- SetPos(44, 20);
- printf("暂停: 空格");
-
- SetPos(44, 22);
- printf("退出: Esc");
-
- SetPos(44, 24);
- printf("重新开始:R");
-
-
- SetPos(44, 26);
- printf("当前分数:%d", grade);
-
-
-
- }
注解:
1)一个宽字符占2个字节,我们设置 cols=60 lines=30,实际就是长宽各30个字节大小
2)如上图所示,游戏展示区长为20,高为30,右上部分为下一个方块提示区域,正下面为游戏操作提示区
3)横轴为x轴,向右坐标逐渐增大,向下为y轴,坐标逐渐增大。(x,y)确定宽字符(小方块)的位置,因为宽字符(小方块)占2个字节宽度,所以x轴坐标没有奇数值
4)为了方便检测下落方块是否靠墙、落地及碰到其它方块,我们定义一个大数组pos[60][30],一一对应地图上每个宽字符(小方块)的位置,用大数组pos[60][30]记录墙体位置及落地的方块位置
typedef struct InterFace { int pos[60][30]; //用于标记整个界面有方块的位置(1为有,0为无) }InterFace; InterFace IF;
- void InitBlockInfo()
- {
- //'T'字型
- int i = 0;
- for (i = 0; i < 3; i++)
- {
- block[0][0].space[1][i] = 1;
- }
- block[0][0].space[2][1] = 1;
-
- //‘L'字型
- for (i = 1; i < 4; i++)
- {
- block[1][0].space[i][1] = 1;
- }
- block[1][0].space[3][2] = 1;
-
- //'J'字形
- for (i = 1; i < 4; i++)
- {
- block[2][0].space[i][2] = 1;
- }
- block[2][0].space[3][1] = 1;
-
- //’Z'字型
- for (i = 0; i < 2; i++)
- {
- block[3][0].space[1][i] = 1;
- }
- for (i = 1; i < 3; i++)
- {
- block[3][0].space[2][i] = 1;
- }
-
- //‘S'字型
- for (i = 1; i < 3; i++)
- {
- block[4][0].space[1][i] = 1;
- }
- for (i = 0; i < 2; i++)
- {
- block[4][0].space[2][i] = 1;
- }
-
- //'田’字型
- int j = 0;
- for (i = 1; i < 3; i++)
- {
- for (j = 1; j < 3; j++)
- {
- block[5][0].space[i][j] = 1;
- }
- }
-
- //长条型
- for (i = 0; i < 4; i++)
- {
- block[6][0].space[i][1] = 1;
- }
-
- int m = 0;
- int n = 0;
- int tmp[4][4];
- for (i = 0; i < 7; i++)
- {
- block[i][0].speed = 200;//初始化下落速度
- block[i][0].blockstate = NORMAL;
- }
- for (i = 0; i < LINES; i++)
- {
- for (j = 0; j < COLS-1; j++)
- {
- for (m = 0; m < 4; m++)
- {
- for (n = 0; n < 4; n++)
- {
- tmp[m][n] = block[i][j].space[m][n];
- }
- }
- for (m = 0; m < 4; m++)
- {
- for (n = 0; n < 4; n++)
- {
- block[i][j + 1].space[n][3-m] = tmp[m][n];
- }
- }
- block[i][j + 1].speed = 200;//初始化下落速度
- block[i][j + 1].blockstate =NORMAL;//初始化方块状态
- }
- }
-
- }
注解:
1)我们定义结构体二位数组,用于存储7种基本形状方块的各自的4种形态的信息,共28种。
- #define COLS 4
- #define LINES 7
- enum BlockState{CHANGE,LEFT,RIGHT,DOWN,NORMAL};
- typedef struct Block
- {
- int space[4][4];
- int speed;
- enum BlockState blockstate;
- }Block;
- Block block[LINES][COLS] ;//结构体二位数组,用于存储7种基本形状方块的各自的4种形态的信息,共28种
2)首先初始化7种基本形状方块,然后基本形状方块通过顺时针旋转依次得到各自剩下的三种形态。
- void GameRun()
- {
-
- int i = rand() % 7, j = rand() % 4; //随机获取方块的形状
- do
- {
- int x = 18;
- int y = 0;
- int next_i = rand() % 7, next_j = rand() % 4;
- PrintBlock(next_i, next_j, 46, 4);//绘制提示框图形
- PrintBlock(i, j, 18, 0);//绘制方块初始位置
-
- while(IsLegal(i, j, x, y ))//不合法,说明到底了
- {
-
- if (KEY_PRESS(VK_UP))
- {
- block[i][j].blockstate = CHANGE;
- }
- else if (KEY_PRESS(VK_DOWN))
- {
-
- block[i][j].blockstate = DOWN;
- }
- else if (KEY_PRESS(VK_LEFT))
- {
- block[i][j].blockstate = LEFT;//左移
- }
- else if (KEY_PRESS(VK_RIGHT))
- {
- block[i][j].blockstate = RIGHT;//右移
- }
- else if (KEY_PRESS(VK_SPACE))
- {
- pause();//暂停
- }
- else if (KEY_PRESS(VK_ESCAPE))
- {
- state = END;//退出
- break;
- }
- else if (KEY_PRESS(R))
- {
- state = RESTART;//重新开始
- break;
- }
- else//什么都没按
- {
- block[i][j].blockstate = NORMAL;
- }
- BlockMove(block, &i, &j, &x,&y);
- PrintBlock(i, j, x, y);//绘制方块位置
- Sleep(block[i][j].speed);//每一帧休息的时间
- }
- i = next_i;
- j = next_j;
- PrintSpace(i,j, 46, 4);//覆盖提示框图形
- } while (state ==RUN);
- }
- //碰撞检测
- int IsLegal(int i, int j, int x, int y)
- {
- for (int m = 0; m < 4; m++)
- {
- for (int n = 0; n< 4; n++)
- {
- //如果方块落下的位置本来就已经有方块了,则不合法
- if ((block[i][j].space[m][n] == 1) && (IF.pos[x + n * 2][y + m] == 1))
- return 0; //不合法
- }
- }
- return 1; //合法
- }
- void PrintSpace(int i, int j, int x, int y)//覆盖指定位置方块
- {
- for (int m = 0; m< 4; m++)
- {
- for (int n = 0; n < 4; n++)
- {
- if (block[i][j].space[m][n] == 1)
- {
- SetPos(x+2*n,y+m); //光标跳转到指定位置
- printf(" "); //打印空格覆盖(两个空格)
- }
- }
- }
-
- }
- void PrintBlock(int i, int j, int x, int y)
- {
- int m = 0;
- int n = 0;
- for (m = 0; m < 4; m++)
- {
- for (n = 0; n < 4; n++)
- {
- if (block[i][j].space[m][n] == 1)
- {
- //IF.pos[x+n*2][y+m] = 1;//记录该位置有方块
- SetPos(x + n * 2, y + m);
- wprintf(L"%c", BIOCK);
- }
- }
- }
- }
- int JudeIsErase()
- {
- int sign = 0;
- int i = 0;
-
- for (i = 28; i > 3; i--)
- {
- int count = 0;
- for (int j = 2; j < 38; j += 2)
- {
- if (IF.pos[j][i] == 1)
- {
- count++;
- }
- }
- if (count == 18)//整行全是方块
- {
- sign = 1;
- grade += 10;
- SetPos(44, 26);
- printf("当前分数:%d", grade);//更新当前分数
- for (int a = 2; a < 38; a += 2)
- {
- SetPos(a, i);
- printf(" ");
- IF.pos[a][i] = 0;
- }
-
- //将该行以上全部下移一格
-
- for (int m = i; m > 3; m--)
- {
-
- for (int n = 2; n < 38; n += 2)
- {
-
- IF.pos[n][m] = IF.pos[n][m - 1]; //将上一行方块移到下一行
-
- if (IF.pos[n][m]==1) //上一行移下来的是方块,打印方块
- {
- SetPos(n, m); //光标跳转到该位置
-
- wprintf(L"%c", BIOCK); //打印方块
- }
- else //上一行移下来的是空格,打印空格
- {
- SetPos(n, m); //光标跳转到该位置
- printf(" "); //打印空格(两个空格)
- }
- }
-
-
- }
-
- }
- }
- return sign;
-
- }
注:
1)用大数组pos[60][30],从下往上判断是否满行,如果满行,则将将该行以上全部下移一格,这样就达到消行的目的
2)若第十四和第十三行都满行了,当检测到第十四行满行,将第十三行及以上全部下移一格,再次循环,从现十三行(原十二行)开始检测,此时会漏掉现十四行(原十三行),所以引进标识变量sign,只要有消行,则变为1,当sign=0时,表示全部方块没有满行了。
- void JudeIsOver()
- {
- for (int i = 2; i < 38; i+=2)
- {
- if (IF.pos[i][1] == 1) //第一行有方块存在
- {
- state = FAIL;
-
-
- }
- }
-
- }
- void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y)
- {
- switch (block[*i][*j].blockstate)
- {
- case CHANGE:
- if (IsLegal(*i, ((*j) + 1) % 4, *x, (*y )+1) == 1) //判断方块变化后(变化的同时下降一格)是否碰撞
- {
- //方块旋转后合法才进行以下操作
- PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置
- (*j) = ((*j) + 1) % 4;
- (*y)++;
-
-
- //PrintBlock(i, j, x, y);//绘制下一个变化的方块
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed = 200;
- }
- //else //方块再下落就碰撞了(已经到达底部)
- //{
- // //将当前方块的信息录入IF当中
- // for (int m = 0; m< 4; m++)
- // {
- // for (int n = 0; n< 4; n++)
- // {
- // if (block[*i][*j].space[m][n] == 1)
- // {
- // IF.pos[(*x )+n*2][(*y )+m] = 1; //将该位置标记为有方块
- //
- // }
- // }
- // }
- // while (JudeIsErase()); //判断此次方块下落是否得分
- // JudeIsOver();
-
- //}
- break;
- case LEFT:
- if (IsLegal(*i, *j,(*x )-2, *y ) == 1) //判断方块左移后是否合法
- {
- //方块左移后合法才进行以下操作
- PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置
-
- (*x) = (*x) - 2;
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed = 200;
-
- //PrintBlock(i, j, x, y);//绘制下一个变化的方块
-
- }
- break;
- case RIGHT:
- if (IsLegal(*i, *j, (*x) + 2, *y) == 1) //判断方块右移后是否合法
- {
- //方块右移后合法才进行以下操作
- PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置
- (*x) = (*x) + 2;
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed = 200;
- //PrintBlock(i, j, x, y);//绘制下一个变化的方块
-
- }
- break;
- case DOWN:
- if (IsLegal(*i, *j,* x , (*y) + 1) == 1) //判断方块下移后是否合法
- {
- //方块下移后合法才进行以下操作
- PrintSpace(*i,* j, *x,* y); //用空格覆盖当前方块所在位置
- (*y)++;
- block[*i][*j].blockstate = NORMAL;
- if (block[*i][*j].speed>50)//避免连续按,变成负数,会出现bug
- {
- block[*i][*j].speed -= 50;
- }
-
-
- }
- else //碰撞
- {
- //将当前方块的信息录入IF当中
-
- for (int m = 0; m < 4; m++)
- {
- for (int n = 0; n < 4; n++)
- {
- if (block[*i][*j].space[m][n] == 1)
- {
- IF.pos[(*x) + n * 2][(*y) + m] = 1; //将该位置标记为有方块
-
- }
- }
- }
- while (JudeIsErase()); //判断此次方块下落是否得分
- JudeIsOver();
- }
- break;
- case NORMAL:
- if (IsLegal(*i, *j,*x, (*y) + 1) == 1) //方块再下落合法
- {
-
- //方块右移后合法才进行以下操作
- PrintSpace(*i, *j, *x,* y); //用空格覆盖当前方块所在位置
-
- (*y)++;
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed=200;
-
- }
- else //碰撞
- {
- //将当前方块的信息录入IF当中
-
- for (int m = 0; m < 4; m++)
- {
- for (int n = 0; n < 4; n++)
- {
- if (block[*i][*j].space[m][n] == 1)
- {
- IF.pos[(*x) + n * 2][(*y) + m] = 1; //将该位置标记为有方块
-
- }
- }
- }
- while (JudeIsErase()); //判断此次方块下落是否得分
- JudeIsOver();
- }
- break;
- }
-
- }
注:
1)这里我们根据方块状态判断方块的下一步位置和形状,需要改变参数,所以传的是地址
2)只有在正常下落和加速下落才需要描述不合法时(碰撞时)状况,因为此时已经到底了。变换方块时不需要描述不合法(碰撞时)状况(因为'L'型,在变换时容易与边界发生碰撞,若此时将方块位置记录在pos[60][30]的大数组中,导致跳出该循环,进行下一个方块判断,导致该方块悬浮在半空的BUG)
- enum GameState{ RUN = 1,END,FAIL,RESTART };
- enum GameState state;
-
- void GameOver()
- {
-
- switch (state)
- {
- case END:
-
- SetPos(14, 15);
- printf("您主动退出游戏\n");
- SetPos(14, 18);
- system("pause");
- break;
- case FAIL:
- Sleep(1000); //留给玩家反应时间
- system("cls"); //清空屏幕
- SetPos(20, 10);
- printf("小垃圾,游戏结束!\n");
- SetPos(20, 14);
- printf("你的最终得分为: %d ", grade);
- SetPos(20, 18);
- system("pause");
- break;
- case RESTART:
- system("cls"); //清空屏幕
- SetPos(26, 10);
- printf("重新开始!\n");
- SetPos(22, 14);
- printf("小垃圾,你准备好了吗?\n");
- Sleep(2000);
- main();
- break;
- }
- }
注:
1)对游戏的三个状态分别进行描述。
- #include<Windows.h>
- #include <locale.h>
- #include<stdbool.h>
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
- #include<windows.h>
- #define WALL L'□'
- #define BIOCK L'■'
- #define COLS 4
- #define LINES 7
- #define R 0x52
- #define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
- enum GameState{ RUN = 1,END,FAIL,RESTART };
- enum GameState state;
-
- enum BlockState{CHANGE,LEFT,RIGHT,DOWN,NORMAL};
- int grade;
- typedef struct InterFace
- {
- int pos[60][30]; //用于标记整个界面有方块的位置(1为有,0为无)
-
- }InterFace;
-
- InterFace IF;
- typedef struct Block
- {
- int space[4][4];
- int speed;
- enum BlockState blockstate;
- }Block;
- Block block[LINES][COLS] ;//结构体二位数组,用于存储7种基本形状方块的各自的4种形态的信息,共28种
- void GameInit();
- void CreateMap();
- void InitBlockInfo();
- void GameRun();
- void PrintBlock(int i,int j,int x,int y);//ij 为方块形状,x,y为打印坐标
- void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y);
- void PrintSpace(int i, int j, int x, int y);
- int JudeIsErase();
- void JudeIsOver();
- void GameOver();
- #include"tetris.h"
-
- //设置光标的坐标
- void SetPos(short x, short y)
- {
- COORD pos = { x, y };
- HANDLE hOutput = NULL;
- //获取标准输出的句柄(⽤来标识不同设备的数值)
- hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- //设置标准输出上光标的位置为pos
- SetConsoleCursorPosition(hOutput, pos);
-
- }
- void WelcomeToGame()
- {
- SetPos(20, 10);
- printf("欢迎来到俄罗斯方块!");
- SetPos(20, 23);//让按任意键继续的出现的位置好看点
- system("pause");
- system("cls");
- SetPos(14, 10);
- printf("↑为变换.↓为加速.←为左移.→为右移\n");
-
- SetPos(20, 23);//让按任意键继续的出现的位置好看点
- system("pause");
- system("cls");
- }
- void CreateMap()
- {
- memset(IF.pos, 0, sizeof(int)*30*60);//对数组初始化(如果不初始化,FAIL后第二次循环会IF.pos会保留上次数据)
- int i = 0;
- //墙体轮廓
- SetPos(40, 0);
- for (i = 40; i < 60; i += 2)
- {
-
- wprintf(L"%lc", WALL);
- }
- SetPos(0,29);
- for (i = 0; i < 60; i += 2)
- {
- IF.pos[i][29] = 1;
- wprintf(L"%lc", WALL);
- }
- //x是0,y从1开始增⻓
- for (i = 0; i < 30; i++)
- {
- IF.pos[0][i] = 1;//标记此处有方块
- SetPos(0, i);
- wprintf(L"%c", WALL);
- }
-
- for (i = 0; i < 30; i++)
- {
- IF.pos[38][i] = 1;
- SetPos(38, i);
- wprintf(L"%c", WALL);
- }
- for (i = 0; i < 30; i++)
- {
- IF.pos[58][i] = 1;
- SetPos(58, i);
- wprintf(L"%c", WALL);
- }
- SetPos(40, 10);
- for (i = 40; i < 60; i += 2)
- {
- IF.pos[i][10] = 1;
- wprintf(L"%lc", WALL);
- }
- //提示字体
- SetPos(40, 1);
- printf("下一个方块:");
-
- SetPos(44, 12);
- printf("左移:←");
-
- SetPos(44, 14);
- printf("右移:→");
-
- SetPos(44, 16);
- printf("加速:↓");
-
- SetPos(44, 18);
- printf("变换:↑");
-
- SetPos(44, 20);
- printf("暂停: 空格");
-
- SetPos(44, 22);
- printf("退出: Esc");
-
- SetPos(44, 24);
- printf("重新开始:R");
-
- /*SetPos(44, 26);
- printf("最高纪录:%d", max);*/
-
- SetPos(44, 26);
- printf("当前分数:%d", grade);
-
- /*getchar();*/
-
- }
- void InitBlockInfo()
- {
- //'T'字型
- int i = 0;
- for (i = 0; i < 3; i++)
- {
- block[0][0].space[1][i] = 1;
- }
- block[0][0].space[2][1] = 1;
-
- //‘L'字型
- for (i = 1; i < 4; i++)
- {
- block[1][0].space[i][1] = 1;
- }
- block[1][0].space[3][2] = 1;
-
- //'J'字形
- for (i = 1; i < 4; i++)
- {
- block[2][0].space[i][2] = 1;
- }
- block[2][0].space[3][1] = 1;
-
- //’Z'字型
- for (i = 0; i < 2; i++)
- {
- block[3][0].space[1][i] = 1;
- }
- for (i = 1; i < 3; i++)
- {
- block[3][0].space[2][i] = 1;
- }
-
- //‘S'字型
- for (i = 1; i < 3; i++)
- {
- block[4][0].space[1][i] = 1;
- }
- for (i = 0; i < 2; i++)
- {
- block[4][0].space[2][i] = 1;
- }
-
- //'田’字型
- int j = 0;
- for (i = 1; i < 3; i++)
- {
- for (j = 1; j < 3; j++)
- {
- block[5][0].space[i][j] = 1;
- }
- }
-
- //长条型
- for (i = 0; i < 4; i++)
- {
- block[6][0].space[i][1] = 1;
- }
-
- int m = 0;
- int n = 0;
- int tmp[4][4];
- for (i = 0; i < 7; i++)
- {
- block[i][0].speed = 200;//初始化下落速度
- block[i][0].blockstate = NORMAL;
- }
- for (i = 0; i < LINES; i++)
- {
- for (j = 0; j < COLS-1; j++)
- {
- for (m = 0; m < 4; m++)
- {
- for (n = 0; n < 4; n++)
- {
- tmp[m][n] = block[i][j].space[m][n];
- }
- }
- for (m = 0; m < 4; m++)
- {
- for (n = 0; n < 4; n++)
- {
- block[i][j + 1].space[n][3 - m] = tmp[m][n];
- }
- }
- block[i][j + 1].speed = 200;//初始化下落速度
- block[i][j + 1].blockstate =NORMAL;//初始化方块状态
- }
- }
-
- }
-
- void GameInit()
- {
- //设置控制台窗⼝的⼤⼩,30⾏,30列
- system("mode con cols=60 lines=30");
- //设置cmd窗⼝名称
- system("title 俄罗斯方块");
- //获取标准输出的句柄(⽤来标识不同设备的数值)
- HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- //影藏光标操作
- CONSOLE_CURSOR_INFO CursorInfo;
- GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
- CursorInfo.bVisible = false; //隐藏控制台光标
- SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
- //打印欢迎界⾯
- WelcomeToGame();
- //初始化地图
- CreateMap();
- //初始化方块信息
- InitBlockInfo();
- //初始化游戏状态
- state = RUN;
- }
- void PrintBlock(int i, int j, int x, int y)
- {
- int m = 0;
- int n = 0;
- for (m = 0; m < 4; m++)
- {
- for (n = 0; n < 4; n++)
- {
- if (block[i][j].space[m][n] == 1)
- {
- //IF.pos[x+n*2][y+m] = 1;//记录该位置有方块
- SetPos(x + n * 2, y + m);
- wprintf(L"%c", BIOCK);
- }
- }
- }
- }
- void pause()//暂停
- {
- while (1)
- {
- Sleep(300);
- if (KEY_PRESS(VK_SPACE))
- {
- break;
- }
- }
- }
- //碰撞检测
- int IsLegal(int i, int j, int x, int y)
- {
- for (int m = 0; m < 4; m++)
- {
- for (int n = 0; n< 4; n++)
- {
- //如果方块落下的位置本来就已经有方块了,则不合法
- if ((block[i][j].space[m][n] == 1) && (IF.pos[x + n * 2][y + m] == 1))
- return 0; //不合法
- }
- }
- return 1; //合法
- }
- void PrintSpace(int i, int j, int x, int y)//覆盖指定位置方块
- {
- for (int m = 0; m< 4; m++)
- {
- for (int n = 0; n < 4; n++)
- {
- if (block[i][j].space[m][n] == 1)
- {
- SetPos(x+2*n,y+m); //光标跳转到指定位置
- printf(" "); //打印空格覆盖(两个空格)
- }
- }
- }
-
- }
- int JudeIsErase()
- {
- int sign = 0;
- int i = 0;
-
- for (i = 28; i > 3; i--)
- {
- int count = 0;
- for (int j = 2; j < 38; j += 2)
- {
- if (IF.pos[j][i] == 1)
- {
- count++;
- }
- }
- if (count == 18)//整行全是方块
- {
- sign = 1;
- grade += 10;
- SetPos(44, 26);
- printf("当前分数:%d", grade);//更新当前分数
- for (int a = 2; a < 38; a += 2)
- {
- SetPos(a, i);
- printf(" ");
- IF.pos[a][i] = 0;
- }
-
- //将该行以上全部下移一格
-
- for (int m = i; m > 3; m--)
- {
-
- for (int n = 2; n < 38; n += 2)
- {
-
- IF.pos[n][m] = IF.pos[n][m - 1]; //将上一行方块移到下一行
-
- if (IF.pos[n][m]==1) //上一行移下来的是方块,打印方块
- {
- SetPos(n, m); //光标跳转到该位置
-
- wprintf(L"%c", BIOCK); //打印方块
- }
- else //上一行移下来的是空格,打印空格
- {
- SetPos(n, m); //光标跳转到该位置
- printf(" "); //打印空格(两个空格)
- }
- }
-
-
- }
-
- }
- }
- return sign;
-
- }
- void JudeIsOver()
- {
- for (int i = 2; i < 38; i+=2)
- {
- if (IF.pos[i][1] == 1) //第一行有方块存在
- {
- state = FAIL;
-
-
- }
- }
-
- }
-
- void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y)
- {
- switch (block[*i][*j].blockstate)
- {
- case CHANGE:
- if (IsLegal(*i, ((*j) + 1) % 4, *x, (*y )+1) == 1) //判断方块变化后(变化的同时下降一格)是否碰撞
- {
- //方块旋转后合法才进行以下操作
- PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置
- (*j) = ((*j) + 1) % 4;
- (*y)++;
-
-
- //PrintBlock(i, j, x, y);//绘制下一个变化的方块
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed = 200;
- }
- //else //方块再下落就碰撞了(已经到达底部)
- //{
- // //将当前方块的信息录入IF当中
- // for (int m = 0; m< 4; m++)
- // {
- // for (int n = 0; n< 4; n++)
- // {
- // if (block[*i][*j].space[m][n] == 1)
- // {
- // IF.pos[(*x )+n*2][(*y )+m] = 1; //将该位置标记为有方块
- //
- // }
- // }
- // }
- // while (JudeIsErase()); //判断此次方块下落是否得分
- // JudeIsOver();
-
- //}
- break;
- case LEFT:
- if (IsLegal(*i, *j,(*x )-2, *y ) == 1) //判断方块左移后是否合法
- {
- //方块左移后合法才进行以下操作
- PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置
-
- (*x) = (*x) - 2;
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed = 200;
-
- //PrintBlock(i, j, x, y);//绘制下一个变化的方块
-
- }
- break;
- case RIGHT:
- if (IsLegal(*i, *j, (*x) + 2, *y) == 1) //判断方块右移后是否合法
- {
- //方块右移后合法才进行以下操作
- PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置
- (*x) = (*x) + 2;
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed = 200;
- //PrintBlock(i, j, x, y);//绘制下一个变化的方块
-
- }
- break;
- case DOWN:
- if (IsLegal(*i, *j,* x , (*y) + 1) == 1) //判断方块下移后是否合法
- {
- //方块下移后合法才进行以下操作
- PrintSpace(*i,* j, *x,* y); //用空格覆盖当前方块所在位置
- (*y)++;
- block[*i][*j].blockstate = NORMAL;
- if (block[*i][*j].speed>50)//避免连续按,变成负数,会出现bug
- {
- block[*i][*j].speed -= 50;
- }
-
-
- }
- else //碰撞
- {
- //将当前方块的信息录入IF当中
-
- for (int m = 0; m < 4; m++)
- {
- for (int n = 0; n < 4; n++)
- {
- if (block[*i][*j].space[m][n] == 1)
- {
- IF.pos[(*x) + n * 2][(*y) + m] = 1; //将该位置标记为有方块
-
- }
- }
- }
- while (JudeIsErase()); //判断此次方块下落是否得分
- JudeIsOver();
- }
- break;
- case NORMAL:
- if (IsLegal(*i, *j,*x, (*y) + 1) == 1) //方块再下落合法
- {
-
- //方块右移后合法才进行以下操作
- PrintSpace(*i, *j, *x,* y); //用空格覆盖当前方块所在位置
-
- (*y)++;
- block[*i][*j].blockstate = NORMAL;
- block[*i][*j].speed=200;
-
- }
- else //碰撞
- {
- //将当前方块的信息录入IF当中
-
- for (int m = 0; m < 4; m++)
- {
- for (int n = 0; n < 4; n++)
- {
- if (block[*i][*j].space[m][n] == 1)
- {
- IF.pos[(*x) + n * 2][(*y) + m] = 1; //将该位置标记为有方块
-
- }
- }
- }
- while (JudeIsErase()); //判断此次方块下落是否得分
- JudeIsOver();
- }
- break;
- }
-
- }
-
- void GameRun()
- {
-
- int i = rand() % 7, j = rand() % 4; //随机获取方块的形状
- do
- {
- int x = 18;
- int y = 0;
- int next_i = rand() % 7, next_j = rand() % 4;
- PrintBlock(next_i, next_j, 46, 4);//绘制提示框图形
- PrintBlock(i, j, 18, 0);//绘制方块初始位置
-
- while(IsLegal(i, j, x, y ))//不合法,说明到底了
- {
-
- if (KEY_PRESS(VK_UP))
- {
- block[i][j].blockstate = CHANGE;
- }
- else if (KEY_PRESS(VK_DOWN))
- {
-
- block[i][j].blockstate = DOWN;
- }
- else if (KEY_PRESS(VK_LEFT))
- {
- block[i][j].blockstate = LEFT;//左移
- }
- else if (KEY_PRESS(VK_RIGHT))
- {
- block[i][j].blockstate = RIGHT;//右移
- }
- else if (KEY_PRESS(VK_SPACE))
- {
- pause();//暂停
- }
- else if (KEY_PRESS(VK_ESCAPE))
- {
- state = END;//退出
- break;
- }
- else if (KEY_PRESS(R))
- {
- state = RESTART;//重新开始
- break;
- }
- else//什么都没按
- {
- block[i][j].blockstate = NORMAL;
- }
- BlockMove(block, &i, &j, &x,&y);
- PrintBlock(i, j, x, y);//绘制方块位置
- Sleep(block[i][j].speed);//每一帧休息的时间
- }
- i = next_i;
- j = next_j;
- PrintSpace(i,j, 46, 4);//覆盖提示框图形
- } while (state ==RUN);
- }
- void GameOver()
- {
-
- switch (state)
- {
- case END:
-
- SetPos(14, 15);
- printf("您主动退出游戏\n");
- SetPos(14, 18);
- system("pause");
- break;
- case FAIL:
- Sleep(1000); //留给玩家反应时间
- system("cls"); //清空屏幕
- SetPos(20, 10);
- printf("小垃圾,游戏结束!\n");
- SetPos(20, 14);
- printf("你的最终得分为: %d ", grade);
- SetPos(20, 18);
- system("pause");
- break;
- case RESTART:
- system("cls"); //清空屏幕
- SetPos(26, 10);
- printf("重新开始!\n");
- SetPos(22, 14);
- printf("小垃圾,你准备好了吗?\n");
- Sleep(2000);
- main();
- break;
- }
- }
- #include"tetris.h"
-
- int main()
- {
- //修改当前地区为本地模式,为了⽀持中⽂宽字符的打印
- setlocale(LC_ALL, "");
- srand((unsigned int)time(NULL));
- char ch=0;
- do
- {
- GameInit();
- GameRun();
- GameOver();
- system("cls");
- SetPos(20, 12);
- printf("再来一局吗?(Y/N):\n");
- SetPos(22, 15);
- ch = getchar();
-
- getchar();//清理\n
- } while (ch == 'y' || ch == 'Y');
- SetPos(0, 29);
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。