俄罗斯方块(简易实现)

#include <stdio.h>
#include <graphics.h>
#include <Windows.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 UNIT_SIZE 20

#define START_X 130
#define START_Y 30

#define KEY_UP 72
#define KEY_RIGHT 77
#define KEY_LEFT 75
#define KEY_DOWN 80
#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 markColor[30][15];//需要定义这个,因为要判断什么颜色的方块停留在这里(表示对应位置的颜色)

int block[BLOCK_COUNT*4][BLOCK_HEIGHT][BLOCK_WIDTH]=
{
	// | 形方块markColor
	{ 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 },

	{ 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 },

	{ 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 },

	{ 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 },

	{ 0,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,0,0,0,0,
	0,0,1,0,0,
	0,1,1,1,0,
	0,0,0,0,0,
	0,0,0,0,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,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 },

	{ 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 },

	{ 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 },

	{ 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 },
};

//欢迎界面
void welcome(void)
{	
	//初始化画布
	initgraph(550,660);
	
	//设置窗口标题
	HWND window=GetHWnd();//获取窗口
	SetWindowText(window,"俄罗斯方块");//设置窗口标题

	//设置文本的字体样式
	setfont(40,0,"微软雅黑");
	setcolor(WHITE);
	outtextxy(205,200,"俄罗斯方块");

	setfont(22,0,"楷体");
	outtextxy(175,300,"编程,从俄罗斯方块开始!");

	Sleep(3000);

}

//初始化游戏场景
void initGameScene(void)
{
	char str[16];

	//清除屏幕
	cleardevice();
	setcolor(WHITE);

	rectangle(27,27,336,635);
	rectangle(29,29,334,633);
	rectangle(370,50,515,195);

	setfont(24,0,"楷体");
	setcolor(LIGHTGRAY);
	outtextxy(405,215,"下一个");

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

	sprintf(str,"%d",score);
	outtextxy(415,310,str);

	outtextxy(405,375,"等级");
	sprintf(str,"%d",rank);
	outtextxy(425,405,str);
	 
	//操作说明
	setcolor(LIGHTBLUE);
	outtextxy(390,475,"操作说明:");
	outtextxy(390,500,"↑:旋转");
	outtextxy(390,525,"←:左移");
	outtextxy(390,550,"↓:下移");
	outtextxy(390,575,"→:右 移");
	outtextxy(390,600,"空格:暂停");

	
}

void clearBlock()
{
	setcolor(BLACK);
	setfont(23,0,"楷体");
	for(int i=0;i<BLOCK_HEIGHT;i++)
	{
		for(int j=0;j<BLOCK_WIDTH;j++)
		{ 
			int x=391+j*UNIT_SIZE;
			int y=71+i*UNIT_SIZE;
			outtextxy(x,y,"■");
		}
	}
}

//清除指定位置指定方向的方块
//参数x:方块的左上角的x坐标
//参数y:方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x,int y,block_dir_t dir)
{
	setcolor(BLACK);
	int id=BlockIndex*4+dir;
	y=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 drawBlock(int x,int y)
{
	setcolor(color[NextIndex]);
	setfont(23,0,"楷体");

	for(int i=0;i<BLOCK_HEIGHT;i++)
	{
		for(int j=0;j<BLOCK_WIDTH;j++)
		{
			if(block[NextIndex*4][i][j]==1)
			{
				outtextxy(x+j*UNIT_SIZE,y+i*UNIT_SIZE,"■");//这点注意不要从新定义个x和y,否则会叠加值,致使方块位置不对
			}
		}
	}
}

void drawBlock(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<BLOCK_HEIGHT;i++)
	{
		for(int j=0;j<BLOCK_WIDTH;j++)
		{
			if(block[id][i][j]==1)
			{
				int x2=x+j*UNIT_SIZE;
				int y2=y+i*UNIT_SIZE;
				outtextxy(x2,y2,"■");
			}
		}
	}
}

void nextblock()
{
	int x=391;
	int y=71;
	clearBlock();//清除右上角区域

	//随机选择一种方块
	srand(time(NULL));//使用时间函数的返回值,来作为随机种子
	NextIndex=rand()%BLOCK_COUNT;
	  
	drawBlock(x,y);
}

//如果在指定位置可以向指定方向移动,就返回1,否则就返回0
int moveable(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir)
{
	//计算当前方块的左上角在30*15的游戏区中的位置(第多少行,第多少列)
	int x=(y0-minY)/UNIT_SIZE;
	int y=(x0-minX)/UNIT_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++)
			{
				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");

		exit(0);
	}
}


//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x,int y,block_dir_t dir)
{
	int xIndex=(y-minY)/20;
	int yIndex=(x-minX)/20;

	if(!moveable(x,y,MOVE_DOWN,dir))
	{
		return 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&&
			   (yIndex+j<0||yIndex+j>=15||visit[xIndex+i][yIndex+j]==1))
			{
				return 0;
			}
		}
	}
	return 1;
}

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

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;
				markColor[x2+i][y2+j]=color[blockIndex];
			}
		}
	}
	

}

void move()
{
	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(); 
			}
		}

		//清除当前方块
		clearBlock(x,k,blockDir);

		if(_kbhit())
		{
			int key=getch();

			if(key==KEY_UP)
			{
				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))//同时下降,所以要+20
				{
					x=x-20;
				}
			}else if(key==KEY_RIGHT)
			{
				if(moveable(x,y+k+20,MOVE_RIGHT,blockDir))
				{
					x=x+20;
				}
			}
		}

		k=k+20;

		//绘制当前方块
		drawBlock(x,y+k,BlockIndex,blockDir);

		wait(curSpeed);

		

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

}

void newblock()
{
	//确定即将使用的方块的类别
	BlockIndex=NextIndex;

	//绘制一个刚从顶部下降的方块
	drawBlock(START_X,START_Y);

	//让新出现的方块暂停一会,让用户识别到
	Sleep(100);//0.1秒

	//在右上角区域,绘制一个下一个方块
	nextblock(); 

	//方块降落
	move();
}

void down(int x)
{
	for(int i=x;i>0;i--)//这里i不能>=0,因为总是判断上一行,不能为-1
	{
		for(int j=0;j<15;j++)
		{
			if(visit[i-1][j])
			{
				visit[i][j]=1;
				markColor[i][j]=markColor[i-1][j];
				setcolor(markColor[i][j]);
				outtextxy(20*j+minX,20*i+minY,"■");
			}else
			{
				visit[i][j]=0;
				setcolor(BLACK);
				outtextxy(20*j+minX,20*i+minY,"■");
			}
		} 
	}

	//清除最顶上的那一行(就是行标为0的那一行),因为不能为-1,所有最顶行要单独判断
	setcolor(BLACK);
	for(int j=0;j<15;j++)
	{
		visit[0][j]=0;
		outtextxy(20*j+minX,minY,"■");
	}

}

void addScore(int lines)
{
	char str[32];

	setcolor(RED);
	score=score+lines*10;
	sprintf(str,"%d",score);
	outtextxy(415,310,str);
}

void updateGrade()
{
	//更新等级的提示
	//假设:50分一级
	rank=score/50;
	char str[16];
	sprintf(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--)
	{
		for(j=0;j<15&&visit[i][j];j++);	
	
			//执行到此处时,有两种情况:
			//2.第i行已经满了,此时j>=15
			//1.第i行没有满,即表示有空位 此时就j<15
			if(j>=15)
			{
				//此时,第i行已经满了,就需要消除第i行
				down(i);//消除第i行,并把上面的行都下移
				i++;//因为最外层的循环中有i--,所以我们先i++,使得下次循环时,再把这一行检查以下
				clearLines++;
			}
			
	}

	//更新分数
	addScore(clearLines);

	//更新等级
	updateGrade();
}

int main(void)
{
	//设置游戏窗口
	welcome();

	//初始化游戏界面
	initGameScene();

	//产生新方块
	nextblock();
	Sleep(500);//在刚开始的时候等一会儿,让用户体验感更好

	//初始化访问数组
	memset(visit,0,sizeof(visit));

	while(1)
	{
		newblock();

		check();
	}

	system("pause");

	closegraph();


	return 0;
}

需要安装esayX,vc2010或者2019

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的鱼-blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值