这是笔者用作练习C++的一个小项目,框架思路和程序上很大程度借鉴了牛客网-项目实战-2048小游戏,并在此基础上进行了功能上的拓展,增加了记录历史最高成绩和当前玩家得分两个功能。下面进行介绍

码云:https://gitee.com/hinzer/my-notes-of-C_plus/tree/master/project/

项目界面

我的系统环境是CentOS7.x,编译运行项目前,需要安装依赖

sudo yum install libncurses5-dev

编译命令

 g++ demo.cpp -l ncurses -o 2048

一、问题描述

对于整个项目,从IPO(输入-处理-输出)的角度来看这个游戏
1.输入:用户按按键
2.处理规则:
    一次只能合并相邻的两个数字,例如 [2 2 2 2] ,向右合并以后是 [空 空 4 4] ,不是 [空 空 空 8]
    每次合并的时候,合并方向优先级高,例如 [空 2 2 2],向右合并以后是 [空 空 2 4],不是 [空 空 4 2]
    判断游戏胜利或者失败
    每次合并以后随机新出4的概率10%
3.输出:数字元素的动态变化和用户提示语句形成的图形界面

二、问题分析

1.随机产生两个数字元素在这个"方阵"上,通过按键的上下左右来控制数字的移动,实现数字的合并。因此数字特点是离散的、和二维的。
2.每次按下按键的时刻和键值是不确定的,将按键按下视为一个事件,然后程序再进行判断。因此是事件驱动模型。
3.事件的主要信息是,按键按下产生的键值和之后对二维数字方阵的结果。因此用二维数组保存最终的二维数据。
4.游戏主要是根据二维数组或者玩家的动作(按下按键)来判断游戏玩家的状态。因此用变量来记录玩家的状态。

综上,定义一个类Game2048来描述这个游戏,通过变量status和二维数组data来表示这个游戏的属性,再有就是类的方法表示游戏的动作,比如绘制图形界面相关方法、重启初始化方法、上下左右移动方法、判断游戏结束方法。

三、开发步骤

Step1.引入curses库 终端内绘制简单的图形用户界面,这里先让其对窗口进行一系列的初始化。

Step2.绘制游戏界面 将界面框架绘制好,具体在实现draw方法的时候,利用循环结构填充字符框架,除了布局整洁之外,还要注意保持合适的间距,后面用来填充数字元素。

Step3.游戏状态切换 实现processInput方法,捕获用户按下按键的键值,能够判断QRWASD几个字母,后面会实现具体的逻辑功能。

Step4.重启初始化游戏实现restart方法,当用户按下R键时,清空界面并随机生成两个数值元素作为初始元素。在随机生成数值元素的处理程序中,先定义一个vector容器,存储当前数组中的空元素的位置(将二维坐标转换成一维坐标值表示),然后在随机选择空位置上生成2or4的元素(使用rand()函数生成随机数,用时间作为随机数种子)。

Step5.向左移动 逐行遍历每一个元素data[i][j],将前一个元素设定为比较值compareValue,进行相邻元素之间的比较。如果相邻元素值相等,将结果当前要写入的位置currentWritePos,重新设定比较值compareValue并进行下一轮比较;如果相邻元素值不相等,就将比较值写回当前要写入的位置currentWritePos,并跟新比较值compareValue,进行下一次比较。直到这行遍历完成,将比较值再写回当前要写入的位置currentWritePos(这是为了保证右边的元素不会被"清除")

Step6.向其他方向上移动 这里用了一个颇为巧妙的方法,将数组逆时针翻转90度之后的新数组,再进行step5中的想左移动,相当于原数组向上移动。将移动后的结果再翻转回去,就有了向其他方向上的移动效果。

Step7.游戏判断胜负 任意地方出现2048数字,就算赢;如果矩阵被填满并且不能再移动,就算输。具体通过查询或相邻元素比较来判断。

四、拓展

记录历史最高分,并且显示当前玩家得分。

我这里通过读写文件的方式,完成玩家历史得分的显示和记录。每次有方向移动过程中,如果一对数字组合成功,就加10分。在图形界面刷新过程中,实时显示当前玩家分数为当前分数,和读取文件的分数为历史最高。判断玩家分数是否大于历史分数,是的话就跟新文件中记录的最大值。

五、完整程序

#include <string>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <curses.h>
#include <fstream>using namespace std;// 格子数
#define N 4
// 每个格子的字符长度
#define WIDTH 5
// 胜利条件
#define TARGET 2048// 游戏状态
#define S_FAIL 0
#define S_WIN 1
#define S_NORMAL 2
#define S_QUIT 3class Game2048
{
public:Game2048():status(S_NORMAL){myScore = 0;setTestData();}int getStatus(){return status;}//处理按键void processInput(){char ch = getch();bool updated = false;if( 'a' <= ch <= 'z' ){ch = ch -32;}if(ch == 'Q'){status = S_QUIT;}else if (ch == 'R'){restart();}else if (ch == 'A'){//向左updated = moveLeft();}else if (ch == 'W'){//向上rotate();updated = moveLeft();rotate();rotate();rotate();}else if (ch == 'D'){//向右rotate();rotate();updated = moveLeft();rotate();rotate();}else if (ch == 'S'){//向下rotate();rotate();rotate();updated = moveLeft();rotate();}if (updated){randNew();    //随机产生一个新元素if (isOver()){status = S_FAIL;}}}// 绘制游戏界面void draw() {// 清理屏幕clear();// 居中偏移const int offset = 12;for (int i = 0; i <= N; ++i) {for (int j = 0; j <= N; ++j) {// 相交点drawItem(i * 2, 1 + j * WIDTH + offset, '+');// 竖线if (i != N) {drawItem(i * 2 + 1, 1 + j * WIDTH + offset, '|');}// 横线for (int k = 1; j != N && k < WIDTH; ++k) {drawItem(i * 2, 1 + j * WIDTH + k + offset, '-');}// 每个格子里的数if (i != N && j != N) {drawNum(i * 2 + 1, (j + 1) * WIDTH + offset, data[i][j]);}}}drawString(1,offset-WIDTH,"highest:");drawNum(1,offset,readDateToFile("score.txt"));drawString(2,offset-WIDTH,"score:");drawNum(2,offset,myScore);// 提示文字mvprintw(2 * N + 2, (5 * (N - 4) - 1) / 2, "W(UP),S(DOWN),A(LEFT),D(RIGHT),R(RESTART),Q(QUIT)");mvprintw(2 * N + 3, 5 + 5 * (N - 4) / 2, "My Blog : https://blog.csdn.net/feit2417");if (status == S_WIN) {mvprintw( N, 5 * N / 2 - 1, " YOU WIN,PRESS R TO CONTINUE ");} else if (status == S_FAIL) {mvprintw( N, 5 * N / 2 - 1, " YOU LOSE,PRESS R TO CONTINUE ");}if (myScore > readDateToFile("score.txt")){writeDateToFile("score.txt",myScore);  //将分数记录到文件}}// 方便调试, 随时设置数据void setTestData() {for (int i = 0; i < N; ++i) {for (int j = 0; j < N; ++j) {data[i][j] = 16 << (i + j);// data[i][j] = 2 << (1 + rand() % 7);/*data[i][0] = 2;data[i][1] = 4;data[i][2] = 8;data[i][3] = 16;*/}}}private:// 判断游戏已经结束bool isOver() {for (int i = 0; i < N; ++i) {for (int j = 0; j < N; ++j) {// 有空位或者相邻有一样的都可以继续if ((j + 1 < N) && (data[i][j] * data[i][j+1] == 0 || data[i][j] == data[i][j+1])) return false;if ((i + 1 < N) && (data[i][j] * data[i+1][j] == 0 || data[i][j] == data[i+1][j])) return false;}}return true;}//向左移动bool moveLeft(){int tmp[N][N];    for (int i = 0; i < N; ++i){//逐行遍历int compareValue = 0;  //比较值int currentWritePos = 0;  //当前写入的位置for (int j = 0; j < N; ++j){tmp[i][j] = data[i][j];if (0 == data[i][j]){//元素为空,结束本次循环continue;}if (0 == compareValue){compareValue = data[i][j];    //设定比较值}else{if (compareValue == data[i][j]){//相邻元素值相等data[i][currentWritePos] = compareValue*2; //结果写入当前位置compareValue = 0;    //准备下一轮比较++currentWritePos;myScore += 10;   //跟新分数}else {data[i][currentWritePos] = compareValue;  //将比较值写回当前写入位置compareValue = data[i][j];                   //跟新比较值++currentWritePos;}}data[i][j] = 0;       //清除该位置的记录}if (0 != compareValue){//考虑最右边的元素,保证不会被"清除"data[i][currentWritePos] = compareValue;}}for (int i = 0; i <= N; ++i){//看看左移前后数组是否有变化for (int j = 0; j <= N; ++j){if (tmp[i][j] != data[i][j]) return true;}}return false;}// 矩阵逆时针旋转90度void rotate() {int tmp[N][N] = {0};for (int i = 0; i < N; ++i) {for (int j = 0; j < N; ++j) {tmp[i][j] = data[j][N - 1 - i];}}for (int i = 0; i < N; ++i) {for (int j = 0; j < N; ++j) {data[i][j] = tmp[i][j];}}}// 重新开始void restart() {for (int i = 0; i < N; ++i) {for (int j = 0; j < N; ++j) {data[i][j] = 0;}}randNew();randNew();status = S_NORMAL;}// 随机产生一个新的数字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%的概率产生4data[value / N][value % N] = rand() % 10 == 1 ? 4 : 2;return true;}// 左上角为(0,0),在指定的位置画一个字符void drawItem(int row, int col, char c) {move(row, col);addch(c);}//在指定位置写一个字符串void drawString(int row, int col, string str){int num = str.size();while (num--) {drawItem(row, col, str[num]);--col;}}// 游戏里的数字是右对齐,row, col是数字最后一位所在的位置void drawNum(int row, int col, int num) {while (num > 0) {drawItem(row, col, num % 10 + '0');num /= 10;--col;}}//写数值到文件void writeDateToFile(const char *path,int value){ofstream of(path);of << value;of.close();}//读文件中的数值int readDateToFile(const char *path){ifstream inf(path);int sb;inf>>sb;inf.close();return sb;}private:int myScore; //玩家得分int status;       //游戏状态int data[N][N];   //阵盘};void initialize()
{// ncurses初始化initscr();// 按键不需要输入回车直接交互cbreak();// 按键不显示noecho();// 隐藏光标curs_set(0);// 随机数srand(time(NULL));
}void shutdown() {// ncurses清理endwin();
}int main(int argc, char const *argv[])
{initialize();Game2048 game;do {game.draw();game.processInput();}while(S_QUIT != game.getStatus());shutdown();return 0;
}

【C++项目】2048益智小游戏相关推荐

  1. C/C++项目:4399小游戏黄金矿工编译教程

    <黄金矿工中文版>是一款休闲益智小游戏,游戏中你需要看准时机出钩子勾取金子或者砖石来获得金钱的累加,达到一定的金钱数才能够闯关成功,游戏十分经典,强烈推荐大家学会编写这款游戏,休闲时没网也 ...

  2. 《游戏开发》html5 益智小游戏-小熊吃星星

    游戏截图 项目结构 一共3个文件 分别为 index.html   script.js style.css index.html文件源码展示 <!DOCTYPE html> <htm ...

  3. c语言设置一个选择数字的程序,C语言编一个数字益智小游戏

    程序功能及运行情况 设计的程序是一个数字益智游戏,旨在培养小朋友玩家的数学思维,提高玩家的数学能力.游戏共设有四个不同的小游戏,分别是一位数四则运算.两位数四则运算.找最值游戏.排序游戏.程序能实现产 ...

  4. 搭建2048网页小游戏

    本文已发表于本人博客 哔哔哔哔-搭建2048网页小游戏 玩羊了个羊玩腻的话,可以试试2048 前言 仓库地址 https://github.com/gst-be/2048 本站演示站点 https:/ ...

  5. 益智小游戏《测试脑力》源码H5+安卓+IOS三端源码

    cocos creator2.4.2 益智小游戏<测试脑力>源码H5+安卓+IOS三端源码,开发脚本为typeScript方便扩展和阅读,支持cocos creator2.X版本,完整的源 ...

  6. C语言:数字益智小游戏

    程序功能及运行情况 设计的程序是一个数字益智游戏,旨在培养小朋友玩家的数学思维,提高玩家的数学能力.游戏共设有四个不同的小游戏,分别是一位数四则运算.两位数四则运算.找最值游戏.排序游戏.程序能实现产 ...

  7. java 五子棋项目_Java项目实现五子棋小游戏

    本文实例为大家分享了Java实现五子棋小游戏的具体代码,供大家参考,具体内容如下 项目名称 五子棋小游戏 项目描述 可以改变获胜棋子数,率先连成棋数的人获胜 代码实现 测试类 public class ...

  8. 【lvgl游戏开发】用lvgl写了一个数字对拼图益智小游戏

    文章目录 游戏说明 实现原理 演示效果 实现代码 游戏说明 单击方格用于显示数字.匹配两个数字,方格将显示从而显示图像. 实现原理 // TODO 演示效果 [lvgl游戏开发]用lvgl写了一个数字 ...

  9. 趣味益智小游戏 三子棋+五子棋 优化版(可任意选择棋盘大小)

    前言 今天牛牛给大家分享的是c语言实现三子棋和五子棋游戏,初学者可能有些不理解的地方,记得私信提问哦,牛牛会一 一回答的. 目录 前言 一.游戏介绍 二.游戏设计思路 2.1 主函数测试区(test. ...

最新文章

  1. Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.解决方案
  2. 802.1x------2
  3. 程序流程图_干货收藏 | Java 程序员必备的一些流程图
  4. 数据智能构建管理平台Dataphin V2.9.4.3版本发布
  5. python基础期末考试_python基础试题(4)
  6. .Net Core with 微服务 - Consul 注册中心
  7. 了解png 格式,绝对是让你PPT设计感瞬间爆棚的大杀器
  8. java死锁怎么用jvm调试_jvm 内存dump、gc查看、线程死锁,jmap、jstack、jstat
  9. windows下Tomcat安装及Eclipse配置教程
  10. linux 中文件的操作
  11. 深度卷积神经网络及各种改进
  12. 【教学类-20-02】20221203《世界杯16强国旗-定量版》(大班)
  13. unity3d 本地化数据PlayerPrefs详解
  14. Python+QT+Selenium制作在线视频播放器
  15. c语言的文件打开模式,c语言文件打开模式 - osc_b2jf5efr的个人空间 - OSCHINA - 中文开源技术交流社区...
  16. CSS | align-content 与 justify-content 的区别
  17. 大型多人在线角色扮演类网络游戏角色战斗系统的设计与实现
  18. 静态时序分析 第六章 串扰和噪声
  19. MessageSolution 企业邮件归档管理系统 EEA 存在信息泄露漏洞(CNVD-2021-10543)
  20. 施密特触发器基础知识

热门文章

  1. CentOS 7 的安装步骤
  2. 安恒堡垒机使用手册_齐治堡垒机简易使用手册
  3. 转载 | 纵览一季度 NFT 发展全景(上):发展历史与生态概述
  4. 使用腾讯IM实现uni-app小程序中的客服聊天
  5. SVN 具体某一行代码是谁添加的
  6. 硬核探厂|数字原生中压真空断路器生产揭秘
  7. 泌尿外科电子病历软件的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  8. STM32工程模板简单套用教程(Keil MDK)
  9. PostgreSQL官网下载
  10. hash值和history值详解区别