C++俄罗斯方块

#include<stdio.h>
#include<graphics.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>//kbhit()的使用


int score = 0;
int rank = 0;

int speed = 500;//500毫秒
int minX = 30;
int minY = 30;

#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_DOWN     80
#define KEY_LEFT     75
#define KEY_SPACE    32


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] = {
	//条形方块
	{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,
	}
};//3种方块有4种形态,一个方块占据的空间在5*5的格子里面

//欢迎界面
void welcome(void)
{
	initgraph(550, 660);
	//设置窗口标题
	HWND window= GetHWnd();//获取窗口
	SetWindowText(window, _T("俄罗斯方块 2023年版本"));//设置窗口标题

	//设置文本的样式
	setfont(40, 0, _T("微软雅黑"));
	setcolor(WHITE);
	outtextxy(205, 200, _T("俄罗斯方块"));
	setfont(22, 0, _T("楷体"));
	outtextxy(175, 300, _T("2023年版本 编程小游戏"));

	Sleep(3000);//睡眠 3秒
}

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

	//清除屏幕
	cleardevice();

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

	//设置文本的样式
	setfont(24, 0, _T("楷体"));
	setcolor(LIGHTGRAY);//灰色
	outtextxy(405, 215, _T("下一个"));

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

	sprintf(str,"%d", score);//按指定格式把score打印到str;
	outtextxy(415, 310, str);

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

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

	
}

//清除方块
void clearBlock(int x,int y)
{
	setcolor(BLACK);
	setfont(23, 0, "楷体");
	for (int i = 0; i < BLOCK_HEIGHT; i++)
	{
		for (int j = 0; j < BLOCK_WIDTH; j++)
		{
			int w = x + UNIT_SIZE * j;
			int z = y + UNIT_SIZE * i;
			outtextxy(w , z , "■");
		}
	}
}
//清楚指定位置指定方块的函数
//参数x:方块的左上角坐标
//参数y:方块的左上角在游戏区域内的坐标,距离游戏顶部的距离
void clearBlock(int x, int y,block_dir_t dir)
{
	setcolor(BLACK);
	setfont(23, 0, "楷体");
	int id = BlockIndex * 4 + dir;
	y += START_Y;
	/*x += START_X;*/

	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[4*NextIndex][i][j]==1)
			{
				int w = x + UNIT_SIZE * j;
				int z = y + UNIT_SIZE * i;
				outtextxy(w,z, "■");
			}
			
		}
	}
}
//绘制方块:z在指定位置绘制指定方块
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 w = x + UNIT_SIZE * j;
				int z = y + UNIT_SIZE * i;
				outtextxy(w, z, "■");

			}

		}
	}
}

//产生新方块
void nextblock(void)
{
	clearBlock(391,71);//清除右上角缓冲区

	//随机选择一种方块
	srand(time(NULL));//使用时间函数的返回值,来作为随机种子
	NextIndex=rand() % BLOCK_COUNT;//生成随机数,取余可以得到5种 0到4随机的值
	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)/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 failCheak(void)
{
	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);//结束
	}
}
//判断当前方块是否可以转到指定方向
//注意,此时还没有转到指定方向
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 &&
				(j + yIndex < 0 || yIndex + j>15 || visit[xIndex + i][yIndex + i] == 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(void)
{
	int x = START_X;
	int y = START_Y;
	int k = 0;
	int curSpeed = speed;
	block_dir_t blockDir=BLOCK_UP;
	
	
	//检测游戏是否结束

	failCheak();

	//持续向下降落
	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))
				{
					x -= 20;
				}
				
			}
			else if(key == KEY_RIGHT)
			{
				if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir))
				{
					x += 20;
				}
			}
		}
		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(void)
{
	//确定即将使用的方块的类别
	BlockIndex = NextIndex;

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

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

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

	//方块降落过程
	move();
}

//消除第x行
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;
				markColor[i][j] = markColor[i - 1][j];
				setcolor(markColor[i][j]);
				outtextxy(j * 20 + minX, i * 20 + minY, "■");
			}
			else
			{
				visit[i][j] = 0;
				setcolor(BLACK);
				outtextxy(j * 20 + minX, i * 20 + minY, "■");
			}
		}
	}
	//清楚最顶上的哪一行,就是清楚定0行
	setcolor(BLACK);
	for (int j = 0; j < 15; j++)
	{
		visit[0][j] = 0;
		outtextxy(j * 20 + minX,  minY, "■");
	}
}
void addScore(int lines)
{
	char str[32];
	setcolor(RED);
	score += 10* lines;
	sprintf(str, "%d", score);//按指定格式把score打印到str;
	outtextxy(415, 310, str);
}
void updateGrade()
{
	char str[16];
	//跟新等级,50分一级
	rank = score / 50 + 1;
	sprintf(str, "%d", rank);//按指定格式把score打印到str;
	outtextxy(425, 405, str);
	//速度跟新,等级高,速度快
	speed = 500 - rank * 10;
	if (speed <= 100)
	{
		speed = 100;
	}


}

void cheak()
{
	int i = 0;
	int j = 0;
	int clearLines = 0;
	int rank = 0;
	for ( i = 29; i >=0; i--)
	{
		for ( j =0; j <15&&visit[i][j]; j++)//visit[i][j]==1等于visit[i][j]
		{
			//执行到此处时有两种情况,
			//第I行没有满,j<15
			//第i行满了。j>=15
			if (j >= 14)
			{
				//此时第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));//把整个数组设置为0,清空数组

	while (1) 
	{
		newblock();
		//消除满行,更新分数
		cheak();
	}

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值