俄罗斯方块,C语言源文件,带有详细的注释

13 篇文章 0 订阅
9 篇文章 0 订阅

为了方便在其它程序中测试,将方块的功能包装成了一个类,下面是头文件Tetris.h的内容 

欢迎测试并提出宝贵意见,其中有个bug不知到怎么回事,有时落下的方块没显示

//俄罗斯方块C++语言源代码,vs2019编译通过
#pragma once
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <string.h>
#include <math.h>
//游戏区大小
#define ROW 29 //游戏区行数,建议不小于29,否则乱屏
#define COL 20 //游戏区列数
//键码
#define SPACE 32 //空格键
#define ESC 27 //Esc键
//四个方向键控制方块姿态,方便习惯右手操作的用户
#define LEFT 75
#define RIGHT 77
#define DOWN 80
#define UP 72 //用于旋转方块
//其它
#define LenofName 11
struct Face
{
	int data[ROW][COL + 20] = { 0 }; //用于标记指定位置是否有方块(1为有,0为无)
	int color[ROW][COL + 20] = { 0 }; //用于记录指定位置的方块颜色编码
};

struct Block//用于存储7种基本形状方块的各自的4种形态的信息,共28种
{
	int space[4][4] = { 0 };
}; 
//游戏得分数据
typedef struct Data {
	char user[LenofName];
	int hisScore;
	int hisGrade;//写完了发现这个数没意义,因为可以通过hisScore计算得到
	int max;
	Face history;
}DATA;
class Tetris {
private:
	int lines;//记录消去的行数,控制得分权值
	int disScore;//屏显分数
	int forceGrade;//强制等级
	int grade;//等级
	int difficulty;//下落刷新时间,单位ms
	Face face;
	Block block[7][4];
	DATA data;
	//设置方块大小
	void SetFont(int size = 20);
	//重新开始的清理工作
	void Restart();
	//配置运行环境
	void ConfigEnvironment();
	//光标跳转
	void CursorJump(int x, int y);
	//初始化界面
	void InitInterface();
	//初始化方块信息
	void InitBlockInfo();
	//颜色设置
	void color(int num);
	//画出方块
	void DrawBlock(int shape, int form, int x, int y);
	//空格覆盖
	void DrawSpace(int shape, int form, int x, int y);
	//合法性判断
	int IsLegal(int shape, int form, int x, int y);
	//判断得分与结束
	int JudeFunc();
	//游戏记录
	DATA ReadGrade();
	//更新最高分到文件
	int WriteGrade(DATA);
	//根据得分调整下落速度
	int AdjustDifficulty();
	//刷新游戏灯牌
	void Display();
public:
	Tetris();
	int StartGame();
};

类中函数的实现部分,文件名Tetris.app

#include "Tetris.h"

#pragma warning (disable:4996) //消除警告,兼容旧版本库文件

 Tetris::Tetris() {//通过构造函数完成初始化
		lines = 1;
		disScore =0;
		forceGrade = 0;
		data = ReadGrade();
		grade = data.hisGrade;//延续上次的等级
		difficulty = AdjustDifficulty();//重新计算下落时间
		ConfigEnvironment();
		SetFont();
		InitInterface();
		InitBlockInfo();
 }
 //配置运行窗口
 void Tetris:: ConfigEnvironment() {
	 system("chcp 936");//设置utf-8字符集65001,GBK字符集936,如果乱码才需要更改
	 //设置标题
	 char cmd[60];
	 sprintf(cmd, "title 俄罗斯方块:%s", data.user);//含变量的DOS命令,序列化到字符中再传递
	 system(cmd);
	//设置cmd窗口的大小
	 sprintf(cmd,"mode con lines=%d cols=%d" ,ROW, 2*COL+40);//改变ROW,COL时窗口自动调整大小
	 system(cmd);
	//设置随机数起点,每次启动,游戏会有不同的开局
	 srand((unsigned int)time(NULL));
	 //隐藏光标
	 CONSOLE_CURSOR_INFO curInfo; //光标结构体变量
	 curInfo.dwSize = 1;  //如果没赋值的话,隐藏光标无效
	 curInfo.bVisible = FALSE; //可见为TRUE
	 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出句柄	 
	 SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
	//游戏界面最大化会破坏布局,禁用最大化
	 HWND hwnd = GetConsoleWindow();
	 HMENU hmenu = GetSystemMenu(hwnd, false);		// 复制或修改而访问窗口菜单
	 RemoveMenu(hmenu, SC_MAXIMIZE, MF_BYCOMMAND);	// 从指定菜单删除一个菜单项或分离一个子菜单
	 DrawMenuBar(hwnd);//去除最大化菜单
 }
 //设置字体,一是美观,二是控制方块大小,太小的方块玩起来太累
 //虽然可以游戏启动后手动调节字体,但是为了具有良好游戏体验,直接设好吧
 void Tetris::SetFont(int size) {//默认大小为20号字,传入参数可以改变字体及方块大小
	 CONSOLE_FONT_INFOEX cfi;//控制台字体信息的结构体
	 cfi.cbSize = sizeof(CONSOLE_FONT_INFOEX);
	 cfi.nFont = 0;
	 cfi.dwFontSize.X = 0;
	 cfi.dwFontSize.Y = size;  //设置字体大小
	 cfi.FontFamily = FF_DONTCARE;
	 cfi.FontWeight = FW_NORMAL; //字体粗细 FW_BOLD
	 wcscpy_s(cfi.FaceName, L"隶书");  //设置字体,必须是控制台已有的
	 SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);//根据结构体内容改变字体
 }
 //重新开始
 void Tetris::Restart() {
	 //清空游戏区方块
	 for (int i = 0; i < ROW-1; i++) {
		 for (int j = 2; j < 2*COL - 2; j+=2) {
			 CursorJump(j, i);
			 printf("  ");//两个空格填充一个方块
		 }
	 }
	 //清除当前分数
	 disScore = 0;
	 forceGrade = 0;
	 grade = 0;//游戏速度降到最低,不再延续以前的速度继续
	 CursorJump(2 * COL + 22, 16);
	 printf("                ");//这里可能写了字,清除掉
	 Display();//刷新游戏灯牌
	 //清除已落下的方块
	 for (int i = 0; i < ROW-1; i++) {
		 for (int j = 1; j < COL - 1; j++) {
			 face.data[i][j] = 0;
			 face.color[i][j] = 0;
		}
	}
	 return;
 }
//光标跳转
void Tetris::CursorJump(int x, int y)
{
	COORD pos; //光标位置的结构体变量
	pos.X = x; //横坐标
	pos.Y = y; //纵坐标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
	SetConsoleCursorPosition(handle, pos); //设置光标位置
}
//刷新游戏灯牌
void Tetris:: Display() {
	color(7);//白色
	int g = grade < forceGrade ? forceGrade:grade ;//高等级优先
	CursorJump(2 * COL + 31, 6);//光标要跳到正确位置再修改相应内容
	printf("%d", g);
	CursorJump(2 * COL + 23, 8);
	int i=0;
	for (; i <= g; i++) {//显示一个看似酷炫的速度进度条
		color(0xae);//被景绿色,前景黄色
		printf(">");
	}
	for (; i <= 9; i++) {
		color(0x80);//灰色背景
		printf(" ");
	}
	color(7);//改回白色
	CursorJump(2 * COL + 31, 12);
	printf("%-5d", disScore);
	CursorJump(2 * COL + 31, 14);
	difficulty = AdjustDifficulty();
	printf("%-4dms", difficulty);
}
//初始化界面
void Tetris::InitInterface()
{
	color(0x38); //边框颜色
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL + 20; j++)
		{
			if (j == 0 || j == COL - 1 || j == COL + 9||j==COL+19)//竖线位置
			{
				face.data[i][j] = 1; //标记该位置有方块
				CursorJump(2 * j, i);
				printf("◆");
			}
			else if (i == ROW - 1)
			{
				face.data[i][j] = 1;
				printf("◆");
			}
			else
				face.data[i][j] = 0;
		}
	}
	for (int i = COL; i < COL + 10; i++)//左栏横线
	{
		face.data[8][i] = 1; 
		CursorJump(2 * i, 8);
		printf("◆");
	}
	for (int i = COL+10; i < COL + 20; i++)//右栏横线
	{
		face.data[18][i] = 1; 
		CursorJump(2 * i, 18);
		printf("◆");
	}
	color(7);//操作说明文字设置为白色
	CursorJump(2 * COL+4, 1);
	printf("下一个方块");
	CursorJump(2 * COL + 4, ROW - 19);
	printf("左移:A←");
	CursorJump(2 * COL + 4, ROW - 17);
	printf("右移:F→");
	CursorJump(2 * COL + 4, ROW - 15);
	printf("加速:D ↓");
	CursorJump(2 * COL + 4, ROW - 13);
	printf("旋转:空格↑");
	CursorJump(2 * COL + 4, ROW - 11);
	printf("暂停: S");
	CursorJump(2 * COL + 4, ROW - 9);
	printf("退出: esc");
	CursorJump(2 * COL + 4, ROW - 7);
	printf("重新开始:R");
	CursorJump(2 * COL + 2, ROW-5);
	printf("继续上次游戏:C");
	CursorJump(2 * COL + 2, ROW-3);
	printf("更换新用户名:U");
	//游戏灯牌
	CursorJump(2 * COL + 22, 2);
	printf("直接跳级提速");
	CursorJump(2 * COL + 22, 4);
	printf("请选择:0--9");
	CursorJump(2 * COL + 22, 6);
	printf("当前等级:%d",grade);
	CursorJump(2 * COL + 22, 10);
	printf("最高纪录:%-5d", data.max);//估计玩到大于10万分就觉得没意思了,所以5位数宽度就够了
	CursorJump(2 * COL + 22, 12);
	printf("当前分数:%-5d", disScore);
	CursorJump(2 * COL + 22, 14);
	difficulty = AdjustDifficulty();
	printf("下落速度:%-4dms", difficulty);
	Display();//画一下进度条
//打印游戏说明
	CursorJump(2 * COL + 21, 20);
	printf("将方块垒整齐得分");
	CursorJump(2 * COL + 21, 21);
	printf("连续消多行多得分");
	CursorJump(2 * COL + 21, 23);
	printf("中途保存可按esc");
	CursorJump(2 * COL + 21, 24);
	printf("游戏结束不会保存");
	CursorJump(2 * COL + 21, 25);
	printf("根据你的分数速度");
	CursorJump(2 * COL + 21, 26);
	printf("只能提速不能降速");
}
//初始化方块信息
void Tetris::InitBlockInfo()//7种方块28种形态预先存入数组,供显示使用
{
	//“T”形
	for (int i = 0; i <= 2; i++)
		block[0][0].space[1][i] = 1;
	block[0][0].space[2][1] = 1;

	//“L”形
	for (int i = 1; i <= 3; i++)
		block[1][0].space[i][1] = 1;
	block[1][0].space[3][2] = 1;

	//“J”形
	for (int i = 1; i <= 3; i++)
		block[2][0].space[i][2] = 1;
	block[2][0].space[3][1] = 1;

	for (int i = 0; i <= 1; i++)
	{
		//“Z”形
		block[3][0].space[1][i] = 1;
		block[3][0].space[2][i + 1] = 1;
		//“S”形
		block[4][0].space[1][i + 1] = 1;
		block[4][0].space[2][i] = 1;
		//“O”形
		block[5][0].space[1][i + 1] = 1;
		block[5][0].space[2][i + 1] = 1;
	}

	//“I”形
	for (int i = 0; i <= 3; i++)
		block[6][0].space[i][1] = 1;

	int temp[4][4];
	for (int shape = 0; shape < 7; shape++) //7种形状
	{
		for (int form = 0; form < 3; form++) //4种形态,已经有了一种,每个还需增加3种
		{
			//获取第form种形态
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					temp[i][j] = block[shape][form].space[i][j];
				}
			}
			//将第form种形态顺时针旋转,得到第form+1种形态
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					block[shape][form + 1].space[i][j] = temp[3 - j][i];
				}
			}
		}
	}
}
//颜色设置
void Tetris::color(int c)
{//c可以用两位十六进制数表示,高位为背景色,低位为前景色,也可以写转换后的10进制数
	switch (c)
	{
	case 0:
		c = 13; //“T”形方块设置为紫色
		break;
	case 1:
	case 2:
		c = 12; //“L”形和“J”形方块设置为红色
		break;
	case 3:
	case 4:
		c = 10; //“Z”形和“S”形方块设置为绿色
		break;
	case 5:
		c = 14; //“O”形方块设置为黄色
		break;
	case 6:
		c = 11; //“I”形方块设置为浅蓝色
		break;
	case 7:
		c = 7; //文字设置为白色
		break;
	default:
		c=c;//默认根据色码设定
	}
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //调用API设置颜色
}
//画出方块
void Tetris::DrawBlock(int shape, int form, int x, int y)
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if (block[shape][form].space[i][j] == 1) //如果该位置有方块
			{
				CursorJump(2 * (x + j), y + i); //光标跳转到指定位置
				printf("■"); //输出方块
			}
		}
	}
}
//空格覆盖
void Tetris::DrawSpace(int shape, int form, int x, int y)
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if (block[shape][form].space[i][j] == 1) //如果该位置有方块
			{
				CursorJump(2 * (x + j), y + i); 
				printf("  "); //两个空格占用一个方块的位置
			}
		}
	}
}
//合法性判断
int Tetris::IsLegal(int shape, int form, int x, int y)
{
	for (int i = 3; i >=0; i--)//从方块下沿开始判断,可能少循环几次,提高一点点性能
	{
		for (int j = 0; j <4; j++)
		{
			//如果方块落下的位置本来就已经有方块了,则不合法
			if ((block[shape][form].space[i][j] == 1) && (face.data[y + i][x + j] == 1))
				return 0; //不合法
		}
	}
	return 1; //合法
}
//调整游戏难度
int Tetris::AdjustDifficulty() {
	int t;//这里t越小,方块下落越快,根据得分调整游戏难度
	int score;
	if (forceGrade > grade) {//有强制提速
		score = disScore % 1000 + forceGrade * 1000;
	}
	else {//根据等级设置速度,防止速度正反馈
		score = disScore % 1000 + grade * 1000;
	}
	if (score < 9000) {
		t = 1000 - sqrt(score)*9.4;//非线性调整
	}
	else {
		t = 100;//极限挑战,只加分不提速,如果感觉难度不算极限,可以改小一点
	}
	return t;
}
//从文件读取数据
DATA Tetris::ReadGrade() {
	DATA d;
	FILE* pf = fopen("record.dat", "r"); //以只读方式打开文件
	if (pf == NULL) //第一次运行无此文件
	{//创建一个具有初始数据的新文件
		strcpy(d.user, "古老的经典\0");
		d.hisScore = 0;
		d.hisGrade = 0;
		d.max = 0;
		for (int i = 0; i < ROW; i++)//初始化face再保存
		{
			for (int j = 0; j < COL + 20; j++)
			{
				if (j == 0 || j == COL - 1 || j == COL + 9 || j == COL + 19)
				{
					face.data[i][j] = 1; //标记该位置有方块					
				}
				else if (i == ROW - 1)
				{
					face.data[i][j] = 1;//最下面一行全是方块 
				}
				else
					face.data[i][j] = 0; //一个方块都没落下
			}
		}
		d.history = face;//防止第一次运行就试图继续上次游戏,会造成方块掉入无底深渊
		pf = fopen("record.dat", "w"); //自动创建该文件
		if (pf == NULL) {//创建文件失败
			CursorJump(COL, ROW / 2);
			printf("你的存储介质为只读类型,无法创建游戏数据文件");
			CursorJump(COL, ROW / 2+2);
			printf("按任意键继续进入游戏……");
			system("pause>nul");
		}
		else {
			fwrite(&d, sizeof(DATA), 1, pf);
		}
		
	}
	else {
		fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
		fread(&d, sizeof(DATA), 1, pf); //读取文件中的数据
		fclose(pf); //关闭文件
		pf = NULL; //防止内存泄露
	}
	return d;//读取失败游戏也可以玩,因为d被初始化了
}
//保存得分记录
int Tetris::WriteGrade(DATA d) {
	FILE* pf = fopen("record.dat", "w");
	if (pf == NULL) {
		CursorJump(COL, ROW / 2 + 4);
		printf("保存游戏数据失败,record.dat文件为只读或存储介质为只读");
		CursorJump(COL, ROW / 2 + 6);
		printf("按任意键直接退出……");
		system("pause>nul");
		return 1;
	}
	else
	{
		fwrite(&d, sizeof(DATA), 1, pf);
		fclose(pf); 
		pf = NULL; 
	}
	return 0;
}
//判断是否得分
int Tetris::JudeFunc()
{
	for (int i = ROW - 2; i > 4; i--)//最底层开始判断
	{
		int sum = 0; //记录第i行的方块个数
		for (int j = 1; j < COL - 1; j++)
		{
			sum += face.data[i][j]; //统计第i行的方块个数
		}
		if (sum == 0) {
			break;//最后一行没有方块,无需再判断其上的层次
		}
		else if (sum == COL - 2) //该行全是方块,可得分
		{
			disScore += 10 * lines * lines; //根据一次性得分行数加分
			for (int j = 1; j < COL - 1; j++) //清除整行的方块
			{
				face.data[i][j] = 0; //标记为无方块既被清除
			}
			//把被清除行上面的行整体向下挪一格
			for (int m = i; m > 1; m--)
			{
				sum = 0; //记录上一行的方块个数
				for (int n = 1; n < COL - 1; n++)
				{
					sum += face.data[m - 1][n]; //统计上一行的方块个数
					face.data[m][n] = face.data[m - 1][n]; //将上一行方块的标识移到下一行
					face.color[m][n] = face.color[m - 1][n]; //将上一行方块的颜色编号移到下一行
					if (face.data[m][n] == 1) //上一行移下来的是方块,打印方块
					{
						CursorJump(2 * n, m); //光标跳转到该位置
						color(face.color[m][n]); //颜色设置为原方块的颜色
						printf("■"); //打印方块
					}
					else //上一行移下来的是空格,打印空格
					{
						CursorJump(2 * n, m); //光标跳转到该位置
						printf("  "); //打印空格(两个空格)
					}
				}
				//上一行移下来的全是空格,无需再将上层的方块向下移动(移动结束)
				if (sum == 0)return 1;  //移动下来的可能还有满行,还需调用该函数进行判断	
			}
		}
	}
	lines = 1;//重置连续消掉的行数
	return 0; //返回值用于结束调用者的死循环
}
//游戏主体逻辑函数 
int Tetris::StartGame(){
	clock_t start, end=clock();//用于计时
	char ch;
	int gameover = 0;
	int nextShape,nextForm;
	int x, y;//方块坐标
	int shape = rand() % 7, form = rand() % 4; //随机获取方块的形状和形态
	while (1){//主循环,用于不断产生新的方块
		nextShape = rand() % 7, nextForm = rand() % 4; //随机获取下一个方块的形状和形态
		x = COL / 2 - 2,y = 0;//初始位置
		color(nextShape); //颜色设置为下一个方块的颜色
		DrawBlock(nextShape, nextForm, COL + 3, 3); //将下一个方块显示在右上角
		//下落循环,控制方块不断下落
		while (1){
			color(shape); //颜色设置为当前正在下落的方块
			DrawBlock(shape, form, x, y); //将该方块显示在初始下落位置
			start = clock();//计时开始
			while (kbhit()) {//下落过程中不断检测键盘是否有动作,用于控制方块
					ch = getch(); //读取keycode
					switch (ch)
					{
					case 'd': 
					case 'D':
					case DOWN://方向键:下
						if (IsLegal(shape, form, x, y + 1)==1) //判断方块向下移动一位后是否合法
						{
							DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
							y++; //下一次显示方块时就相当于下落了一格了
							DrawBlock(shape, form, x, y);//及时重画该方块
						}
						break;
					case 'a': //方便左手操作
					case 'A':
					case LEFT://方向键:左,方便右手操作
						if (IsLegal(shape, form, x - 1, y) == 1) 
						{
							DrawSpace(shape, form, x, y); 
							x--;
							DrawBlock(shape, form, x, y);
						}
						break;
					case 'f': 
					case 'F':
					case RIGHT://方向键:右
						if (IsLegal(shape, form, x + 1, y) == 1) 
						{
							DrawSpace(shape, form, x, y); 
							x++; 
							DrawBlock(shape, form, x, y);
						}
						break;
					case SPACE: //空格键
					case UP://方向,上
						if (IsLegal(shape, (form + 1) % 4, x, y) == 1)
						{
							DrawSpace(shape, form, x, y); 
							form = (form + 1) % 4;
							DrawBlock(shape, form, x, y);
						}
						break;
					case 's'://暂停
					case 'S':
						CursorJump(2 * COL + 4, ROW - 11);
						printf("%-10s", "任意键继续");//让用户知道自己暂停了
						system("pause>nul"); //按任意键继续
						color(7);
						CursorJump(2 * COL + 4, ROW - 11);
						printf("%-10s","暂停: S");//恢复运行后,将操作提示写回去
						break;
					case 'r'://重新开始
					case 'R':
						Restart();
						break;
					case ESC: //esc键
						system("cls"); //清空屏幕
						color(7);
						CursorJump(COL, ROW / 2);
						printf("确认要保存游戏并退出吗?(y/n):");
						CursorJump(COL, ROW / 2-4);
						printf("不想保存直接退出请再次按esc");
						while (1) {//必须做出正确选择
							ch = getch();
							if (ch == 'y' ||ch== 'Y') {//同意保存退出
								if (disScore > data.max) { data.max = disScore; }
								data.hisScore = disScore;
								data.hisGrade = grade;
								data.history = face;//保存现场
								WriteGrade(data);
								return 1;//结束所有循环,返回主程序
							}
							else if (ch == 'n' || ch == 'N') {
								system("cls");
								//保护现场
								Face tempFace=face;
								InitInterface();//重画界面会清除数据
								face = tempFace;
								//恢复现场
								for (int i = 1; i < ROW - 1; i++) {
									for (int j = 1; j < COL - 1; j++) {
										if (face.data[i][j] == 1) {
											CursorJump(2*j,i);
											color(face.color[i][j]); //颜色设置为原方块的颜色
											printf("■"); //打印方块
										}
									}
								}
								DrawBlock(nextShape, nextForm, COL + 3, 3);//画出下一个方块
								break;//结束读取循环
							}
							else if (ch == ESC) {
								return 1;//相当于双击esc直接退出游戏
							}
							else {
								Beep(500,500);//输入错误发出提示音
							}
						}
						break;
					case 'U'://改名操作
					case 'u': {
						CursorJump(2 * COL + 22, 16);
						printf("姓名:");
						CursorJump(2 * COL + 27, 16);
						color(8);
						printf("最多五个字");
						color(7);
						CursorJump(2 * COL + 27, 16);
						//显示光标
						CONSOLE_CURSOR_INFO curInfo; 
						curInfo.dwSize = 1;
						curInfo.bVisible = true;
						HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);  
						SetConsoleCursorInfo(handle, &curInfo);
						char cmd[600];//如果非要输入大于这个数的字符串也溢出
						scanf("%s", cmd);
						if (strlen(cmd)<LenofName) {//处理一下用户名溢出
							strcpy(data.user, cmd);
							sprintf(cmd, "title 俄罗斯方块当前用户:%s", data.user);
							system(cmd);//把你的名字写在标题栏,装酷
							WriteGrade(data);
						}
						else {
							CursorJump(13, ROW/2);
							printf("你的名字太长了");
						}
						//隐藏光标
						curInfo.bVisible = false;
						SetConsoleCursorInfo(handle, &curInfo);
						CursorJump(2 * COL + 22, 16);//回到起始位置,擦掉刚才写的字
						printf("               ");
						break;
					}
					case 'C'://继续上次游戏
					case 'c':
						Face temFace=face;
						system("cls"); //清空屏幕
						CursorJump(COL, ROW / 2);
						color(7);
						printf("确认要恢复上次游戏继续玩吗?(y/任意键继续当前游戏):");
						ch = getch();
						if (ch == 'y' || ch == 'Y') {
							system("cls");
							data=ReadGrade();
							disScore = data.hisScore;
							grade = data.hisGrade;
							forceGrade = 0;//恢复时没有强制提速
							InitInterface();
							face = data.history;
							difficulty = AdjustDifficulty();//恢复游戏速度
							Display();
						}
						else {//误操作时继续
							system("cls");
							InitInterface();
							face = temFace;
						}
							//恢复现场
							for (int i = 1; i < ROW - 1; i++) {
								for (int j = 1; j < COL - 1; j++) {
									if (face.data[i][j] == 1) {
										CursorJump(2 * j, i);
										color(face.color[i][j]); //颜色设置为原方块的颜色
										printf("■"); //打印方块
									}
								}
							}
						DrawBlock(nextShape, nextForm, COL + 3, 3);//画出下一个方块
						break;
					}
				//越级调整游戏难度,既方块下落速度
					if (ch >= '0' && ch <= '9') {
						int g=ch-'0';
						//起步速度为上次最高得分的速度,想降速,根据本局得分确定可以将到的最低速度
						if (g>=grade) {//速度选择正确
							forceGrade = g;
							difficulty = AdjustDifficulty();
							Display();
						}
						else{//给点提示,为什么不让降速
							CursorJump(2 * COL + 22, 16);
							printf("坚持!别走回头路");
							Beep(500, 500);
						}
					}
				}
			if (start-end>=difficulty) //下落时间到达
			{
				if (IsLegal(shape, form, x, y + 1)==1) //可以下落
				{
					DrawSpace(shape, form, x, y); 
					y++; 
					DrawBlock(shape, form, x, y);
				}
				else if(IsLegal(shape, form, x, y + 1) == 0)//不再下落
				{
					//face:记录界面的每个位置是否有方块,若有方块还需记录该位置方块的颜色。
					for (int i = 0; i < 4; i++)
					{
						for (int j = 0; j < 4; j++)
						{
							if (block[shape][form].space[i][j]==1)
							{
								face.data[y + i][x + j] = 1; //将该位置标记为有方块
								face.color[y + i][x + j] = shape; //记录该方块的颜色数值
							}
						}
					}
					//判断此次方块下落是否得分
					while (JudeFunc()) {//有得分情况下
						lines++;//有连续满行
						//根据分数调整等级
						if (disScore / 1000 > 9) {//防止得分过万显示大于9的等级
							grade = 9;
						}
						else if (disScore / 1000 < data.hisGrade) {//延续历史速度
							grade = data.hisGrade; 
						}
						else {
							grade = disScore / 1000;
						}
						difficulty = AdjustDifficulty();//重新计算下落时间
						Display();
						//擦掉试图降速时写的字
						CursorJump(2 * COL + 22, 16);
						//printf("坚持!别走回头路");
						printf("                ");
					}
					break; //跳出当前死循环,准备进行下一个方块的下落
				}
				end = start;//重新开始计时
			}
			Sleep(20);//休眠20ms,减少无用循环次数,降低CPU占用率,否则CPU30%消耗在这个死循环
		}
		//判断游戏是否结束
		for (int j = 1; j < COL - 1; j++)
		{
			if (face.data[1][j] == 1) {//最上面一行有方块就OVER
				gameover = 1;//用于后面处理GAME OVER事项
				break;
			}
		}
		//游戏结束放在循环外面,这样方便退出死循环
		if (gameover)
		{//弄点动静
			Beep(523, 400);//do  
			Beep(578, 400);//re  
			Beep(659, 400);//mi
			Sleep(1200);//等上面的doremi
			if (disScore / 1000 <= 9) { data.hisGrade = disScore / 1000; }//下次玩的初始速度
			else { data.hisGrade = 0; }//分数过万且创记录,相当于通过所有挑战而爆机,等级恢复最低
			system("cls"); 
			color(7); 
			CursorJump(2 * (COL / 3), ROW / 2 - 3);
			if (disScore > data.max)
			{
				printf("恭喜你创造出新的世界记录%d,留给后人慢慢挑战吧!", disScore);
				data.max = disScore;
			}
			else if (disScore == data.max)
			{
				printf("你已追平世界记录,离超越只差一点点");
			}
			else
			{
				printf("你与最高记录相差%d,请继续加油", data.max - disScore);
			}
			WriteGrade(data);//不管玩不玩,还是保存一下吧
			CursorJump(2 * (COL / 3), ROW / 2);
			printf("GAME OVER");
			CursorJump(2 * (COL / 3), ROW / 2 + 3);
			printf("再来一局?(y/n):");
			while (1)//要有明确选择才肯放过你
			{
				ch = getch();
				if (ch == 'y' || ch == 'Y')
				{
					gameover = 0;
					system("cls");
					InitInterface();
					Restart();
					break;//跳出本死循环,进入主死循环,就开始了新的一局
				}
				else if (ch == 'n' || ch == 'N')
				{
					return 1;//直接跳出死循环返回主程序
				}
				else//错误选择鸣笛
				{
					Beep(500, 500);
				}
			}
		}
		//准备下一个方块
		shape = nextShape, form = nextForm;
		DrawSpace(nextShape, nextForm, COL + 3, 3); //将右上角的方块信息用空格覆盖
	}
}

主程序,仅仅为了测试一下

#include "Tetris.h"
int main()
{
	Tetris t = Tetris();
	t.StartGame(); //开始游戏
	return 0;
}

错误之处敬请批评指正 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值