俄罗斯方块2.0版本

#include <stdio.h>
#include <graphics.h>
#include <time.h>
#include <conio.h>

int score = 0;//总分,设置为变量
int rank = 0; //等级

#define BLOCK_COUNT 5  //几种方块
#define BLOCK_WIDTH 5
#define BLOCK_HEIGHT 5
#define BLOCK_SIZE   20 

#define START_X 130 
#define START_Y 30

#define KEY_UP     72
#define KEY_RIGHT  77
#define KEY_DOWN   80
#define KEY_LEFT   75
#define KEY_SPACE  32

int speed = 500;
int minX = 30;
int minY = 30;

typedef enum{

	BLOCK_UP,
	BLOCK_RIGHT,
	BLOCK_DOWN,
	BLOCK_LEFT

}block_dir_t;

typedef enum {
	MOVE_DOWN,
	MOVE_LEFT,
	MOVE_RIGHT
}move_dir_t;

int NextIndex = -1;  //下一格方块的种类
int BlockIndex = -1;  //当前方块的种类

int color[BLOCK_COUNT] = {
	GREEN,CYAN,MAGENTA,BROWN,YELLOW
};

int visit[30][15];//访问数组
int pointcolor[30][15];//表示对应位置的颜色

int block[BLOCK_COUNT * 4][BLOCK_HEIGHT][BLOCK_WIDTH] = {
	
	//条形方块
	{
		0,0,0,0,0,
		0,0,1,0,0,
		0,0,1,0,0,
		0,0,1,0,0,
		0,0,0,0,0
	},
	{
		0,0,0,0,0,
		0,0,0,0,0,
		0,1,1,1,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
		{
		0,0,0,0,0,
		0,0,1,0,0,
		0,0,1,0,0,
		0,0,1,0,0,
		0,0,0,0,0
	},
		{
		0,0,0,0,0,
		0,0,0,0,0,
		0,1,1,1,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//L型方块
	{
		0,0,0,0,0,
		0,0,1,0,0,
		0,0,1,0,0,
		0,0,1,1,0,
		0,0,0,0,0
	},

	//L型方块
	{
		0,0,0,0,0,
		0,0,0,0,0,
		0,1,1,1,0,
		0,1,0,0,0,
		0,0,0,0,0
	},

	//L型方块
	{
		0,0,0,0,0,
		0,1,1,0,0,
		0,0,1,0,0,
		0,0,1,0,0,
		0,0,0,0,0
	},
	//L型方块
	{
		0,0,0,0,0,
		0,0,0,1,0,
		0,1,1,1,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//田字型
	{
		0,0,0,0,0,
		0,1,1,0,0,
		0,1,1,0,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//田字型
	{
		0,0,0,0,0,
		0,1,1,0,0,
		0,1,1,0,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//田字型
	{
		0,0,0,0,0,
		0,1,1,0,0,
		0,1,1,0,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//田字型
	{
		0,0,0,0,0,
		0,1,1,0,0,
		0,1,1,0,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//T型方块
	{
		0,0,0,0,0,
		0,1,1,1,0,
		0,0,1,0,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//T型方块
	{
		0,0,0,0,0,
		0,0,1,0,0,
		0,1,1,0,0,
		0,0,1,0,0,
		0,0,0,0,0
	},
	//T型方块
	{
		0,0,0,0,0,
		0,0,1,0,0,
		0,1,1,1,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//T型方块
	{
		0,0,0,0,0,
		0,0,1,0,0,
		0,0,1,1,0,
		0,0,1,0,0,
		0,0,0,0,0
	},
	//Z型方块
	{
		0,0,0,0,0,
		0,1,1,0,0,
		0,0,1,1,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//Z型方块
	{
		0,0,0,0,0,
		0,0,1,0,0,
		0,1,1,0,0,
		0,1,0,0,0,
		0,0,0,0,0
	},
	//Z型方块
	{
		0,0,0,0,0,
		0,0,1,1,0,
		0,1,1,0,0,
		0,0,0,0,0,
		0,0,0,0,0
	},
	//Z型方块
	{
		0,0,0,0,0,
		0,1,0,0,0,
		0,1,1,0,0,
		0,0,1,0,0,
		0,0,0,0,0
	}
};

//欢迎界面
void welcome(void) {
	//初始化画布
	initgraph(550,650);
	//加标题
	HWND window = GetHWnd();   //获取当前窗格
	SetWindowText(window,_T("俄罗斯方块"));  //设置窗口标题
	//设置文本字体样式
	setfont(40, 0, _T("微软雅黑"));
	setcolor(WHITE);

	outtextxy(205,200,_T("俄罗斯方块"));

	setfont(22, 0, _T("楷体"));
	//setcolor(WHITE);

	outtextxy(175, 300, _T("编程,从俄罗斯方块开始!"));

	Sleep(3000); //3秒
}
//初始化游戏场景
void initGameScene(void) {

	char str[16];
	//清除屏幕
	cleardevice();
	rectangle(27,27,336,635);
	rectangle(29, 29, 334, 633);
	//右上角
	rectangle(370, 50, 515, 195);

	setfont(24, 0, _T("楷体"));
	setcolor(LIGHTGRAY);
	outtextxy(405, 215, _T("下一个:"));  //_T只能接受常量

	setcolor(RED);
	outtextxy(405, 280, _T("分数:"));

	sprintf_s(str,"%d", score);       //按照指定格式将字符串打印到字符数组中
	outtextxy(415, 310,str);

	outtextxy(405, 375, _T("等级:"));
	sprintf_s(str, "%d", rank);
	outtextxy(425, 405, str);

	//操作说明
	setcolor(LIGHTBLUE);
	outtextxy(390, 475, _T("操作说明:"));
	outtextxy(390, 500, _T("↑:旋转"));
	outtextxy(390, 525, _T("↓:下降"));
	outtextxy(390, 550, _T("←:左移"));
	outtextxy(390, 575, _T("→:右移"));
	outtextxy(390, 600, _T("空格:暂停"));
}

void clearBlock(void) {//右上角清除

	setcolor(BLACK);
	settextstyle(23, 0, "楷体");

	for (int i = 0;i < 5;i++) {
		for (int j = 0;j < 5;j++) {
			int x = 391 + j * BLOCK_SIZE;
			int y = 71 + i * BLOCK_SIZE;
			outtextxy(x,y,"■");
		}
	}

}
//绘制方块:在指定位置指定方向绘制方块
void drawBlock(int x, int y) {//在右上角画出图像
	setcolor(color[NextIndex]);
	setfont(23, 0, "楷体");

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[NextIndex * 4][i][j] == 1) {
				int x2 = x + j * BLOCK_SIZE;
				int y2 = y + i * BLOCK_SIZE;
				outtextxy(x2, y2, "■");
			}
		}
	}
}
//绘制方块:在指定位置指定方向绘制方块
void drawBlock2(int x,int y,int blockIndex,block_dir_t dir) {//在右上角画出图像
	setcolor(color[blockIndex]);
	setfont(23, 0, "楷体");
	int id = blockIndex * 4 + dir;

	for (int i = 0;i < 5;i++) {
		for (int j = 0;j < 5;j++) {
			if (block[id][i][j] == 1) {
				int x2 = x + j * BLOCK_SIZE;
				int y2 = y + i * BLOCK_SIZE;
				outtextxy(x2, y2, "■");
			}
		}
	}
}


void nextblock(void) {
	clearBlock();//清除
	//随机选择一种方块
	srand((int)time(NULL));//随时间产生个随机种子,才会产生随机数,使用时间函数的返回值,作为随机种子
	NextIndex = rand() % BLOCK_COUNT;//根据随机种子产生随机数,产生个序号,画第几个方块
	drawBlock(391,71);//在右上角画出图像
}
//如果在指定位置可以向指定方向移动,就返回1,否则返回0
int moveable(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir) {
	//计算当前方块左上角在30*15的游戏区中的位置(第多少行,第多少列)
	int x = (y0 - minY) / BLOCK_SIZE;
	int y = (x0 - minX) / BLOCK_SIZE;
	int id = BlockIndex * 4 + blockDir;
	int ret = 1;
	if (moveDir == MOVE_DOWN) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {    //当时这个y+j错了,写成y+1了
				if (block[id][i][j] == 1 &&   
					(x + i + 1 >= 30 || visit[x + i + 1][y + j] == 1)) {
							ret = 0;
				}
			}
		}
	}
	else if(moveDir ==MOVE_LEFT)
	{
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					y + j == 0 || visit[x + i][y + j - 1] == 1) {
					ret = 0;
				}
			}
		}
	}
	else if (moveDir == MOVE_RIGHT) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) {
							ret = 0;
				}
			}
		}
	}
	return ret;
}
//检测游戏是否结束
void failCheck() {
	if (!moveable(START_X, START_Y, MOVE_DOWN, BLOCK_UP)) {
		setcolor(WHITE);
		setfont(45, 0, "隶体");
		outtextxy(75, 300, "GAME OVER");
		Sleep(1000);
		system("pause");
		closegraph();
		exit(0);
	}
}
//清除指定位置指定方向的方块
//参数x:方框的左上角的x坐标
//参数y:方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBIGBlock(int x, int y, block_dir_t dir) {
	setcolor(BLACK);
	int id = BlockIndex * 4 + dir;
	y += START_Y;

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1) {
				//擦除该方块的第i行的第j列
				outtextxy(x + j * 20, y + i * 20, "■");
			}
		}
	}
}

void wait(int interval) {//500ms
	int count = interval / 10;
	for (int i = 0; i < count; i++) {
		Sleep(10);
		if (_kbhit()) {
			return;
		}
	}
}

//判断当前方块是否可以转向到指定方向
//注意,此时还没有转该方向
int rotatable(int x, int y, block_dir_t dir) {

	int id = BlockIndex * 4 + dir;
	int xIndex = (y - minY) / 20;
	int yIndex = (x - minX) / 20;
	if (!moveable(x, y, MOVE_DOWN, dir)) {
		return 0;
	}

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1 &&
				(yIndex + j < 0 || yIndex + j >= 15 || visit[xIndex + i][yIndex + j] == 1)) {
					return 0;
			}
		}
	}
	return 1;
}
//方块固定下来
void mark(int x, int y, int blockIndex, block_dir_t dir) {
	int id = blockIndex * 4 + dir;
	int x2 = (y - minY) / 20;
	int y2 = (x - minX) / 20;

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1) {
				visit[x2+i][y2+j]=1;
				pointcolor[x2 + i][y2 + j] = color[blockIndex];
			}
		}
	}

}

void move(void) {

	int x = START_X;
	int y = START_Y;
	int k = 0;
	block_dir_t blockDir = BLOCK_UP;
	int curspeed = speed;

	//检测游戏是否结束
	failCheck();

	//持续向下降落
	while (1) {
		if (_kbhit()) {
			int key = _getch();
			if (key == KEY_SPACE) {
				_getch(); //暂停
			}
		}

		//清除当前方块
		clearBIGBlock(x, k, blockDir);  //第三个是方向朝向

		//再次判断有没有按键按下去
		if (_kbhit()) {  //要绘制新形态
			int key = _getch();
			if (KEY_UP == key) {
				block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4);
				if (rotatable(x, y + k, nextDir)) {
					blockDir = nextDir;
				}
			}
			else if (key == KEY_DOWN) { //加速
				curspeed = 50;
			}
			else if (key == KEY_LEFT) {
				if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) {
					x -= 20;
				}				
			}
			else if (key == KEY_RIGHT) {
				if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) {
					x += 20;
				}				
			}
		}
		k += 20;
		//绘制当前模块
		drawBlock2(x,y+k,BlockIndex,blockDir);
		wait(curspeed);

		
		//方块固化处理
		if (!moveable(x, y + k, MOVE_DOWN, blockDir)) {
			mark(x,y+k,BlockIndex,blockDir);
			break;
		}

	}
}
/*进入主框图的方块*/
void newblock(void) {

	//确定即将使用的方块类型
	BlockIndex = NextIndex;

	//绘制刚从顶部下降的方块
	drawBlock(START_X,START_Y);
	
	//让新出现的方块暂停一会,让用户识别
	Sleep(100); //0.1s

	//绘制下一个右上方的图像
	nextblock();

	//下降过程
	move();
}

void down(int x) {
	for (int i = x; i > 0; i--) {
		//消除第i行,第j列方格消除
		for (int j = 0; j < 15; j++) {
			if (visit[i - 1][j]) {
				visit[i][j] = 1;
				pointcolor[i][j] = pointcolor[i - 1][j];
				setcolor(pointcolor[i][j]);
				outtextxy(20*j+minX,20*i+minY,"■");
			}
			else {
				visit[i][j] = 0;
				setcolor(BLACK);
				outtextxy(20 * j + minX, 20 * i + minY, "■");
			}
		}
		
	}
	//清除最顶上的一行,就是行标为0的那一行
	setcolor(BLACK);
	for (int j = 0; j < 15; j++) {
		visit[0][j] = 0;
		outtextxy(20 * j + minX, minY, "■");
	}
}
/*
更新分数,参数lines表示消除的行数
*/
void addScore(int lines) {
	char str[32];
	setcolor(RED);
	score += lines * 10;
	sprintf_s(str, "%d", score);   //申请个变量
	outtextxy(415, 310, str);
}

void updataGrade() {
	//更新等级的提示
	//假设50分一级
	rank = score / 50;
	char str[16];
	sprintf_s(str, "%d", rank);
	outtextxy(425, 405, str);
	//更新速度,等级越高,速度越快,speed越小
	//最慢:500,最快是:100
	speed = 500 - rank*100;
	if (speed <= 100) {
		speed = 100;
	}

}
void check(void) {
	int i, j;
	int clearLines=0;
	for (i = 29; i >= 0; i--) {
		//检查第i行有没有满
		for (j = 0; j < 15 && visit[i][j]; j++);
			
			//执行到此处时有两种情况
			//1.第i行没满,即表示有空位,此时j<15
			//2.第i行已经满了,此时j>=15,
			if (j >= 15) {
				//此时第i行已经满了,就需要消除第i行
				down(i);//消除第i行,并下降一行
				i++;//因为最外层循环中有i--,所以我们先i++,使得下次循环时,再把
				     //这一行检查一下
				clearLines++;
			}

	}
	//更新分数
	addScore(clearLines);

	//更新等级(更新等级提示,更新速度)
	updataGrade();
}

int main(void) {

	//欢迎界面
	welcome();
	//初始化游戏画幕
	initGameScene();

	//右上角产生新方块
	nextblock();
	Sleep(500);

	//初始化访问数组,访问数组是干啥的?
	memset(visit, 0, sizeof(visit)); //整个数组都设置为0,设置状态

	while (1) {
		newblock();
		//消除满行并更新分数和速度
		check();
	}

	system("pause");
	closegraph();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值