ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[cpp项目]2048

2021-03-01 15:03:55  阅读:185  来源: 互联网

标签:lastValue 格子 项目 ++ 2048 int cpp data 输入


当前时间:2021/3/1

项目网址:https://www.nowcoder.com/project/index/8

源码及教程(readme):https://git.nowcoder.com/68/2048 

起步

安装依赖

# 依赖库
- linux: apt-get install libncurses5-dev
- Mac: brew install ncurses
- Windows:
    1. 安装编译器MinGW,https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/ 下载mingw-w64-install.exe(注意是exe,擅用ctrl+F) 5.0.4版本,解压到本地目录,例如 C:\mingw64,然后把C:\mingw64\bin 加入到系统设置的路径里,打开命令行控制台输入g++,确认有这个命令以保证安装是成功
    2. 编译pdcurses库,https://sourceforge.net/projects/pdcurses/files/pdcurses/3.6/pdcurs36.zip/download 下载pdcurses后解压到C:\pdcurs36目录,命令行控制台cd到 C:\pdcurs36\wincon目录,运行 mingw32-make 命令编译pdcurses库,编译成功后目录下有多个demo的exe文件以及一个pdcurses.a文件,这个文件是库文件。

安装MinGW:首先确定下载的是exe,双击通过在线安装时,version选择5.1.0,架构选择x86-64,threads选择win32。安装完成后在cmd中输入gcc -v产生如下输出即可(输入g++后显示 fatal error: no input files,这是正确的,它在提示你没有给 g++ 命令一个输入)

下载pdcurses库并编译:没啥坑,找准目录即可,编译时cmd中节取如下:

运行

在【源码及教程(readme):https://git.nowcoder.com/68/2048】下载代码,在2048.cpp运行下述命令

g++ 2048.cpp C:\pdcurs36\wincon\pdcurses.a -I C:\pdcurs36\ -o 2048

编译成功后目录下出现2048.exe,双击后按R即可游戏

代码结构

仅一个cpp文件,300行的代码,其中一个类、两个函数、一个main函数

  • class:Game2048
  • 初始化函数 initialize()
  • 关闭 shutdown()
  • main()
    • initialize()  // 设置图形交互界面
    • 初始化class: Game2048
      • public:
        • processInput()  //处理按键
        • draw()  // 绘制界面
        • setTestData()  // 设置测试数据
      • private:
        • isOver()  // 判断游戏结束
        • moveLeft()  // 向左边移动
        • rotate()  // 矩阵逆时针旋转90°
        • restart()  // 重新开始
        • randNew()  // 随机产生一个新的数字
        • drawItem()  // 左上角为原点,在指定位置绘制 char 字符
        • drawNum()  // 在指定位置绘制 int 字符
    • 当 S_QUIT != game.getStatus() 时,循环 game.draw();  game.processInput();
    • 结束shutdown()

项目运行样例

实现细节分析

想象一下2048的交互,无非以下几点:

  1. 读取用户输入,此输入仅有四种:上 下 左 右,其他输入视为无效
  2. 输入后,将数字按照输入方向移动到尽可能远的位置
  3. 当两个数字重复时合二为一
  4. 每次输入后在一个随机位置出现一个新的数字(数值不会太大)
  5. 作为一个游戏,应该有开始、结束,以及图形交互界面

下面将功能点和代码实现对应,如下:

1.读取用户输入

4.每次输入后在一个随机位置出现一个新的数字(数值不会太大)

class2048 - public - void processInput():

这里的要点有二:

  • 只定义了左移动一个操作(函数),上移、下移、右移的实现依赖左移动和旋转矩阵实现
  • 定义FLAG:updated,初始化为false;其接收 移动操作 的返回值,为true时 生成新的随机数 并 判断游戏是否结束
    // 处理按键
    void processInput() {
        char ch = getch();
        // 转化成大写
        if (ch >= 'a' && ch <= 'z') {
            ch -= 32;
        }

        if (status == S_NORMAL) {
            bool updated = false;
            if (ch == 'A') {
                updated = moveLeft();
            } else if (ch == 'S') {
                // 向下移动 = 旋转270度,向左移动,再旋转90度
                rotate();
                rotate();
                rotate();
                updated = moveLeft();
                rotate();
            } else if (ch == 'D') {
                rotate();
                rotate();
                updated = moveLeft();
                rotate();
                rotate();
            } else if (ch == 'W') {
                rotate();
                updated = moveLeft();
                rotate();
                rotate();
                rotate();
            }

            if (updated) {
                randNew();
                if (isOver()) {
                    status = S_FAIL;
                }
            }
        }

        if (ch == 'Q') {
            status = S_QUIT;
        } else if (ch == 'R') {
            restart();
        }
    }

 此外还需注意,上面代码在盘面发生变化后调用了randNew(),实现在随机位置出现新数字:

    // 随机产生一个新的数字
    bool randNew() {
        vector<int> emptyPos;
        // 把空位置先存起来
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                if (data[i][j] == 0) {
                    emptyPos.push_back(i * N + j);
                }
            }
        }

        if (emptyPos.size() == 0) {
            return false;
        }

        // 随机找个空位置
        int value = emptyPos[rand() % emptyPos.size()];
        // 10%的概率产生4
        data[value / N][value % N] = rand() % 10 == 1 ? 4 : 2;
        return true;
    }

2.输入后,将数字按照输入方向移动到尽可能远的位置

3.当两个数字重复时合二为一

class2048 - public - bool moveLeft():

这里可以说是本项目的算法核心,需要完成的功能有以下几点

  • 将数字左移到尽可能远的位置
  • 若与左侧数字相同则将其合并
  • 生成一个新的随机数字并加到矩阵中的随机位置

看一下代码,使用逐行计算,每一行从左到右遍历,其中维持两个变量:

  • currentWritePos:待写格子
  • lastValue:记录每一行的第一个与左值不同的值,需要注意的是,修改后的格子里的值是根据这个变量计算的

记录第一个非零值到 lastValue,若第二个非零值与lastValue相同则进行合并处理,若不同则仅更新lastValue

currentWritePos值得思考,因为我们遍历过程中的“当前”格子的标号用 j 表示,与“待写”格子是相区别的:

当当前格子为空时,不改变待写格子的位置;当当前格子非空时,将其数值加入lastvalue,不改变待写格子位置;当lastvalue非空、当前格子非空,判断两者数值是否相同,编辑待写格子内容并右移待写格子位置。

遍历过一行后,需要判断一下lastvalue是否有值,若有则需要将其填如待写

遍历完一个表格后,判断修改后的矩阵(data)和修改前的矩阵(tmp)是否相同,若不同(即发生了变化)则返回true,否则返回false

    // 向左边移动, 返回值表示盘面是否有发生变化
    bool moveLeft() {
        int tmp[N][N];
        for (int i = 0; i < N; ++i) {
            // 逐行处理
            // 如果两个数字一样,当前可写入的位置
            int currentWritePos = 0;
            int lastValue = 0;
            for (int j = 0; j < N; ++j) {
                tmp[i][j] = data[i][j];

                if (data[i][j] == 0) {
                    continue;
                }

                if (lastValue == 0) {
                    lastValue = data[i][j];
                } else {
                    if (lastValue == data[i][j]) {
                        data[i][currentWritePos] = lastValue * 2;
                        lastValue = 0;
                        if (data[i][currentWritePos] == TARGET) {
                            status = S_WIN;
                        }
                    } else {
                        data[i][currentWritePos] = lastValue;
                        lastValue = data[i][j];
                    }
                    ++currentWritePos;
                }
                data[i][j] = 0;
            }
            if (lastValue != 0) {
                data[i][currentWritePos] = lastValue;
            }
        }


        // 看看是否发生了变化
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                if (data[i][j] != tmp[i][j]) return true;
            }

        }
        return false;
    }

5.作为一个游戏,应该有开始、结束,以及图形交互界面

逻辑呈现在main中,设置了表示游戏状态的标记位

// 游戏状态
#define S_FAIL 0
#define S_WIN 1
#define S_NORMAL 2
#define S_QUIT 3

每次处理用户输入后判断程序状态是否为 S_QUIT,若是则退出循环。

循环中执行draw绘图以及处理用户输入。

 

14:51完成

 

 

 

 

 

 

标签:lastValue,格子,项目,++,2048,int,cpp,data,输入
来源: https://blog.csdn.net/m0_37495408/article/details/114252361

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有