【C++项目】2048益智小游戏
这是笔者用作练习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益智小游戏相关推荐
- C/C++项目:4399小游戏黄金矿工编译教程
<黄金矿工中文版>是一款休闲益智小游戏,游戏中你需要看准时机出钩子勾取金子或者砖石来获得金钱的累加,达到一定的金钱数才能够闯关成功,游戏十分经典,强烈推荐大家学会编写这款游戏,休闲时没网也 ...
- 《游戏开发》html5 益智小游戏-小熊吃星星
游戏截图 项目结构 一共3个文件 分别为 index.html script.js style.css index.html文件源码展示 <!DOCTYPE html> <htm ...
- c语言设置一个选择数字的程序,C语言编一个数字益智小游戏
程序功能及运行情况 设计的程序是一个数字益智游戏,旨在培养小朋友玩家的数学思维,提高玩家的数学能力.游戏共设有四个不同的小游戏,分别是一位数四则运算.两位数四则运算.找最值游戏.排序游戏.程序能实现产 ...
- 搭建2048网页小游戏
本文已发表于本人博客 哔哔哔哔-搭建2048网页小游戏 玩羊了个羊玩腻的话,可以试试2048 前言 仓库地址 https://github.com/gst-be/2048 本站演示站点 https:/ ...
- 益智小游戏《测试脑力》源码H5+安卓+IOS三端源码
cocos creator2.4.2 益智小游戏<测试脑力>源码H5+安卓+IOS三端源码,开发脚本为typeScript方便扩展和阅读,支持cocos creator2.X版本,完整的源 ...
- C语言:数字益智小游戏
程序功能及运行情况 设计的程序是一个数字益智游戏,旨在培养小朋友玩家的数学思维,提高玩家的数学能力.游戏共设有四个不同的小游戏,分别是一位数四则运算.两位数四则运算.找最值游戏.排序游戏.程序能实现产 ...
- java 五子棋项目_Java项目实现五子棋小游戏
本文实例为大家分享了Java实现五子棋小游戏的具体代码,供大家参考,具体内容如下 项目名称 五子棋小游戏 项目描述 可以改变获胜棋子数,率先连成棋数的人获胜 代码实现 测试类 public class ...
- 【lvgl游戏开发】用lvgl写了一个数字对拼图益智小游戏
文章目录 游戏说明 实现原理 演示效果 实现代码 游戏说明 单击方格用于显示数字.匹配两个数字,方格将显示从而显示图像. 实现原理 // TODO 演示效果 [lvgl游戏开发]用lvgl写了一个数字 ...
- 趣味益智小游戏 三子棋+五子棋 优化版(可任意选择棋盘大小)
前言 今天牛牛给大家分享的是c语言实现三子棋和五子棋游戏,初学者可能有些不理解的地方,记得私信提问哦,牛牛会一 一回答的. 目录 前言 一.游戏介绍 二.游戏设计思路 2.1 主函数测试区(test. ...
最新文章
- Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.解决方案
- 802.1x------2
- 程序流程图_干货收藏 | Java 程序员必备的一些流程图
- 数据智能构建管理平台Dataphin V2.9.4.3版本发布
- python基础期末考试_python基础试题(4)
- .Net Core with 微服务 - Consul 注册中心
- 了解png 格式,绝对是让你PPT设计感瞬间爆棚的大杀器
- java死锁怎么用jvm调试_jvm 内存dump、gc查看、线程死锁,jmap、jstack、jstat
- windows下Tomcat安装及Eclipse配置教程
- linux 中文件的操作
- 深度卷积神经网络及各种改进
- 【教学类-20-02】20221203《世界杯16强国旗-定量版》(大班)
- unity3d 本地化数据PlayerPrefs详解
- Python+QT+Selenium制作在线视频播放器
- c语言的文件打开模式,c语言文件打开模式 - osc_b2jf5efr的个人空间 - OSCHINA - 中文开源技术交流社区...
- CSS | align-content 与 justify-content 的区别
- 大型多人在线角色扮演类网络游戏角色战斗系统的设计与实现
- 静态时序分析 第六章 串扰和噪声
- MessageSolution 企业邮件归档管理系统 EEA 存在信息泄露漏洞(CNVD-2021-10543)
- 施密特触发器基础知识