C语言版---2048小游戏,简单易懂,看过就会

一.废话不多说,直接上代码。

(不懂游戏规则,可以自行查阅^_^)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

#define N 4         //定义阶数
#define up 72       //键盘上键
#define down 80     //键盘下键
#define left 75     //键盘左键
#define right 77    //键盘右键
#define esc 27      //键盘esc键

//产生n个随机数
void Random(int *data, int n)
{
    for(int i = 0; i<n; i++)
    {
        data[i] = rand()%N;
    }
}

//初始化游戏,随机生成两个数字方块
void initGame(int data[N][N], int num)
{
    int a[4];

    while(1)
    {
        Random(a, 4);  //产生两个坐标

        //如果坐标不重合,就退出
        if(a[0] != a[2] && a[1] != a[3])
        {
            data[a[0]][a[1]] = num;
            data[a[2]][a[3]] = num;
            break;
        }
    }
}

//判断是否还有空位
int checkSpace(int data[N][N])
{
    int i, j, n;

    n = 0;
    for(i = 0; i<N; i++)
    {
        for(j = 0; j<N; j++)
        {
            if(!data[i][j])     //还有空位
            {
                n++;
            }
        }
    }

    return n;
}

//选择一个空坐标,生成数字
void produceXY(int data[N][N], int num)
{
    int i, j, flag, a[2];
    
    flag = checkSpace(data);

    if(!flag)   //如果没有空位就不用产生数字
    {
        return;
    }

    while(1)    //一定可以找到空位并产生数字
    {
        flag = 0;

        Random(a, 2);   //随机产生一个坐标
        for(i = 0; i<N; i++)
        {
            for(j = 0; j<N; j++)    //尝试找出一个空坐标
            {
                if(data[i][j] == 0 && a[0] == i && a[1] == j)
                {
                    data[a[0]][a[1]] = num; //成功找到空坐标
                    return;
                }
            }
        }
    }
}

//有空位就向左移动
void pushLeft(int data[N])
{
    int i, j = 0;
	for (i = 0; i < N; i++) {
		if (data[i] != 0) {
			data[j] = data[i];  //每个位都要判断一次
			j++;
		}
	}

    //将左移后的产生的新空位标识出来
    for(;j<N;j++)
    {
        data[j] = 0;
    }
}

//复制二维数组,方便旋转
void copyData(int data[N][N], int tmp[N][N])
{
    int i, j;

    for(i = 0; i<N; i++)
    {
        for(j = 0; j<N; j++)
        {
            tmp[i][j] = data[i][j];
        }
    }
}

//将游戏界面顺时针旋转90°*count, count表示旋转次数
void rotateMatrix(int data[N][N], int count)
{
    int i, j, tmp[N][N];

    while(count)
    {
        copyData(data, tmp);

        for(i = 0; i<N; i++)
        {
            for(j = 0; j<N; j++)
            {
                data[i][j] = tmp[N-j-1][i];
            }
        }
        count--;
    }
}

//向左合并
int mergeLeft(int data[N][N])
{
    int i, j, flag;

    flag = 0;   //假设不能向左合并
    for(i = 0; i<N; i++)
    {
        pushLeft(data[i]);
        for(j = 0; j<N-1; j++)
        {
            if(data[i][j] == data[i][j+1])  //找到相等的就左右合并
            {
                flag = 1;   //有合并过就可以继续游戏
                data[i][j] += data[i][j];
                data[i][j+1] = 0;   //标记空位
                pushLeft(data[i]);  //重新有空位就左移
            }
        }
    }

   return flag;    //返回标记值
}

//向右合并
int mergeRight(int data[N][N])
{
    int flag = 0;
    rotateMatrix(data, 2);    //将游戏界面先顺时针旋转180°

    flag = mergeLeft(data);   //向左合并

    rotateMatrix(data, 2);    //还原游戏界面,顺时针旋转180°

    return flag;
}

//向上合并
int mergeUp(int data[N][N])
{
    int flag = 0;
    rotateMatrix(data, 3);    //将游戏界面先顺时针旋转270°

    flag = mergeLeft(data);   //向左合并

    rotateMatrix(data, 1);    //还原游戏界面,顺时针旋转90°

    return flag;
}

//向下合并
int mergeDown(int data[N][N])
{
    int flag = 0;
    rotateMatrix(data, 1);    //将游戏界面先顺时针旋转90°

    flag = mergeLeft(data);   //向左合并

    rotateMatrix(data, 3);    //还原游戏界面,顺时针旋转270°

    return flag;
}

//检查是否能继续游戏
int check(int data[N][N])
{
    int i, j, n;

    for(i = 0; i<N; i++)
    {
        for(j = 0; j<N; j++)
        {
            if(data[i][j] == 2048)  //如果等于2048, 游戏胜利
            {
                return 2;
            }
        }
    }
    n = checkSpace(data);

    if(n)   //如果还有空位就可以继续游戏
    {
        return 1;
    }

    n = 0;  //如果没有空位, 但是还可以合并, 也可以继续游戏
    for(i = 0; i<N; i++)
    {
        for(j = 0; j<N-1; j++)
        {
            if(data[i][j]==data[i][j+1] || data[j][i] == data[j+1][i])
            {
                n++;    //存在左右数字相等或者上下数字相等情况,可以继续游戏
            }
        }
    }

    if(n)
    {
        return 1;
    }

    return 0;
}

//显示游戏界面
void show(int data[N][N])
{
    printf("\n\t2048小游戏");

	for (int i = 0; i < N; i++) {
        printf("\n|______|______|______|______|\n|");
		for (int j = 0; j < N; j++) {
			if (data[i][j] == 0) 
            {
				printf("%5c |", ' ');
			} 
            else 
            {
				printf("%5d |", data[i][j]);
			}
        }
	}
    printf("\n|______|______|______|______|\n");
}

int main(void)
{
    char ch;
    int win;
    srand(time(0));

    int data[N][N] = {0};   //游戏界面初始数据   
    
    initGame(data, 2);
   
    while(1)
    {
        show(data);

        win = check(data);   //检查能否继续游戏
        
        if(win == 0)
        {
            printf("游戏结束\n");
            return 0;
        }
        if(win == 2)
        {
            printf("恭喜你,胜利了\n");
            return 0;
        }
        
        ch = getch();   //获取一个字符,不回显

        switch(ch)
        {
            case up:    //上移
                mergeUp(data);
                produceXY(data, 2);
                break;

            case down:  //下移
                mergeDown(data);
                produceXY(data, 2);
                break;

            case left:  //左移
                mergeLeft(data);
                produceXY(data, 2);
                break;

            case right: //右移
                mergeRight(data);
                produceXY(data, 2);
                break;

            case esc:
                printf("2048小游戏不是这么简单的哦\n");
                return 0;
        }

        system("cls");  //每次按键后,清屏,然后显示新的游戏界面
    }

    return 0;
}

游戏截图

 

二.代码分析

        相信认真看完我的代码注释的小伙伴,一定感慨,老夫活这么久,查阅代码无数,从未见有如此注释简单,清晰的代码(说笑^_^),之所以写这么详细,是因为查代码的小伙伴很大程度上不会写,或者自己写的时候出bug,不知道哪里错了,想看看人家的,但是网上一找,发现,全部在讲原理,或者代码根本没有什么关键的注释,看完以后,一头雾水。所有博主特意写下这篇博客,来赠予友猿人。

        这里重点介绍一下,合并的方法。可以看到,我的代码非常简洁,把右移,上移和下移操作等全部转化为左移操作了,节省了不少(这里采用了数学的转化与化归思想和空间想象力)。右移,相当于先把游戏界面顺时针旋转180°,然后左移,然后再把游戏界面顺时针旋转180°,还原。上移和下移是同样的逻辑,当然也可以逆时针旋转,这里不展开。

        所以关键步骤变化为,如何左移。首先左移可以分解为两个步骤。第一步,每一行整体左移,遇到空位(这里用数字0表示),直接跳过,直到碰到非零数字,最终结果是,每一行的所有数字都紧紧挨一起,留下几个空位或者这行全空(都是数字0)。第二步,合并左右相同的数字,当合并了一个数字后,要重新整体左移,因为刚才合并时,右边的数字已经没有意义了,需要被下一个数字覆盖,所有要整体左移。循环这步,直到不能合并结束(找不到左右相同的数字)。

        其他的部分,应该都挺好理解的,代码也有注释,这里就不一一讲解了。对了,这里用到了一些特殊函数,system("cls"),用来清屏的,类似Linux下在终端输入clear。还有一个getch(),是用来获取一个字符,并且不回显的(头文件conio.h),当然你回显也没有问题,因为每次移动后都清屏,然后重新显示游戏界面。

三.总结

        本次游戏,在算法方面每怎么优化,小伙伴可以自行尝试一下。好了,本期的小游戏分享就到这里了,以后会不定期更新,更多好玩的,可以在左边的专栏里慢慢玩,毕竟可以白嫖(^_^)。

        

        

  • 13
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值