linux下使用C语言实现俄罗斯方块游戏

实现思路:

关键点是使用闹钟信号,闹钟定时一段时间触发信号函数,在信号函数里对方块进行自动下落操作,而主线控制界面显示以及处理用户的输入。

共有两个文件 main.cdata.h,需要把两个文件放到同一目录下,直接在改目录下执行命令:

gcc main.c -o main

注意:如果编译的时候提示没有或者找不到 data.h 这个文件的话:

  • 1、确保两个文件是在同一路径下并检查 data.h 这个文件的命名是否正确(有时候你的data.h后面可能多了个空格或者其它空白字符)导致这个文件找不到;
  • 2、实在是解决不了的话,直接把 data.h 里面的数据复制到 main.c 文件的 #include "data.h" 下面并去掉 #include "data.h" 这句代码

提示:如果中途遇到问题,可以留言、评论或者私信,我有时间上线看到之后会回复大家的疑惑(但我的空闲时间有限,并不是所有问题都会回复,代码一定是完整、可以正常运行的,至于自己的电脑为什么起不来,就需要自己学会找一下问题了),同时也希望大家看完后认真学习一下代码的思路(代码里面的注释已经非常详细了),只有这样大家才能有所进步!


main.c —— 这是主程序,需要用到下面的 data.h 文件

/*************************************************
 * name: main
 * 功能:实现俄罗斯方块小游戏
 * 编写人:王廷云
 * 编写日期:2018-3-21
 * 最近更新日期:2019-7-3
**************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include "data.h"

#define   ROW    21     // 游戏区域的行数
#define   COL    18     // 游戏区域的列数

/* 按键枚举 */
enum key {
     
   DOWN,    			// 上
   LEFT,    			// 左
   RIGHT,   			// 右
   CHANGE,  			// 变化
   STOP,    			// 停止
   EXIT,    			// 退出
   UNKNOW,  			// 未知
};

/***** 函数声明区域 ******/
void initalGameArea(void);                  // 初始化游戏区域
void drawBlock(char bl[NR][NR]);            // 画方块
void cleanBlock(char bl[NR][NR]);           // 清除方块
void turnBlock(char bl[NR][NR]);            // 旋转方块
void gameEnd(void);                         // 结束游戏
void gameStop(void);                        // 暂停游戏
void showGame(void);                        // 显示游戏
void gameSelf(int signo);                   // 游戏自动运行
void checkDeleteLine(void);                 // 检查是否满一行
void checkGameOver(char bl[NR][NR]);        // 检查是否游戏结束
int  checkMove(char bl[NR][NR], int flag);  // 检查方块是否可移动
int  getInput(void);                        // 获取输入

/* 全局变量区域 */
static char gameArea[ROW][COL] = {
     0};   	// 游戏区域数据
static int startX = 7, startY = 6;      	// 方块出现的起始位置
static int type = 0;                    	// 方块当前类型
static int nextType = 0;                	// 方块的下一种类型
static int diret = 0;                   	// 方块的方向
char *state = "\033[32m游戏中...\033[0m";	// 游戏运行状态
static unsigned int level = 0;          	// 游戏等级
static unsigned int score = 0;          	// 游戏分数
static unsigned int maxScore = 0;       	// 游戏最高记录
static FILE *fp = NULL;                 	// 用于把记录保存到文件

/*
 * 主函数:控制全局流程
*/
int main(void)
{
     
    /*
     * 设置闹钟:
     * 当前时间间隔为0.7秒,下一次时间间隔为0.7秒
    */
    struct itimerval  timer = {
     {
     0,700000},{
     0,700000}};
    setitimer(ITIMER_REAL, &timer,NULL);

    /* 初始化游戏区域 */
    initalGameArea();

    /* 设置游戏信号 */
    signal(SIGALRM, gameSelf);

    /* 初始化方块类型 */
    srand(time(NULL));
    type     = rand()%7;
    nextType = rand()%7;

    /* 读取文件的最高记录 */
    fp = fopen("./record","r+");
    if (NULL == fp)
    {
     
        /*
         * 文件不存在则创建并打开 
         * "w"方式打开会自动创建不存在的文
         */
        fp = fopen("./record","w");
    }
    fscanf(fp,"%u",&maxScore);

    /* 用户操作 */
    int key;
    while (1)
    {
     
        key = getInput();
        switch (key)
    	{
     
    	    case RIGHT : checkMove(bl[type][diret],RIGHT);
    	                 break;
    	    case LEFT  : checkMove(bl[type][diret],LEFT);
    	                 break;
    	    case DOWN  : checkMove(bl[type][diret],DOWN);
    	                 break;
    	    case CHANGE: turnBlock(bl[type][(diret+1)%4]);
    	                 break;
    	    case STOP  : gameStop();
    	                 break;
    	    case EXIT  : gameEnd();
    	                 break;
    	    case UNKNOW: continue;
    	}

        /* 画方块 */
        drawBlock(bl[type][diret]);

        /* 显示游戏 */
        showGame();

        /* 清除方块 */
        cleanBlock(bl[type][diret]);
    }

    return 0;
}

/*
 * 函数名:initalGameArea
 * 函数功能:初始化游戏区域
 * 参数:无
 * 返回值:无
*/
void initalGameArea(void)
{
     
    int i;

	/* 屏幕设置 */
	printf("\033[2J");			  // 清屏
    system("stty -icanon");		  // 关缓冲
    system("stty -echo");		  // 关回显
    fprintf(stdout,"\033[?25l");  // 关闭鼠标显示

    /* 初始化行 */
    for (i = 0; i < COL; i++)
    {
     
        gameArea[0][i]     = 8;   // 第0行
        gameArea[5][i]     = 8;   // 第5行
        gameArea[ROW-1][i] = 8;   // 最后一行
    }

    /* 初始化列 */
    for (i = 0; i < ROW; i++)
    {
     
        gameArea[i][0]     = 8;  // 第0列
        gameArea[i][COL-1] = 8;  // 最后一列
    }

    /* 中间一小列 */
    for (i = 1; i < 5; i++)
    {
     
        gameArea[i][6] = 8;
    }
}


/*
 * 函数名:gameSelf
 * 函数功能:作为信号函数,闹钟时间一到就自动下落
 * 参数:信号
 * 返回值:无
*/
void gameSelf(int signo)
{
     
    /* 画方块 */
    drawBlock(bl[type][diret]);

    /* 显示游戏 */
    showGame();

    /* 清除方块 */
    cleanBlock(bl[type][diret]);

    /* 如果方块已经到底 */
    if (!checkMove(bl[type][diret],DOWN))
    {
     
    	/* 检查是否游戏结束 */
    	checkGameOver(bl[type][diret]);

    	/* 保留已经到底的方块 */
    	drawBlock(bl[type][diret]);

    	/* 显示游戏结果 */
    	showGame();

    	/* 到达边界后检查是否可消行 */
    	checkDeleteLine();

    	/* 重新开始下一个方块 */
    	startY = 6;
    	startX = 7;
    	type = nextType;
    	nextType = rand()%7;
    	diret = 0;
    }
}

/*
 * 函数名:checkDeleteLine
 * 函数功能:检查是否可消行
 * 参数:无
 * 返回值:无
*/
void checkDeleteLine(void)
{
     
    int i, j;
    int x, y;

    /* 检查当前方块的四行区域内 */
    for (i = 3; i >= 0; i--)
    {
     
        for (j = 1; j < COL-1; j++)
    	{
     
    	    /* 检测方块是否满一行 */
    	    if (gameArea[startY+i][j] == 0)
    	        break;
    	    /* 跳过边框区域 */
    	    else if (gameArea[startY+i][j] == 8)
    	        break;
    	}
    	/* 如果满了一行则删除一行 */
    	if (j == COL-1)
    	{
     
    	    /* 删除满了的一行 */
    	    for (j = 1; j < COL-1; j++)
    	    {
     
    	        gameArea[startY+i][j] = 0;
    	    }

    	    /* 分数累加 */
    	    score += 100;

    	    /* 记录最高分 */
    	    if (score > maxScore)
    	    {
     
    	        maxScore = score;

    		    /* 保存最高分 */
                rewind(fp);
    		    fprintf(fp,"%u\n",maxScore);
    	    }

    	    /* 记录级别 */
    	    if (score%200 == 0)
    	    {
     
    		    /* 每200分加一级 */
    	        level++;
    	    }

    	    /* 删除后往下移动一行 */
    	    for (x = 1; x < COL-1; x++)
    	    {
     
    	        for (y = startY+i; y >= 7; y--)
        		{
     
        		    gameArea[y][x] = gameArea[y-1][x];
        		}
    	    }

    	    /* 移动的一行需要检测范围加一行 */
    	    i++;
    	}
    }
}

/*
 * 函数名:checkGameOver
 * 函数功能:检查游戏是否结束
 * 参数:待检查方块数据数据
 * 返回值:无
*/
void checkGameOver(char block[NR][NR])
{
     
    int i;

    for (i = 0; i < NR; i++)
    {
     
        /* 方块碰到上方边界则游戏结束 */
        if (block[0][i] != 0 && gameArea[startY-1][startX+i] == 8)
    	{
     
    	    gameEnd();
    	}
    }
}

/*
 * 函数名:turnBlock
 * 函数功能:旋转方块
 * 参数:需要旋转的方块数组数据
 * 返回值:无
*/
void turnBlock(char bl[NR][NR])
{
     
    int x, y;

    /* 检查是否越界 */
    for (y = 0; y < NR; y++)
    {
     
        for (x = 0; x < NR; x++)
    	{
     
    	    /* 只能判断到达了边界 */
    	    if (bl[y][x] != 0 && gameArea[startY+y][startX+x] != 0)
    	    {
     
    	        return;
    	    }
    	}
    }

    /* 两边都没有越界则旋转方块方向 */
    diret = (diret+1)%4;
}

/*
 * 函数名:gameStop
 * 函数功能:暂停游戏,等待用户再次启动游戏
 * 参数:无
 * 返回值:无
*/
void gameStop(void)
{
     
    /* 创建一个暂停的是时钟 */
    struct itimerval stop = {
     0}, older;

    /* 设置新闹钟并存储旧闹钟 */
    setitimer(ITIMER_REAL,&stop,&older);

    /* 配置暂停后的界面 */
    state = "\033[31m暂停中...\033[0m";
    startY--;             // 为了防止按下暂停键后方块下滑一格
    drawBlock(bl[type][diret]);
    showGame();
    cleanBlock(bl[type][diret]);

    /* 等待用户按开始键或退出键 */
    int key;
    while (1)
    {
     
        key = fgetc(stdin);

    	/* 空格开始 */
    	if (key == ' ')
    	    break;

    	/* q 退出 */
    	else if (key == 'q')
    	    gameEnd();
    }

    /* 恢复闹钟和游戏 */
    setitimer(ITIMER_REAL,&older,NULL);
    state = "\033[32m游戏中...\033[0m";
}

/*
 * 函数名:checkMove
 * 函数功能:检查方块是否可移动,能移则移
 * 参数:1.方块数组数据 2.方向标志位
 * 返回值:可移动返回1,不能移动返回0
*/
int checkMove(char bl[NR][NR], int flag)
{
     
    int m, n;   // 用于标明可移动方向
    int x, y;   // 用于循环

    switch (flag)
    {
     
        case RIGHT : n = 0; m = 1;  break;
    	case LEFT  : n = 0; m = -1; break;
    	case DOWN  : n = 1; m = 0;  break;
    }

    /* 全局检查 */
    for (y = 0; y < NR; y++)
    {
     
        for (x = 0; x < NR; x++)
    	{
     
    	    /* 只能判断到达了边界 */
    	    if (bl[y][x] != 0 && gameArea[startY+y+n][startX+x+m] != 0)
            {
     
                return 0;
            }
    	}
    }

    /* 出来说明没有到达边界 */
    startY += n;
    startX += m;

    return 1;
}

/*
 * 函数名:getInput
 * 函数功能:获取用户输入
 * 参数:无
 * 返回值:无
*/
int getInput(void)
{
     
    char key;

    key = fgetc(stdin);

    if (key == '\033' && fgetc(stdin) == '[') // 方向键
    {
     
        switch (fgetc(stdin))
    	{
     
    	    case 'A': return CHANGE;
    	    case 'B': return DOWN;
    	    case 'C': return RIGHT;
    	    case 'D': return LEFT;
    	}
    }
    else if (key == 'q')    // 退出键
    {
     
        return EXIT;
    }
    else if (key == ' ')    // 空格键-暂停游戏
    {
     
        return STOP;
    }
    else                    // 其它不相关的键
        return UNKNOW;
}

/*
 * 函数名:drawBlock
 * 函数功能:填充方块数据
 * 参数:方块数组数据
 * 返回值:无
*/
void drawBlock(char block[NR][NR])
{
     
    int x, y;

    /* 画当前方块 */
    for (y = 0; y < NR; y++)
    {
     
        for (x = 0; x < NR; x++)
    	{
     
    	    if (block[y][x] != 0)
            {
     
                gameArea[startY+y][startX+x] = block[y][x];
            }
    	}
    }

    /* 画提示的下一个方块 */
    for (x = 0; x < 2; x++)
    {
     
        for (y = 0; y < NR; y++)
    	{
     
    	    if (bl[nextType][0][x][y] != 0)
    	        gameArea[3+x][2+y] = bl[nextType][0][x][y];
            else
    	        gameArea[3+x][2+y] = 0;
    	}
    }
}

/*
 * 函数名:cleanBlock
 * 函数功能:清除方块数据
 * 参数:方块数组数据
 * 返回值:无
*/
void cleanBlock(char bl[NR][NR])
{
     
    int x, y;

    for (y = 0; y < NR; y++)
    {
     
        for (x = 0; x < NR; x++)
    	{
     
    	    if (bl[y][x] != 0)
            {
     
                gameArea[startY+y][startX+x] = 0;
            }
    	}
    }
}

/*
 * 函数名:showGame
 * 函数功能:显示游戏
 * 参数:无
 * 返回值:无
*/
void showGame(void)
{
     
    int i, j;

    /* 定位到第一行第一列 */
    fprintf(stdout,"\033[1;1H");
    fflush(stdout);

    /* 打印所有数据 */
    for (i = 0; i < ROW; i++)
    {
     
        for (j = 0; j < COL; j++)
    	{
     
            if (gameArea[i][j] == 0)       // 空白区域
            {
     
                fprintf(stdout,"  ");
            }
    	    else if (gameArea[i][j] == 8)  // 边界区域
            {
     
                fprintf(stdout,"\033[40m  \033[0m");
            }
            else                           // 方块区域
            {
     
                fprintf(stdout,"\033[%dm[]\033[0m",gameArea[i][j]+40);
            }
    	}
	    fputc('\n',stdout);
    }

    /* 打印提示信息 */
    fprintf(stdout,"\033[2;3H\033[33m【下一个】\033[0m\n");
    fprintf(stdout,"\033[2;15H当前级别:\033[36m%u\033[0m\n",level);
    fprintf(stdout,"\033[3;15H当前分数:\033[32m%u\033[0m\n",score);
    fprintf(stdout,"\033[4;15H最高记录:\033[35m%u\033[0m\n",maxScore);
    fprintf(stdout,"\033[5;15H当前状态:%s\n",state);
}

/*
 * 函数名:gameEnd
 * 函数功能:处理游戏结束的设置
 * 参数:无
 * 返回值:无
*/
void gameEnd(void)
{
     
    /* 配置游戏结束后的界面 */
    state = "\033[31m游戏结束!!\033[0m";
    drawBlock(bl[type][diret]);
    showGame();

    /* 恢复终端设置 */
    system("stty icanon");          // 恢复缓冲
    system("stty echo");            // 恢复回显
    fprintf(stdout,"\033[?25h");    // 恢复鼠标显示

    /* 尾部处理 */
    fprintf(stdout,"\033[200;1H");  // 定位光标到最后一行
    fclose(fp);                     // 关闭文件
    exit(0);                        // 退出程序
}


data.h —— 这是存放数据文件,为 main.c 提供数据

/*************************************************
 * 文件名: Data.h
 * 功能描述:俄罗斯方块游戏的方块数据库
 * 编写人:王廷云
 * 编写日期:2018-3-21
 * 最近更新日期:2019-7-3
*************************************************/
#ifndef  _DATA_H_
#define  _DATA_H_

#define    NR    4  // 方块数据大小
#define    TYPE  7  // 方块类型数

/* 方块数据 */
char bl[TYPE][NR][NR][NR] = {
     
    /* 第一种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     1,1,0,0},
  	 {
     1,1,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     1,1,0,0},
  	 {
     1,1,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     1,1,0,0},
  	 {
     1,1,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     1,1,0,0},
  	 {
     1,1,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
 },

 /* 第二种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     2,2,2,2},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     0,2,0,0},
  	 {
     0,2,0,0},
  	 {
     0,2,0,0},
  	 {
     0,2,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     2,2,2,2},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     0,2,0,0},
  	 {
     0,2,0,0},
  	 {
     0,2,0,0},
  	 {
     0,2,0,0},
  	},
 },

 /* 第三种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     3,0,0,0},
  	 {
     3,3,3,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     0,3,3,0},
  	 {
     0,3,0,0},
  	 {
     0,3,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     3,3,3,0},
  	 {
     0,0,3,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     0,0,3,0},
  	 {
     0,0,3,0},
  	 {
     0,3,3,0},
  	 {
     0,0,0,0},
  	},
 },

 /* 第四种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     0,0,4,0},
  	 {
     4,4,4,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     0,4,0,0},
  	 {
     0,4,0,0},
  	 {
     0,4,4,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     0,4,4,4},
  	 {
     0,4,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     0,4,4,0},
  	 {
     0,0,4,0},
  	 {
     0,0,4,0},
  	 {
     0,0,0,0},
  	},
 },

 /* 第五种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     0,5,0,0},
  	 {
     5,5,5,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     0,5,0,0},
  	 {
     0,5,5,0},
  	 {
     0,5,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     5,5,5,0},
  	 {
     0,5,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     0,5,0,0},
  	 {
     5,5,0,0},
  	 {
     0,5,0,0},
  	 {
     0,0,0,0},
  	},
 },

 /* 第六种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     6,6,0,0},
  	 {
     0,6,6,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     0,0,6,0},
  	 {
     0,6,6,0},
  	 {
     0,6,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     6,6,0,0},
  	 {
     0,6,6,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     0,0,6,0},
  	 {
     0,6,6,0},
  	 {
     0,6,0,0},
  	 {
     0,0,0,0},
  	},
 },

 /* 第七种方块 */
 {
     
  	{
      /* 第一种方向 */
  	 {
     0,7,7,0},
  	 {
     7,7,0,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第二种方向 */
  	 {
     0,7,0,0},
  	 {
     0,7,7,0},
  	 {
     0,0,7,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第三种方向 */
  	 {
     0,0,7,7},
  	 {
     0,7,7,0},
  	 {
     0,0,0,0},
  	 {
     0,0,0,0},
  	},
  	{
      /* 第四种方向 */
  	 {
     0,7,0,0},
  	 {
     0,7,7,0},
  	 {
     0,0,7,0},
  	 {
     0,0,0,0},
  	},
  },
};

#endif   //_DATA_H_

你可能感兴趣的:(C语言,C语言,俄罗斯方块)