2048小游戏(变态版哦)

近日,由于博主同学暑假有个作业是写个2048小游戏,我一听挺好玩的。。然后就开始了。。

首先,2048在移动过程中的规则其实也没有特别难,但是感觉也不是一句话就能说完的。(不过玩的多……感觉总是有的)

废话不多说,其实博主同学提供了pdf描述了2048的算法。
这里写图片描述
各位筒子入坑前请先过几眼这个规则,以及其算法(当然我觉得算法第二点有点问题,后述)

那么在游戏的编写前,可以先对细枝末节做一些准备。
1.出现数字2/4的概率

int getRand()
{
    int i = rand() % 10;
    if (i < 1)
        return 4;
    else
        return 2;
}

2.移动后新数字产生的位置(当然也是随机数了)

int getRandAddr()//返回一个0-15的数
{
    return rand() % 16;
}

bool isSafeAddr(int addr)//由上一个函数产生的位置不一定安全,判断一下
{
    if (a[addr / 4][addr % 4] == 0)//当这个位置是0,也就是空才安全
        return true;
    return false;//都没安全当然不安全拉
}

3.判断2048游戏是否结束(就是你game over拉)

bool isEnd()
{
    for (int i = 0; i < 4; i++)//第一步,只要有空位,那就还没输
    {
        for (int j = 0; j < 4; j++)
        {
            if (a[i][j] == 0)
                return false;
        }
    }
    //第二步,当你满了之后,要判断上下左右相邻的是否还能合并
    for (int i = 0; i < 4; i++)//行
    {
        for (int j = 0; j < 3; j++)
        {
            if (a[i][j] == a[i][j + 1])
                return false;
        }
    }
    for (int i = 0; i < 4; i++)//列
    {
        for (int j = 0; j < 3; j++)
        {
            if (a[j][i] == a[j+1][i])
                return false;
        }
    }

    return true;
}

对了,顶上预编译指令和全局变量如下

#include<iostream>
#include<string>
#include<ctime>//给srand()预备
#include<conio.h>
using namespace std;

int a[4][4];//2048的数组
int b[4][4];//提供给它撤销功能(不过就提供了一步,感觉要很多步要用栈吧。
//PS:真正的2048哪有这个功能啊)
int score=0;//记录积分
int score_ex = 0;//为撤销而存在

好了,下面进入正文。
W/A/S/D的移动是2048的核心,我只展示一下W吧,反正其他类似,敲起来累死(方向变来变去,路痴的我要神经衰弱了)

void isW()//0往下靠拢
{
    for (int i = 0; i < 4; i++)//排0
    {
        //感觉这个和冒泡类似,不过只将0往下冒
        //这儿采用的是大数往上浮
        //最上面的一格不考虑
        if (a[1][i] != 0)//第二行
        {
            if (a[0][i] == 0)
            {
                a[0][i] = a[1][i];
                a[1][i] = 0;
            }
        }
        if (a[2][i] != 0)//第三行
        {
            for (int j = 0; j < 2; j++)
            {
                if (a[j][i] == 0)
                {
                    a[j][i] = a[2][i];
                    a[2][i] = 0;
                    break;
                    /*这儿要解释一下为什么找到一个就够,
                    而且从最上面开找。
                    假设1{0248},当2浮动后,0就会往下一位,由于我已经从第一行开写,所以不会存在有0存留在上面。
                     假设2{0024},这个假设说明了这个for循环要从0开始的原因,不然2只会和第二个0作交换,形成{0240}。*/
                }
            }
        }
        if (a[3][i] != 0)//第四行
        {
            for (int j = 0; j < 3; j++)
            {
                if (a[j][i] == 0)
                {
                    a[j][i] = a[3][i];
                    a[3][i] = 0;
                    break;
                }
            }
        }
    }
    //排0结束,下面开始合并并算积分
    for (int i = 0; i < 4; i++)
    {
        int flag = 3;
        //找出列中最后一个非零的
        for (int j = 3; j >= 0; j--)
        {
            if (a[j][i] != 0)
            {
                break;
            }
            flag--;
        }
        if (flag == 0)//只有第一行非零
            continue;//也就是只有一个数,根本不能合并,直接跑下一行
        if (flag == 1)
        {//有两个数,判断一下能否合并就好
            if (a[0][i] == a[1][i])
            {
                a[0][i] += a[0][i];//不用*2是为了省时,没去学移位,这儿移位最好吧
                a[1][i] = 0;//合并了就置0
                score += a[0][i];
                continue;
            }
            continue;
        }
        if (flag == 2)
        {
            if (a[0][i] == a[1][i])
            {
                a[0][i] += a[0][i];
                a[1][i] = a[2][i];//这儿要注意不能立马置0,而是把下一个数先移上来
                a[2][i] = 0;
                score += a[0][i];
                continue;
            }
            if (a[1][i] == a[2][i])
            {
                a[1][i] += a[1][i];
                a[2][i] = 0;
                score += a[1][i];
                continue;
            }
            continue;
        }
        /*if (flag == 2)
        //这儿是PDF中的算法,但是我认为它并不符合真正的2048,
        为了方便大家我再手打一遍算法描述2),
        每次按方向键后,逐行计算移动后的方格值的算法是:先将所有值为0的数移至行首。然后从行首开始逐一和后一个数比较,如果相等则合并这2个格子,合并过的方块不再继续合并;
        那么问题就来了假设原先是{0888}的转置,那么往上移动时,0往下沉,完成0的移动后应该为{8880}的转置,
        根据算法描述,0的方向是行首,那么从行首开始合并,结果应该是{81600},
        细心的小伙伴到这儿应该发现问题大了!这个规则不让人玩高分啊!!
        没发现的话,你就用注释的代码多玩玩,出了1024就神级了。。。
        {
            if (a[1][i] == a[2][i])
            {
                a[1][i] += a[1][i];
                a[2][i] = 0;
                score += a[1][i];
                continue;
            }
            else
            {
                if (a[0][i] == a[1][i])
                {
                    a[0][i] += a[0][i];
                    a[1][i] = a[2][i];
                    a[2][i] = 0;
                    score += a[0][i];
                    continue;
                }
            }
            continue;
        }*/
        if (flag == 3)
        {//这儿的逻辑可能会令人作呕,因为我是从原来的规则改过来的
            if (a[0][i] == a[1][i])
            {//判断12两行是否相同
                a[0][i] += a[0][i];
                a[1][i] = a[2][i];
                a[2][i] = a[3][i];
                a[3][i] = 0;
                score += a[0][i];
                //有必要进一步判断剩下的两行能否合并,例{2244},之所以后面不是a[2][i]与a[3][i]是因为上面刚做过移动
                if (a[1][i] == a[2][i])
                {
                    a[1][i] += a[1][i];
                    a[2][i] = 0;
                    score += a[1][i];
                }
                continue;//移动完就去下一行吧
            }
            else
            {
                if (a[1][i] == a[2][i])
                {//当前两行不同,就判断23两行是否相同
                    a[1][i] += a[1][i];
                    a[2][i] = a[3][i];
                    a[3][i] = 0;
                    score += a[1][i];
                    continue;
                }

                if (a[2][i] == a[3][i])
                {//跑到这儿就意味着1223不相同,判断34是否相同
                    a[2][i] += a[2][i];
                    a[3][i] = 0;
                    score += a[2][i];
                    continue;
                }
                continue;
            }
        }
        /*if (flag == 3)
        {
            if (a[2][i] == a[3][i])
            {
                a[2][i] += a[2][i];
                a[3][i] = 0;
                score += a[2][i];
                if (a[0][i] == a[1][i])
                {
                    a[0][i] += a[0][i];
                    a[1][i] = a[2][i];
                    a[2][i] = 0;
                    score += a[0][i];
                }
                continue;
            }
            else
            {
                if (a[1][i] == a[2][i])
                {
                    a[1][i] += a[1][i];
                    a[2][i] = a[3][i];
                    a[3][i] = 0;
                    score += a[1][i];
                    continue;
                }
                if (a[0][i] == a[1][i])
                {
                    a[0][i] += a[0][i];
                    a[1][i] = a[2][i];
                    a[2][i] = a[3][i];
                    a[3][1] = 0;
                    score += a[0][i];
                    continue;
                }
                continue;
            }
        }*/
    }
}

当当当,完成四个移动的函数还改了好几遍真是头昏脑涨啊。

接下来制定游戏规则部分,不过先初始化一下吧

void init()
{
    cout << "rule:\n\t1.w/s/a/d for direction\n\t2.r to restart \n\t3.u to 返回一步(不能连续用)\n\t4.e to exit\n";
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            a[i][j] = 0;
            b[i][j] = 0;
        }
    }
    score = 0;
    score_ex = 0;
    srand(time(NULL));//如果随机数种子不改一下,每次玩的都一样……
    int addr = getRandAddr();
    a[addr / 4][addr % 4] = getRand();

}

那么开始GAME吧

void game()
{
    string choose;
    cout << "choose level: 1-normal   2-hard  3-BT:";//选择难度吧,骚年~
    while (cin>>choose)
    {
        if (choose[0] == '1')
        {
            init();
            break;
        }
        if (choose[0] == '2')
        {
            init2();
            break;
        }
        if (choose[0] == '3')
        {
            init3();//哈哈变态版,添加了四个障碍
            break;
        }
    }
    while (1)
    {
        display();

        if (isEnd())//判断你是否还能移动
        {
            string end;
            cout << "\t\tGame over !\n";
            cout << "\t\tYour Score is:" << score << endl;
            cout << "input 'r' to restart other to exit.";
            cin >> end;
            if (end[0] == 'r')
            {
                system("cls");
                game();
            }
            else
            {
                exit(0);
            }
        }

        //cin >> choose;//原来用这个,每次输入w/a/s/d都要按下Enter
        choose[0] = _getch();//这个按了w就会直接移动了,详情百度conio.h _getch();
        if (choose[0] == 'u')
        {
            reWrite();//just one step ,就是将备份的数组写回进来
            system("cls");
            continue;
        }

        copyRight();//既然你都不撤回了,我就先将现在的保存一下
        switch (choose[0])
        {
            case 'w':isW(); break;//上下左右
            case 'a':isA(); break;
            case 's':isS(); break;
            case 'd':isD(); break;
            case 'e':exit(0);
            case 'r':{system("cls"); game(); break; }//一般人不会一直玩到栈溢出吧。。。
            default:
            {
                system("cls");
                cout << "Error input,Try angin.\n";
                continue;
            }
        }
        if (isSame())
        {//如果相同就不产生新数字了,算法里面没描述,玩过都知道~
            system("cls");//因为每次都会display()还是应该清除一下
            continue;
        }

        int addr = getRandAddr();
        while (!isSafeAddr(addr))//一直找到安全位置为止
        {
            addr = getRandAddr();
        }
        a[addr / 4][addr % 4] = getRand();//产生新数了。
        system("cls");

    }
}

下面这个函数都是在里面出现过,不是很重要而且没代码的

void init2()//这个只是一个障碍,想要4障碍自己写一下吧
{
    cout << "rule:\n\t1.w/s/a/d for direction\n\t2.r to restart \n\t3.u to 返回一步(不能连续用)\n\t4.e to exit\n";
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            a[i][j] = 0;
            b[i][j] = 0;
        }
    }
    score = 0;
    score_ex = 0;
    srand(time(NULL));
    int addr = getRandAddr();
    a[addr / 4][addr % 4] = getRand();
    addr = getRandAddr();
    while (!isSafeAddr(addr))
    {
        addr = getRandAddr();
    }

    a[addr / 4][addr % 4] = -1;
}


void display()//打印一下
{
    char D = char(127);//障碍物的样子,我也不知道用啥好
    for (int i = 0; i < 4; i++)
    {
        cout << "\t";
        for (int j = 0; j < 4; j++)
        {
            if (a[i][j] == 0)//每次都是0眼都花了
            {
                cout << ".\t";
                continue;
            }
            if (a[i][j] == -1 || a[i][j] == -2 || a[i][j] == -3 || a[i][j] == -4)//遇到障碍物
            {
                cout << D<<"\t";
                continue;
            }
            cout << a[i][j] << "\t";
        }
        cout << "\n\n";
    }
    cout << "score: " << score << "\n\n";
    cout << "\t" << "Next:";
}

这儿可能代码还不全,待会上传下载的,会在下面补上。
附上一张变态版的皂片吧(^__^) 嘻嘻……
这里写图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: "Red系列产品变态重口"这个说法并不准确。Red系列产品是指苹果公司推出的高端摄影和视频设备品牌Red Digital Cinema厂牌下的产品,包括专业摄影机、摄像机等。这些产品主要面向电影制片、广告制作和专业摄影领域,拥有高分辨率、色彩准确、高动态范围等优秀的性能特点。Red系列产品在拍摄电影、纪实片和其他高质量影像创作方面非常受欢迎。 "变态重口"这样的评价是不公正的,因为它们的专业属性决定了更高的拍摄质量需求,以满足电影制作行业的要求。 虽然Red系列产品价格昂贵,但是作为专业设备,它们具备卓越的拍摄能力和灵活性,可以满足电影制片人和专业摄影师的各种需求。同时,Red系列产品具备强大的色彩处理能力和图像质量,可以保证在后期制作过程中有更大的空间展现色彩饱和度、对比度等等。 因此,红系列产品在行业内享有很高的声誉,并被广大专业人士所认可。它们的出色性能使得创作者们能够更好地表达自己的创意和艺术追求。所以,正确的观点应该是Red系列产品是专业级别的高品质设备,而非"变态重口"。 ### 回答2: Red系列产品确实是以其特殊的设计风格而闻名,被认为有些“变态重口”。首先,Red系列产品的外观设计通常采用鲜艳的红色,这色调给人一种鲜明、夺目的感觉,与传统产品的设计截然不同,给人留下深刻的印象。其次,Red系列产品在功能方面也有些“变态重口”。比如,Red手机在性能方面远超市场上普通手机的水平,拥有高配置和强大的处理能力,在游戏、多媒体处理等方面表现出色。此外,Red系列产品搭载了世界上最大的手机电池,提供长久的续航能力,满足用户长时间使用的需求。再次,Red系列产品的创新设计使其在用户体验上更加突出。“变态”的一面体现在例如Red手机的虚化摄像头和边框的设计,以及屏幕指纹识别技术等,这些设计不仅满足用户的个性化需求,还带来了便利和创新体验。总的来说,Red系列产品确实在外观和功能上有些“变态重口”,但正是这种独特而狂热的设计风格,让Red系列产品脱颖而出,赢得了众多消费者的喜爱与追捧。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值