【C语言】之实现2048简易字符界面游戏_如何实现2048字符版-程序员宅基地

技术标签: C/C++  C语言  实现2048游戏  

使用介绍:

本次工程项目总共有三个文件:main.c、2048game.h 和 2048game.c 需要放到一起编译


1、main.c


/**************************************
 * 文件名:main.c
 * 文件描述:实验简易的字符界面2048游戏
 * 编辑人:王廷云
 * 编辑日期:2017-10-21
 * 修改日期:2018-2-10
**************************************/

#include <stdio.h>
#include "2048game.h"

int main(void)
{
    
    int gameData[NR][NR];	// 游戏数据
    int isGameOver = 0;		// 游戏结束标志位
    int key;				// 用户的按键输入

    /* 初始化游戏 */
    initGame(gameData);

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

    /* 进入游戏 */
    while (1)
    {
    
        /* 获取用户输入 */
		key = getInput();
		switch (key)
		{
    
		    case UP    : moveUp(gameData);    break;
		    case DOWN  : moveDown(gameData);  break;
    	    case LEFT  : moveLeft(gameData);  break;
		    case RIGHT : moveRight(gameData); break;
		    case EXIT  : exitGame();          break;
		    case UNKNOW: continue;
		}
		showGame(gameData);  // 每操作一次就显示结果

		/* 判断游戏是否结束 */
		isGameOver = checkGameOver(gameData);
		if (isGameOver == 1)
		{
    
		    fprintf(stdout, "\033[6;10H\033[31mGame Over!\033[0m");
		    exitGame();		 // 游戏满足结束条件-退出游戏
		}
    }
	
    return 0;
}


2、2048game.h


/****************************************
 * 文件名:2048game.h
 * 文件描述:该文件为2048游戏项目的头文件
 *   包含所用到的函数声明、宏定义,枚举等
 * 编辑人:王廷云
 * 编辑日期:2017-10-21
 * 修改日期:2018-2-10
*****************************************/

#ifndef _2048GAME_H_
#define _2048GAME_H_

#define   NR    4   // 游戏的维数

/* 按键输入枚举 */
enum KEY {
    
    UP,			// 上
    DOWN,		// 下
    LEFT,		// 左
    RIGHT,		// 右
    EXIT,		// 退出
    UNKNOW,		// 其它键
};

/*
 * 函数名:initGame
 * 功能:初始化2048游戏,该函数会对游戏界面数据进行初始化
 *       并产生两个随机数
 * 参数:需要初始化的游戏数据
 * 返回值:无
*/
void initGame(int data[NR][NR]);

/*
 * 函数名:showGame
 * 功能:显示游戏界面
 * 参数:需要显示的游戏数据
 * 返回值:无
*/
void showGame(int data[NR][NR]);

/*
 * 函数名:moveUp
 * 功能:把游戏数据上移
 * 参数:需要上移的游戏数据
 * 返回值:无
*/
void moveUp(int data[NR][NR]);

/*
 * 函数名:moveDown
 * 功能:把游戏数据下移
 * 参数:需要下移的游戏数据
 * 返回值:无
*/
void moveDown(int data[NR][NR]);

/*
 * 函数名:moveLeft
 * 功能:把游戏数据左移
 * 参数:需要左移的游戏数据
 * 返回值:无
*/
void moveLeft(int data[NR][NR]);

/*
 * 函数名:moveRight
 * 功能:把游戏数据右移
 * 参数:需要右移的游戏数据
 * 返回值:无
*/
void moveRight(int data[NR][NR]);

/*
 * 函数名:maxScore
 * 功能:获取游戏最高分
 * 参数:需要获取最高分的游戏数据
 * 返回值:返回游戏数据中的最大值
*/
int maxScore(int data[NR][NR]);

/*
 * 函数名:exitGame
 * 功能:游戏退出函数,直接退出当前游戏
 * 参数:无
 * 返回值:无
*/
void exitGame(void);

/*
 * 函数名:checkGameOver
 * 功能:检查游戏是否结束
 * 参数:需要检查的游戏数据
 * 返回值:游戏结束返回1,没有结束返回0
*/
int checkGameOver(int data[NR][NR]);

/*
 * 函数名:getInput
 * 功能:获取用户按键输入
 * 参数:无
 * 返回值:返回用户输入的对应按键
*/
int getInput(void);

/*
 * 函数名:getRand
 * 功能:在游戏数据中产生一个随机数,2的概率为80%,4的概率为20%
 * 参数:需要产生随机数的游戏数据
 * 返回值:无
*/
void getRand(int data[NR][NR]);

#endif  // _2048GAME_H_

3、2048game.c


/********************************************
 * 文件名:2048game.c
 * 文件描述: 2048游戏项目里所有功能函数的实现
 * 编辑人:王廷云
 * 编辑日期:2017-10-21
 * 修改日期:2018-2-14
********************************************/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "2048game.h"

/*
 * 函数名:initGame
 * 功能:初始化2048游戏,该函数会对游戏界面数据进行初始化
 *       并产生两个随机数
 * 参数:需要初始化的游戏数据
 * 返回值:无
*/
void initGame(int data[NR][NR])
{
    
    int x, y;

    /* 配置终端显示设置 */
	printf("\033[2J");			 // 清屏
    system("stty -icanon");      // 关闭缓冲
    system("stty -echo");        // 关闭回显
    fprintf(stdout,"\033[?25l"); // 隐藏光标
	fflush(stdout);				 // 刷新

    /* 初始化游戏数据 */
    for (y = 0; y < NR; y++)
    {
    
        for (x = 0; x < NR; x++)
		{
    
		    data[y][x] = 0;		 // 所有数据清零
		}
    }

    /* 游戏上随机产生两个数 */
    srand(time(NULL));           // 设置随机数种子
    getRand(data);
    getRand(data);
}

/*
 * 函数名:showGame
 * 功能:显示游戏界面
 * 参数:需要显示的游戏数据
 * 返回值:无
*/
void showGame(int data[NR][NR])
{
    
    int x, y;

	/* 打印游戏提示信息 */
	fprintf(stdout, "\033[1;1H");	// 定位到第一行第一列
    fprintf(stdout, "欢迎来到\033[32m2048\033[0m游戏!");
    fprintf(stdout, "(\033[33m按'q'退出游戏)\033[0m\n");

	/* 打印所有数据 */
    for (y = 0; y < NR; y++)
    {
    
        for (x = 0; x < NR; x++)
		{
    
	    	if (data[y][x] == 0)
	    	{
    
	        	fprintf(stdout, "%5d", data[y][x]);
	    	}
	    	else
	    	{
    
				switch (data[y][x])	// 根据不同数字打印不同颜色
				{
    
				case 2   : fprintf(stdout, "\033[32m%5d\033[0m", \
				    		       data[y][x]); break;
				case 4   : fprintf(stdout, "\033[32m%5d\033[0m", \
				    		       data[y][x]); break;
				case 8   : fprintf(stdout, "\033[32m%5d\033[0m", \
				    		       data[y][x]); break;
				case 16  : fprintf(stdout, "\033[35m%5d\033[0m", \
				    		       data[y][x]); break;
				case 32  : fprintf(stdout, "\033[35m%5d\033[0m", \
				    		       data[y][x]); break;
				case 64  : fprintf(stdout, "\033[33m%5d\033[0m", \
				    		       data[y][x]); break;
				case 128 : fprintf(stdout, "\033[33m%5d\033[0m", \
				    		       data[y][x]); break;
				case 256 : fprintf(stdout, "\033[34m%5d\033[0m", \
				    		       data[y][x]); break;
				case 512 : fprintf(stdout, "\033[34m%5d\033[0m", \
				    		       data[y][x]); break;
				case 1024: fprintf(stdout, "\033[34m%5d\033[0m", \
				    		       data[y][x]); break;
				case 2048: fprintf(stdout, "\033[36m%5d\033[0m", \
							       data[y][x]); break;
				}
	    	}
		}
		fputc('\n', stdout);
    }

    /* 打印游戏结果 */
	fprintf(stdout, "当前最高纪录:\033[1;35m%d\033[0m", maxScore(data));
    fflush(stdout);
}

/*
 * 函数名:moveUp
 * 功能:把游戏数据上移
 * 参数:需要上移的游戏数据
 * 返回值:无
*/
void moveUp(int data[NR][NR])
{
    
    int x, y;
    int idx;
    int isChange = 0;	// 可移动标记位

    /* 先累加 */
    for (x = 0; x <= NR-1; x++)
    {
    
        for (y = 0; y < NR-1; y++)
		{
    
		    if (data[y][x] == 0)
		    {
    
		        continue;
		    }
		    /* 查找是否可加,能加则加 */
		    for (idx = y+1; idx <= NR-1; idx++)
		    {
    
		        if (data[idx][x] == 0)
				{
    
				    continue;
				}
				else if (data[idx][x] != data[y][x])
				{
    
				    break;
				}
				else
				{
    
				    data[y][x]  += data[idx][x];
				    data[idx][x] = 0;
				    isChange = 1;
				    break;
				}
		    }
		}
    }

    /* 累加完后移动 */
    for (x = 0; x <= NR-1; x++)
    {
    
        for (y = 1; y <= NR-1; y++)
		{
    
		    if (data[y][x] == 0)
		    {
    
		        continue;
		    }

		    /* 寻找移动点 */
    	    idx = y-1;
		    while (data[idx][x] == 0 && idx >= 0)
		    {
    
		        idx--;
		    }

    	    if (data[idx+1][x] == 0)
		    {
    
		        data[idx+1][x] = data[y][x];
		        data[y][x] = 0;
		        isChange = 1;
		    }
		}
    }

	/* 成功移动之后需要随即产生一个数 */
    if (isChange == 1)
    {
    
        getRand(data);
    }
}

/*
 * 函数名:moveDown
 * 功能:把游戏数据下移
 * 参数:需要下移的游戏数据
 * 返回值:无
*/
void moveDown(int data[NR][NR])
{
    
    int x, y;
    int idx;
    int isChange = 0;   // 可移动标记位

    /* 先累加 */
    for (x = 0; x <= NR-1; x++)
    {
    
        for (y = NR-1; y >= 1; y--)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        continue;
    	    }
    	    /* 查找是否可加,能加则加 */
    	    for (idx = y-1; idx >= 0; idx--)
    	    {
    
    	        if (data[idx][x] == 0)
        		{
    
        		    continue;
        		}
        		else if (data[idx][x] != data[y][x])
        		{
    
        		    break;
        		}
        		else
        		{
    
        		    data[y][x]  += data[idx][x];
        		    data[idx][x] = 0;
        		    isChange = 1;
        		    break;
        		}
    	    }
    	}
    }
    /* 累加完后移动 */
    for (x = 0; x <= NR-1; x++)
    {
    
        for (y = NR-2; y >= 0; y--)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        continue;
    	    }
    	    /* 寻找移动点 */
    	    idx = y+1;
    	    while (data[idx][x] == 0 && idx <= NR-1)
    	    {
    
    	        idx++;
    	    }

    	    if (data[idx-1][x] == 0)
    	    {
    
        		data[idx-1][x] = data[y][x];
        		data[y][x] = 0;
        		isChange = 1;
    	    }
    	}
    }

    /* 成功移动之后需要随即产生一个数 */
    if (isChange == 1)
    {
    
        getRand(data);
    }
}

/*
 * 函数名:moveRight
 * 功能:把游戏数据右移
 * 参数:需要右移的游戏数据
 * 返回值:无
*/
void moveRight(int data[NR][NR])
{
    
    int x, y;
    int idx;
    int isChange = 0;   // 可移动标记位

    /* 先累加 */
    for (y = 0; y < NR; y++)
    {
    
        for (x = NR-1; x >= 0; x--)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        continue;
    	    }
    	    /* 查找是否可加,能加则加 */
    	    for (idx = x-1; idx >= 0; idx--)
    	    {
    
    	        if (data[y][idx] == 0)
        		{
    
        		    continue;
        		}
        		else if (data[y][idx] != data[y][x])
        		{
    
        		    break;
        		}
        		else
        		{
    
        		    data[y][x]  += data[y][x];
        		    data[y][idx] = 0;
        		    isChange = 1;
        		    break;
        		}
    	    }
    	}
    }
    /* 累加完后移动 */
    for (y = 0; y < NR; y++)
    {
    
        for (x = NR-2; x >= 0; x--)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        continue;
    	    }
    	    /* 寻找移动点 */
    	    idx = x+1;
    	    while (data[y][idx] == 0 && idx <= NR-1)
    	    {
    
    	        idx++;
    	    }

      	    if (data[y][idx-1] == 0)
    	    {
    
    	        data[y][idx-1] = data[y][x];
    	        data[y][x] = 0;
    	        isChange = 1;
    	    }
    	}
    }

    /* 成功移动之后需要随即产生一个数 */
    if (isChange == 1)
    {
    
        getRand(data);
    }
}

/*
 * 函数名:moveLeft
 * 功能:把游戏数据左移
 * 参数:需要左移的游戏数据
 * 返回值:无
*/
void moveLeft(int data[NR][NR])
{
    
    int x, y;
    int idx;
    int isChange = 0;   // 可移动标记位

    /* 先累加 */
    for (y = 0; y <= NR-1; y++)
    {
    
        for (x = 0; x < NR-1; x++)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        continue;
    	    }
    	    /* 查看是否可加,能加则加 */
    	    for (idx = x+1; idx <= NR-1; idx++)
    	    {
    
    	        if (data[y][idx] == 0)
        		{
    
        		    continue;
        		}
        		else if (data[y][idx] != data[y][x])
        		{
    
        		    break;
        		}
        		else
        		{
    
        		    data[y][x]  += data[y][idx];
        		    data[y][idx] = 0;
        		    isChange = 1;
        		    break;
        		}
    	    }
    	}
    }
    /* 累加完后移动 */
    for (y = 0; y <= NR-1; y++)
    {
    
        for (x = 1; x <= NR-1; x++)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        continue;
    	    }
    	    /* 寻找移动点 */
    	    idx = x-1;
    	    while (data[y][idx] == 0 && idx >= 0)
    	    {
    
    	        idx--;
    	    }

    	    if (data[y][idx+1] == 0)
    	    {
    
    	        data[y][idx+1] = data[y][x];
    	        data[y][x] = 0;
    	        isChange = 1;
    	    }
    	}
    }

    /* 成功移动之后需要随即产生一个数 */
    if (isChange == 1)
    {
    
        getRand(data);
    }
}
/*
 * 函数名:checkGameOver
 * 功能:检查游戏是否结束
 * 参数:需要检查的游戏数据
 * 返回值:游戏结束返回1,没有结束返回0
*/
int checkGameOver(int data[NR][NR])
{
    
    int x, y;

    /* 判断有无空位0 */
    for (y = 0; y < NR; y++)
    {
    
        for (x = 0; x < NR; x++)
    	{
    
    	    if (data[y][x] == 0)
    	    {
    
    	        return 0;
    	    }
    	}
    }
    /* 判断各行是否可加 */
    for (y = 0; y < NR; y++)
    {
    
        for (x = 0; x < NR-1; x++)
    	{
    
    	    if (data[y][x] == data[y][x+1])
    	    {
    
    	        return 0;
    	    }
    	}
    }
    /* 判断各列是否可加 */
    for (x = 0; x < NR; x++)
    {
    
        for (y = 0; y < NR-1; y++)
    	{
    
    	    if (data[y][x] == data[y+1][x])
    	    {
    
    	       return 0;
    	    }
    	}
    }
    /* 游戏结束 */
    return 1;
}

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

    ch = fgetc(stdin);

    if (ch == '\033' && fgetc(stdin) == '[')    // 方向键
    {
    
        ch = fgetc(stdin);
    	switch (ch)
    	{
    
    	    case 'A': key = UP;     break;
    	    case 'B': key = DOWN;   break;
    	    case 'C': key = RIGHT;  break;
    	    case 'D': key = LEFT;   break;
    	}
    }
    else if (ch == 'q') // 退出键
	   key = EXIT;
    else                // 其它键
	   key = UNKNOW;

    return key;
}

/*
 * 函数名:getRand
 * 功能:在游戏数据中产生一个随机数,2的概率为80%,4的概率为20%
 * 参数:需要产生随机数的游戏数据
 * 返回值:无
*/
void getRand(int data[NR][NR])
{
    
    int x, y;
    int idx;  /* 随机种子索引 */
    int seedLib[] = {
    2,2,2,4,2,2,2,4,2,2};

    /* 全局查找是否有空位产生随机数 */
    while (1)
    {
    
        /* 随即定位 */
        x = rand() % NR;
        y = rand() % NR;

        /* 定位的位置有数了 */
        if (data[y][x] != 0)
    	    continue;

        /* 空白位置产生数 */
        idx = rand() % 10;
    	data[y][x] = seedLib[idx];

        return;
    }
}

/*
 * 函数名:maxScore
 * 功能:获取游戏最高分
 * 参数:需要获取最高分的游戏数据
 * 返回值:返回游戏数据中的最大值
*/
int maxScore(int data[NR][NR])
{
    
    int x, y;
    int maxValue = 0;

    for (y = 0; y < NR; y++)
    {
    
        for (x = 0; x < NR; x++)
    	{
    
    	    if (maxValue < data[y][x])
    	    {
    
    	        maxValue = data[y][x];
    	    }
    	}
    }

    return maxValue;
}

/*
 * 函数名:exitGame
 * 功能:配置退出游戏前的一些终端配置
 * 参数:无
 * 返回值:无
*/
void exitGame(void)
{
    
    system("stty icanon");          // 恢复缓冲
    system("stty echo");            // 恢复回显
    fprintf(stdout, "\033[?25h");   // 显示鼠标
    fputc('\n', stdout);            // 让终端提示符下一行显示
    exit(0);                        // 退出游戏
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/aiwangtingyun/article/details/79654242

智能推荐

python简易爬虫v1.0-程序员宅基地

文章浏览阅读1.8k次,点赞4次,收藏6次。python简易爬虫v1.0作者:William Ma (the_CoderWM)进阶python的首秀,大部分童鞋肯定是做个简单的爬虫吧,众所周知,爬虫需要各种各样的第三方库,例如scrapy, bs4, requests, urllib3等等。此处,我们先从最简单的爬虫开始。首先,我们需要安装两个第三方库:requests和bs4。在cmd中输入以下代码:pip install requestspip install bs4等安装成功后,就可以进入pycharm来写爬虫了。爬

安装flask后vim出现:error detected while processing /home/zww/.vim/ftplugin/python/pyflakes.vim:line 28_freetorn.vim-程序员宅基地

文章浏览阅读2.6k次。解决方法:解决方法可以去github重新下载一个pyflakes.vim。执行如下命令git clone --recursive git://github.com/kevinw/pyflakes-vim.git然后进入git克降目录,./pyflakes-vim/ftplugin,通过如下命令将python目录下的所有文件复制到~/.vim/ftplugin目录下即可。cp -R ...._freetorn.vim

HIT CSAPP大作业:程序人生—Hello‘s P2P-程序员宅基地

文章浏览阅读210次,点赞7次,收藏3次。本文简述了hello.c源程序的预处理、编译、汇编、链接和运行的主要过程,以及hello程序的进程管理、存储管理与I/O管理,通过hello.c这一程序周期的描述,对程序的编译、加载、运行有了初步的了解。_hit csapp

18个顶级人工智能平台-程序员宅基地

文章浏览阅读1w次,点赞2次,收藏27次。来源:机器人小妹  很多时候企业拥有重复,乏味且困难的工作流程,这些流程往往会减慢生产速度并增加运营成本。为了降低生产成本,企业别无选择,只能自动化某些功能以降低生产成本。  通过数字化..._人工智能平台

electron热加载_electron-reloader-程序员宅基地

文章浏览阅读2.2k次。热加载能够在每次保存修改的代码后自动刷新 electron 应用界面,而不必每次去手动操作重新运行,这极大的提升了开发效率。安装 electron 热加载插件热加载虽然很方便,但是不是每个 electron 项目必须的,所以想要舒服的开发 electron 就只能给 electron 项目单独的安装热加载插件[electron-reloader]:// 在项目的根目录下安装 electron-reloader,国内建议使用 cnpm 代替 npmnpm install electron-relo._electron-reloader

android 11.0 去掉recovery模式UI页面的选项_android recovery 删除 部分菜单-程序员宅基地

文章浏览阅读942次。在11.0 进行定制化开发,会根据需要去掉recovery模式的一些选项 就是在device.cpp去掉一些选项就可以了。_android recovery 删除 部分菜单

随便推点

echart省会流向图(物流运输、地图)_java+echart地图+物流跟踪-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏6次。继续上次的echart博客,由于省会流向图是从echart画廊中直接取来的。所以直接上代码<!DOCTYPE html><html><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /&_java+echart地图+物流跟踪

Ceph源码解析:读写流程_ceph 发送数据到其他副本的源码-程序员宅基地

文章浏览阅读1.4k次。一、OSD模块简介1.1 消息封装:在OSD上发送和接收信息。cluster_messenger -与其它OSDs和monitors沟通client_messenger -与客户端沟通1.2 消息调度:Dispatcher类,主要负责消息分类1.3 工作队列:1.3.1 OpWQ: 处理ops(从客户端)和sub ops(从其他的OSD)。运行在op_tp线程池。1...._ceph 发送数据到其他副本的源码

进程调度(一)——FIFO算法_进程调度fifo算法代码-程序员宅基地

文章浏览阅读7.9k次,点赞3次,收藏22次。一 定义这是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO 算法并不能保证这些页面不被淘汰。这里,我_进程调度fifo算法代码

mysql rownum写法_mysql应用之类似oracle rownum写法-程序员宅基地

文章浏览阅读133次。rownum是oracle才有的写法,rownum在oracle中可以用于取第一条数据,或者批量写数据时限定批量写的数量等mysql取第一条数据写法SELECT * FROM t order by id LIMIT 1;oracle取第一条数据写法SELECT * FROM t where rownum =1 order by id;ok,上面是mysql和oracle取第一条数据的写法对比,不过..._mysql 替换@rownum的写法

eclipse安装教程_ecjelm-程序员宅基地

文章浏览阅读790次,点赞3次,收藏4次。官网下载下载链接:http://www.eclipse.org/downloads/点击Download下载完成后双击运行我选择第2个,看自己需要(我选择企业级应用,如果只是单纯学习java选第一个就行)进入下一步后选择jre和安装路径修改jvm/jre的时候也可以选择本地的(点后面的文件夹进去),但是我们没有11版本的,所以还是用他的吧选择接受安装中安装过程中如果有其他界面弹出就点accept就行..._ecjelm

Linux常用网络命令_ifconfig 删除vlan-程序员宅基地

文章浏览阅读245次。原文链接:https://linux.cn/article-7801-1.htmlifconfigping &lt;IP地址&gt;:发送ICMP echo消息到某个主机traceroute &lt;IP地址&gt;:用于跟踪IP包的路由路由:netstat -r: 打印路由表route add :添加静态路由路径routed:控制动态路由的BSD守护程序。运行RIP路由协议gat..._ifconfig 删除vlan