新手练习项目 4:简易2048游戏的实现(C++)

名人说:莫听穿林打叶声,何妨吟啸且徐行。—— 苏轼《定风波·莫听穿林打叶声》
Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder)

目录

      • 一、效果图
      • 二、代码(带注释)
      • 三、说明

一、效果图

在这里插入图片描述

二、代码(带注释)

//创作者:Code_流苏(CSDN)
//未经允许,禁止转载发布,可自己学习使用
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

const int SIZE = 4; // 定义游戏板的大小为4x4

// 初始化游戏板
void initializeBoard(vector<vector<int>>& board) {
    board.assign(SIZE, vector<int>(SIZE, 0)); // 将游戏板初始化为SIZE x SIZE的0矩阵
    // 在游戏板上随机生成两个数字2
    board[rand() % SIZE][rand() % SIZE] = 2;
    board[rand() % SIZE][rand() % SIZE] = 2;
}

// 打印游戏板
void printBoard(const vector<vector<int>>& board) {
    for (int i = 0; i < SIZE; ++i) {
        for (int j = 0; j < SIZE; ++j) {
            if(board[i][j] == 0) cout << ".";
            else cout << board[i][j];
            cout << "\t";
        }
        cout << endl;
    }
}

// 检查是否还有可移动的格子
bool canMove(const vector<vector<int>>& board) {
    for (int i = 0; i < SIZE; ++i) {
        for (int j = 0; j < SIZE; ++j) {
            // 如果有空格或者有相邻的相同数字,则可以移动
            if (board[i][j] == 0) return true;
            if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) return true;
            if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) return true;
        }
    }
    return false;
}

// 在随机位置添加一个数字2或4
void addNumber(vector<vector<int>>& board) {
    int i, j;
    do {
        i = rand() % SIZE;
        j = rand() % SIZE;
    } while (board[i][j] != 0); // 选择一个空的格子

    board[i][j] = (rand() % 10 == 0) ? 4 : 2; // 有10%的概率生成4,90%的概率生成2
}

// 旋转游戏板
void rotateBoard(vector<vector<int>>& board) {
    vector<vector<int>> temp(SIZE, vector<int>(SIZE));
    for (int i = 0; i < SIZE; ++i) {
        for (int j = 0; j < SIZE; ++j) {
            temp[j][SIZE - 1 - i] = board[i][j]; // 旋转90度
        }
    }
    board = temp;
}

// 向左移动格子并合并
void moveTiles(vector<vector<int>>& board) {
    for (int i = 0; i < SIZE; ++i) {
        int lastMergePosition = -1; 
        for (int j = 1; j < SIZE; ++j) {
            if (board[i][j] == 0) continue; // 如果当前格子为空,则跳过

            int previousPosition = j - 1;
            // 寻找可以合并或移动的位置
            while (previousPosition > lastMergePosition && board[i][previousPosition] == 0) {
                previousPosition--;
            }

            if (previousPosition == j) continue; // 如果没有可移动或合并的位置,继续下一个格子

            // 根据情况移动或合并格子
            if (board[i][previousPosition] == 0) {
                board[i][previousPosition] = board[i][j];
                board[i][j] = 0;
            } else if (board[i][previousPosition] == board[i][j]) {
                board[i][previousPosition] *= 2;
                board[i][j] = 0;
                lastMergePosition = previousPosition;
            } else if (previousPosition + 1 != j) {
                board[i][previousPosition + 1] = board[i][j];
                board[i][j] = 0;
            }
        }
    }
}

// 定义不同方向的移动
void moveLeft(vector<vector<int>>& board) {
    moveTiles(board);
}

void moveRight(vector<vector<int>>& board) {
    rotateBoard(board);
    rotateBoard(board);
    moveTiles(board);
    rotateBoard(board);
    rotateBoard(board);
}

void moveUp(vector<vector<int>>& board) {
    rotateBoard(board);
    rotateBoard(board);
    rotateBoard(board);
    moveTiles(board);
    rotateBoard(board);
}

void moveDown(vector<vector<int>>& board) {
    rotateBoard(board);
    moveTiles(board);
    rotateBoard(board);
    rotateBoard(board);
    rotateBoard(board);
}

// 主函数
int main() {
    srand(time(NULL)); // 设置随机种子
    vector<vector<int>> board;
    initializeBoard(board); // 初始化游戏板
    printBoard(board); // 打印游戏板

    while (true) {
        if (!canMove(board)) {
            cout << "游戏结束!" << endl;
            break;
        }

        char input;
        cout << "选择方向 (w/a/s/d): ";
        cin >> input; // 获取用户输入
        switch (input) {
            case 'a':
                moveLeft(board);
                break;
            case 'd':
                moveRight(board);
                break;
            case 'w':
                moveUp(board);
                break;
            case 's':
                moveDown(board);
                break;
            default:
                cout << "无效输入! 请使用 w/a/s/d." << endl;
                continue;
        }

        if (canMove(board)) {
            addNumber(board); // 在合适位置添加新的数字
        }

        printBoard(board); // 打印更新后的游戏板
    }

    return 0;
}

三、说明

上述代码实现了一个简单的2048游戏,主要由以下几个部分组成:

  1. 初始化游戏板 (initializeBoard函数):用于初始化一个SIZE x SIZE(在这个例子中是4x4)的游戏板,并随机在两个位置放置数字2。

  2. 打印游戏板 (printBoard函数):该函数用于遍历游戏板并打印每个元素,其中0被替换为.以便于观看。

  3. 检查是否可以移动 (canMove函数):这个函数用来检查游戏板上是否还有可合并的元素或者空位,以决定游戏是否结束。

  4. 添加数字 (addNumber函数):在玩家移动之后,在一个随机的空位置上添加一个新的数字(90%的概率是2,10%的概率是4)。

  5. 旋转游戏板 (rotateBoard函数):为了简化移动逻辑,此函数用来将游戏板顺时针旋转90度。

  6. 移动方块 (moveTiles函数):该函数用于处理实际的方块移动和合并逻辑。

  7. 移动方向 (moveLeft, moveRight, moveUp, moveDown函数):这些函数使用moveTilesrotateBoard来处理不同方向的移动。

  8. 主函数 (main函数):设置游戏的初始状态,然后进入一个循环,等待玩家输入来移动方块,直到没有移动可做时结束游戏。

补充说明:

  • 游戏板的大小是通过const int SIZE = 4预设的,即方格大小为4x4。
  • 游戏开始时,游戏板上有两个数字2。
  • 玩家可以通过输入’w’, ‘a’, ‘s’, 'd’来控制方块向上、左、下、右移动。
  • 当游戏板上没有空位或者没有可合并的相邻方块时,游戏结束。
  • 这个程序没有实现计分功能,可自己扩充实现。

Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder)
点赞加关注,收藏不迷路!本篇文章对你有帮助的话,还请多多点赞支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/302864.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL篇--索引高频面试题

mysql索引 1什么是索引&#xff1f; 索引说白了就是一种数据结构&#xff0c;可以协助快速查询数据&#xff0c;以及更新数据库表中的数据&#xff0c;更通俗的来说索引其实就是目录&#xff0c;通过对数据建立索引形成目录&#xff0c;便于去查询数据&#xff0c;而mysql索引…

静态网页设计——旅游景点介绍(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1f64y1N7uH/?vd_source5f425e0074a7f92921f53ab87712357b 使用技术&#xff1a;HTMLCSSJS&#xff08;…

虚拟机VMware安装Linux

关于安装&#xff0c;安装版本是CentOS 7&#xff0c;选择最小安装即可 第一步&#xff1a;选择创建新的虚拟机 第二步&#xff1a;默认典型&#xff0c;点击下一步 第三步&#xff1a;选择稍后安装操作系统 第四步&#xff1a;选择Linux和版本 第五步&#xff1a;输入虚拟机名…

初识Kafka

1.初识kafka 官网&#xff1a;Apache Kafka Apache Kafka是一个分布式流处理平台&#xff0c;最初由LinkedIn开发并于2011年开源。它主要用于解决大规模数据的实时流式处理和数据管道问题。 Kafka是一个分布式的发布-订阅消息系统&#xff0c;可以快速地处理高吞吐量的…

杨中科 ASP.NET Core前后端分离开发

一、 前后端分离 1、传统MVC开发模式: 前后端的代码被放到同一个项目中&#xff0c;前端人员负责编写页面的模板&#xff0c;而后端开发人员负责编写控制器和模型的代码并且“套模板”。 缺点: 互相依赖&#xff0c;耦合性强&#xff0c;责任划分不清。 2、主流的“前后端分离…

西门子WinCC的C脚本——对象的事件任务

1、 全局脚本编辑器&#xff1b; 2、 对象的属性任务&#xff1b; 3、 对象的事件任务。 本文探讨一下用C脚本来实现对象的事件任务。 一、例程说明引文&#xff1a;博途工控人平时在哪里技术交流博途工控人社群 如图1所示&#xff0c;为本例程的运行画面。本例程实现以下…

【MATLAB第89期】基于MATLAB的差分自回归滑动平均模型ARIMA时间序列预测模型含预测未来

【MATLAB第89期】基于MATLAB的差分自回归滑动平均模型ARIMA时间序列预测模型含预测未来 往期文章 【MATLAB第82期】基于MATLAB的季节性差分自回归滑动平均模型SARIMA时间序列预测模型含预测未来 一、模型介绍 1、模型简介 差分自回归移动平均模型&#xff08;Autoregressiv…

外包做了1个月,技术退步一大半了。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;20年通过校招进入深圳某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

【mysql】—— 事务

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;事务的理解 1、为什么会出现事务 2、什么是事务&#xff1f; 3、事务的版本支持 4、事务提交方式 &#xff08;三&#xff09;事务常见操作方式 1、正常演示 - 证明事务的开始与回滚 2、非正常演示…

CSAPP cache lab - Optimizing Matrix Transpose

CSAPP cache lab part B 矩阵转置 矩阵转置是一种操作&#xff0c;它将矩阵的行和列互换位置&#xff0c;即将原始矩阵的行变为转置矩阵的列&#xff0c;将原始矩阵的列变为转置矩阵的行。转置操作可以通过改变矩阵的布局来方便地进行某些计算和分析。 假设有一个mn的矩阵A&…

Qt读取文件对比:每次获取自定义的长度和使用系统的API,耗时对比

0. 前言 在编程过程中&#xff0c;经常遇到文件读写操作&#xff0c;太频繁了。每次也都写的不一样。 突发奇想&#xff0c;想测试下几种不同的读取文件的效率。 测试以下三种方式读取文件效率&#xff1a; 自定义读取文件耗时使用QFile类API读取文件耗时使用QTextStream类AP…

【BIAI】Lecture 5 - Auditory system

Lecture 5 - Auditory system 专业术语 auditory system 听觉系统 pinna 耳廓 auditory canal 耳道 tympanic membrane 鼓膜 cochlea 耳蜗 ossicles 听骨 auditory-vestibular nerve 前庭神经 oval window 椭圆窗 attenuation reflex 衰减反射 tensor tympani muscle 鼓膜张肌…

那些年听烂了的名词之“高可用“

那些年听烂了的名词之"高可用" 引言什么是可用性 ?哪些风险会影响系统的可用性 &#xff1f;如何应对这些风险&#xff0c;从而确保系统的可用性 &#xff1f;Phase: 设计做好容灾和多活处理做好容错设计做好资源隔离做好扩展性设计做好数据一致性处理 Phase: 预防做…

适配器Adapters

1.适配器作用 主要是对底层的东西进行改造 2.适配器种类&#xff1a;容器适配器&#xff0c;迭代器适配器&#xff0c;仿函数适配器 2.1容器适配器&#xff1a; stack&#xff0c;queue他们两的底层结构都为deque&#xff0c;deque有好多功能&#xff0c;而stack&#x…

如何将支持标准可观测性协议的中间件快速接入观测

前言 作为一名云原生工程师&#xff0c;如何将支持标准可观测性协议的中间件快速接入观测云呢&#xff1f;答案是只需要三步。 首先&#xff0c;需要确定您要观测的中间件类型。支持标准可观测性协议中间件可通过观测云的 DataKit 采集到中间件的关键指标。有些中间件自带可观…

文件系统与日志分析

一&#xff0c;文件系统 &#xff08;一&#xff09;inode 和block概述 1&#xff0c;文件数据包括元信息与实际数据 2&#xff0c;文件存储在硬盘上&#xff0c;硬盘最小存储单位是“扇区”&#xff0c;每个扇区存储512字节 3&#xff0c;block (块) 连续的八个扇区组成一…

Java常用类---包装类

包装类 包装类简介 Java语言是典型的面向对象编程语言&#xff0c;但是其中的8种基本数据类型并不支持面向对象编程&#xff0c;基本类型数据不具备"对象"的特性&#xff0c;即&#xff1a;没有携带属性以及没有方法可以调用。 为了解决上述问题&#xff0c;java为…

【Dubbo3高级特性】「微服务云原生架构」带你从零基础认识搭建公司内部服务用户中心体系(实战指南-01)

基础服务-用户中心 什么是用户中心&#xff1f; 用户中心&#xff0c;在我们的概念里面范围比较的广泛&#xff0c;包含了用户信息、账号信息以及租户信息的管理控制&#xff0c;在我们的总体设计里面&#xff0c;如果设计的边界较为紧密&#xff0c;也可以将权限的部分功能R…

poium测试库介绍

poium测试库前身为selenium-page-objects测试库&#xff0c;我在以前的文章中也有介绍过:这可能是最简单的Page Object库&#xff0c;项目的核心是基于Page Objects实现元素定位的封装。该项目由我个人在维护&#xff0c;目前在公司项目中已经得到的应用。 ### poium的优势 Pa…

Unity中URP下使用屏幕坐标采样深度图

文章目录 前言一、Unity使用了ComputeScreenPos函数得到屏幕坐标1、 我们来看一下这个函数干了什么2、我们看一下该函数实现该结果的意义 二、在Shader中使用&#xff08;法一&#xff09;1、在Varying结构体中2、在顶点着色器中3、在片元着色器中 三、在Shader中使用&#xff…