#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);
setfont(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[NextIndex]);
setfont(23, 0, "楷体");
int id = blockIndex * 4 + dir;
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 nextblock(void) {
clearBlock();//清除
//随机选择一种方块
srand(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++) {
if (block[id][i][j] == 1 &&
(x + i + 1 >= 30 || visit[x + i + 1][y + 1] == 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];
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);
//k += 20;
//方块固化处理
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;
}
俄罗斯方块
最新推荐文章于 2024-09-25 13:54:13 发布