当前位置: 首页 > news >正文

【C语言】2048游戏完整实现|控制台版 ( 附完整源码)

引言

在C语言学习阶段,通过实战项目巩固知识点是最高效的方式。本文分享一个基于C语言的2048游戏,实现了四方向滑动、数字合并、胜负判定等核心功能,涵盖结构体设计、二维数组操作、随机数生成、控制台绘图等关键技术点,代码精简不到200行,非常适合作为入门级项目练手。

一、项目介绍

  1. 开发背景

2048是一款风靡全球的数字益智游戏,玩家通过滑动方块使相同数字合并,最终目标是合成2048。本文使用纯C语言在控制台环境下实现该游戏,无需任何第三方库,帮助初学者理解游戏逻辑的设计思路。

  1. 技术栈

编程语言:C语言(C99标准)

核心库:stdio.h(输入输出)、stdlib.h(随机数与系统调用)、stdbool.h(布尔类型)、conio.h(控制台按键输入)、time.h(时间种子)

运行环境:Windows控制台

  1. 核心功能
功能编号功能名称功能描述
1游戏初始化清空棋盘,随机生成两个初始数字
2四方向滑动支持↑↓←→四个方向移动方块
3数字合并相邻相同数字自动合并(2+2=4)
4随机生成每次移动后随机生成2或4
5胜负判定达到2048胜利,无法移动则失败
6退出游戏按Q键安全退出

二、核心技术要点

  1. 结构体设计

使用结构体封装游戏状态,将棋盘数据和游戏状态绑定在一起,便于函数间传递数据:

typedef struct { int board[4][4]; // 4x4游戏棋盘,0表示空格 bool isWin; // 是否胜利(达到2048) bool isOver; // 是否游戏结束(无法移动) } GameState;
  1. 移动算法设计

以左移为核心,其他方向通过变换复用左移逻辑:

  • 右移 = 反转行 + 左移 + 反转行
  • 上移 = 矩阵转置 + 左移 + 矩阵转置
  • 下移 = 矩阵转置 + 右移 + 矩阵转置
  1. 控制台绘图

使用Unicode制表符绘制棋盘边框:

  • ┌ ┬ ┐ 表示上边框
  • ├ ┼ ┤ 表示中间分隔线
  • └ ┴ ┘ 表示下边框
  • │ 表示竖线分隔
  1. 按键处理

使用conio.h库的_getch()函数捕获方向键:

  • 72 = 上箭头
  • 80 = 下箭头
  • 75 = 左箭头
  • 77 = 右箭头
  1. 随机数生成

使用srand(time(NULL))初始化随机种子,保证每次游戏随机性不同:

  • 90%概率生成数字2
  • 10%概率生成数字4

三、核心功能解析

  1. 游戏初始化(initGame函数)

核心逻辑:

  • 清空棋盘所有格子为0
  • 初始化游戏状态标志
  • 设置随机数种子
  • 生成两个初始数字

代码部分:

void initGame(GameState* game) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = 0; game->isWin = false; game->isOver = false; srand((unsigned int)time(NULL)); generateNewNumber(game); generateNewNumber(game); }
  1. 棋盘打印(printBoard函数)

核心逻辑:

  • 清屏后打印标题和操作提示
  • 使用Unicode字符绘制4x4网格
  • 根据数字位数自动调整对齐
  • 显示游戏胜利或失败状态

代码部分:

void printBoard(GameState* game) { system("cls"); printf("================= 2048 =================\n"); printf("操作: ↑ ↓ ← → 退出:Q\n\n"); printf("┌─────────┬─────────┬─────────┬─────────┐\n"); for (int i = 0; i < 4; i++) { printf("│"); for (int j = 0; j < 4; j++) { if (game->board[i][j] == 0) printf(" │"); else if (game->board[i][j] < 10) printf(" %d │", game->board[i][j]); else if (game->board[i][j] < 100) printf(" %d │", game->board[i][j]); else if (game->board[i][j] < 1000) printf(" %d │", game->board[i][j]); else printf(" %d │", game->board[i][j]); } printf("\n"); if (i < 3) printf("├─────────┼─────────┼─────────┼─────────┤\n"); else printf("└─────────┴─────────┴─────────┴─────────┘\n"); } if (game->isWin) printf("\n恭喜!你赢了!\n"); else if (game->isOver) printf("\n游戏结束!\n"); }
  1. 左移操作(moveLeft函数)

核心逻辑:

  • 遍历每一行,将非零数字靠左存储
  • 合并相邻相同数字(每个数字每轮只能合并一次)
  • 合并后再次靠左,更新棋盘并返回是否发生移动

代码部分:

bool moveLeft(GameState* game) { bool moved = false; int temp[4]; for (int i = 0; i < 4; i++) { int idx = 0; for (int j = 0; j < 4; j++) if (game->board[i][j] != 0) temp[idx++] = game->board[i][j]; while (idx < 4) temp[idx++] = 0; for (int j = 0; j < 3; j++) if (temp[j] != 0 && temp[j] == temp[j+1]) { temp[j] *= 2; temp[j+1] = 0; moved = true; } idx = 0; int newRow[4] = {0}; for (int j = 0; j < 4; j++) if (temp[j] != 0) newRow[idx++] = temp[j]; for (int j = 0; j < 4; j++) if (game->board[i][j] != newRow[j]) { moved = true; game->board[i][j] = newRow[j]; } } return moved; }
  1. 其他方向移动(复用左移)

核心逻辑:

  • 右移:先反转每行,再左移,再反转恢复
  • 上移:先转置矩阵,再左移,再转置恢复
  • 下移:先转置矩阵,再右移,再转置恢复

代码部分:

bool moveRight(GameState* game) { bool moved = false; for (int i = 0; i < 4; i++) for (int j = 0; j < 2; j++) { int t = game->board[i][j]; game->board[i][j] = game->board[i][3-j]; game->board[i][3-j] = t; } moved = moveLeft(game); for (int i = 0; i < 4; i++) for (int j = 0; j < 2; j++) { int t = game->board[i][j]; game->board[i][j] = game->board[i][3-j]; game->board[i][3-j] = t; } return moved; } bool moveUp(GameState* game) { bool moved = false; int temp[4][4]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; moved = moveLeft(game); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; return moved; } bool moveDown(GameState* game) { bool moved = false; int temp[4][4]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; moved = moveRight(game); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; return moved; }
  1. 随机生成数字(generateNewNumber函数)

核心逻辑:

  • 检查棋盘是否已满
  • 随机选择一个空位置
  • 90%概率生成2,10%概率生成4

代码部分:

void generateNewNumber(GameState* game) { if (isBoardFull(game)) return; int x, y; do { x = rand() % 4; y = rand() % 4; } while (game->board[x][y] != 0); game->board[x][y] = (rand() % 10 == 0) ? 4 : 2; }
  1. 胜负判定(checkGameState函数)

核心逻辑:

  • 遍历棋盘检查是否有2048,有则胜利
  • 调用canMove检查是否还能继续移动,不能则失败

代码部分:

void checkGameState(GameState* game) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (game->board[i][j] == 2048) { game->isWin = true; return; } if (!canMove(game)) game->isOver = true; } bool canMove(GameState* game) { if (!isBoardFull(game)) return true; for (int i = 0; i < 4; i++) for (int j = 0; j < 3; j++) if (game->board[i][j] == game->board[i][j+1]) return true; for (int j = 0; j < 4; j++) for (int i = 0; i < 3; i++) if (game->board[i][j] == game->board[i+1][j]) return true; return false; }
  1. 主函数(main)

核心逻辑:

  • 初始化游戏
  • 循环:打印棋盘 → 获取按键 → 执行移动 → 生成新数字 → 检查状态
  • 游戏结束时等待用户按键退出

代码部分:

int main() { GameState game; initGame(&game); while (true) { printBoard(&game); if (game.isWin || game.isOver) break; char input = _getch(); bool moved = false; switch (input) { case 72: moved = moveUp(&game); break; case 80: moved = moveDown(&game); break; case 75: moved = moveLeft(&game); break; case 77: moved = moveRight(&game); break; case 'q': case 'Q': return 0; default: continue; } if (moved) { generateNewNumber(&game); checkGameState(&game); } } printf("\n按任意键退出\n"); _getch(); return 0; }

四、运行效果演示

示例 1:游戏初始界面

================= 2048 ================= 操作: ↑ ↓ ← → 退出:Q ┌─────────┬─────────┬─────────┬─────────┐ │ │ 2 │ │ │ ├─────────┼─────────┼─────────┼─────────┤ │ │ │ │ 4 │ ├─────────┼─────────┼─────────┼─────────┤ │ │ │ │ │ ├─────────┼─────────┼─────────┼─────────┤ │ │ │ │ │ └─────────┴─────────┴─────────┴─────────┘

示例 2:游戏中界面

================= 2048 ================= 操作: ↑ ↓ ← → 退出:Q ┌─────────┬─────────┬─────────┬─────────┐ │ │ 2 │ │ 4 │ ├─────────┼─────────┼─────────┼─────────┤ │ 8 │ 16 │ 4 │ │ ├─────────┼─────────┼─────────┼─────────┤ │ │ 2 │ 8 │ 2 │ ├─────────┼─────────┼─────────┼─────────┤ │ 4 │ │ 4 │ 8 │ └─────────┴─────────┴─────────┴─────────┘

示例 3:游戏胜利

================= 2048 ================= 操作: ↑ ↓ ← → 退出:Q ┌─────────┬─────────┬─────────┬─────────┐ │ 2 │ 4 │ 8 │ 16 │ ├─────────┼─────────┼─────────┼─────────┤ │ 64 │ 32 │ 16 │ 8 │ ├─────────┼─────────┼─────────┼─────────┤ │ 256 │ 128 │ 64 │ 32 │ ├─────────┼─────────┼─────────┼─────────┤ │ 16 │ 1024 │ 512 │ 2048 │ └─────────┴─────────┴─────────┴─────────┘ 恭喜!你赢了!

示例 4:游戏失败

================= 2048 ================= 操作: ↑ ↓ ← → 退出:Q ┌─────────┬─────────┬─────────┬─────────┐ │ 2 │ 4 │ 2 │ 4 │ ├─────────┼─────────┼─────────┼─────────┤ │ 4 │ 2 │ 4 │ 2 │ ├─────────┼─────────┼─────────┼─────────┤ │ 2 │ 4 │ 2 │ 4 │ ├─────────┼─────────┼─────────┼─────────┤ │ 4 │ 2 │ 4 │ 2 │ └─────────┴─────────┴─────────┴─────────┘ 游戏结束!

五、项目总结与改进方向

  1. 项目收获
  • 掌握C语言结构体的定义与使用
  • 理解二维数组的操作技巧
  • 学会矩阵转置、反转等算法应用
  • 掌握随机数生成与种子初始化
  • 学会使用控制台字符绘制简单图形
  1. 改进方向
  • 添加分数统计功能
  • 实现撤销功能
  • 添加最高分记录
  • 跨平台支持(Linux/macOS)
  • 图形界面(SDL/OpenGL)

六、完整源码

#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <conio.h> #include <time.h> typedef struct { int board[4][4]; bool isWin; bool isOver; } GameState; void initGame(GameState* game); void printBoard(GameState* game); void generateNewNumber(GameState* game); bool moveLeft(GameState* game); bool moveRight(GameState* game); bool moveUp(GameState* game); bool moveDown(GameState* game); void checkGameState(GameState* game); bool isBoardFull(GameState* game); bool canMove(GameState* game); void initGame(GameState* game) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = 0; game->isWin = false; game->isOver = false; srand((unsigned int)time(NULL)); generateNewNumber(game); generateNewNumber(game); } void printBoard(GameState* game) { system("cls"); printf("================= 2048 =================\n"); printf("操作: ↑ ↓ ← → 退出:Q\n\n"); printf("┌─────────┬─────────┬─────────┬─────────┐\n"); for (int i = 0; i < 4; i++) { printf("│"); for (int j = 0; j < 4; j++) { if (game->board[i][j] == 0) printf(" │"); else if (game->board[i][j] < 10) printf(" %d │", game->board[i][j]); else if (game->board[i][j] < 100) printf(" %d │", game->board[i][j]); else if (game->board[i][j] < 1000) printf(" %d │", game->board[i][j]); else printf(" %d │", game->board[i][j]); } printf("\n"); if (i < 3) printf("├─────────┼─────────┼─────────┼─────────┤\n"); else printf("└─────────┴─────────┴─────────┴─────────┘\n"); } if (game->isWin) printf("\n恭喜!你赢了!\n"); else if (game->isOver) printf("\n游戏结束!\n"); } void generateNewNumber(GameState* game) { if (isBoardFull(game)) return; int x, y; do { x = rand() % 4; y = rand() % 4; } while (game->board[x][y] != 0); game->board[x][y] = (rand() % 10 == 0) ? 4 : 2; } bool isBoardFull(GameState* game) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (game->board[i][j] == 0) return false; return true; } bool moveLeft(GameState* game) { bool moved = false; int temp[4]; for (int i = 0; i < 4; i++) { int idx = 0; for (int j = 0; j < 4; j++) if (game->board[i][j] != 0) temp[idx++] = game->board[i][j]; while (idx < 4) temp[idx++] = 0; for (int j = 0; j < 3; j++) if (temp[j] != 0 && temp[j] == temp[j+1]) { temp[j] *= 2; temp[j+1] = 0; moved = true; } idx = 0; int newRow[4] = {0}; for (int j = 0; j < 4; j++) if (temp[j] != 0) newRow[idx++] = temp[j]; for (int j = 0; j < 4; j++) if (game->board[i][j] != newRow[j]) { moved = true; game->board[i][j] = newRow[j]; } } return moved; } bool moveRight(GameState* game) { bool moved = false; for (int i = 0; i < 4; i++) for (int j = 0; j < 2; j++) { int t = game->board[i][j]; game->board[i][j] = game->board[i][3-j]; game->board[i][3-j] = t; } moved = moveLeft(game); for (int i = 0; i < 4; i++) for (int j = 0; j < 2; j++) { int t = game->board[i][j]; game->board[i][j] = game->board[i][3-j]; game->board[i][3-j] = t; } return moved; } bool moveUp(GameState* game) { bool moved = false; int temp[4][4]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; moved = moveLeft(game); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; return moved; } bool moveDown(GameState* game) { bool moved = false; int temp[4][4]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; moved = moveRight(game); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) temp[i][j] = game->board[i][j]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) game->board[i][j] = temp[j][i]; return moved; } bool canMove(GameState* game) { if (!isBoardFull(game)) return true; for (int i = 0; i < 4; i++) for (int j = 0; j < 3; j++) if (game->board[i][j] == game->board[i][j+1]) return true; for (int j = 0; j < 4; j++) for (int i = 0; i < 3; i++) if (game->board[i][j] == game->board[i+1][j]) return true; return false; } void checkGameState(GameState* game) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (game->board[i][j] == 2048) { game->isWin = true; return; } if (!canMove(game)) game->isOver = true; } int main() { GameState game; initGame(&game); while (true) { printBoard(&game); if (game.isWin || game.isOver) break; char input = _getch(); bool moved = false; switch (input) { case 72: moved = moveUp(&game); break; case 80: moved = moveDown(&game); break; case 75: moved = moveLeft(&game); break; case 77: moved = moveRight(&game); break; case 'q': case 'Q': printf("游戏退出\n"); return 0; default: continue; } if (moved) { generateNewNumber(&game); checkGameState(&game); } } printf("\n按任意键退出\n"); _getch(); return 0; }
http://www.jsqmd.com/news/1104615/

相关文章:

  • 别再只把 `property` 当装饰器:一文看懂 Python 属性访问的底层机制
  • Unity游戏汉化神器:XUnity Auto Translator让你无障碍畅玩外语游戏
  • Ubuntu24.04编译linux-xlnx-xlnx_rebase_v5.4的问题
  • GHelper终极指南:华硕笔记本性能优化与轻量控制完整教程
  • 无人机路径规划算法
  • 5分钟掌握百度网盘提取码智能获取:告别繁琐搜索的终极方案
  • 告别臃肿控制软件:G-Helper如何让你的华硕笔记本重获新生
  • 漏洞修复与预防实战:从SQL注入到文件上传的闭环安全实践
  • 托珠单抗静注不便,巴瑞替尼口服便利能否取而代之
  • STM32F446RE与MC6470 IMU的运动控制方案实现
  • WebLogic高危漏洞应急响应实战:从CVE-2019-2725反序列化攻击到主动防御
  • openEuler/bigdata:构建下一代大数据生态系统的终极指南 [特殊字符]
  • 华硕设备终极解放方案:G-Helper轻量级控制工具从痛点到解决方案完全指南
  • 【小白也能轻松玩转龙虾】虾壳云一键部署 OpenClaw v2.7.9,实现电脑办公任务自动化(附最新安装包)
  • 终极指南:如何用dnSpy调试和编辑.NET程序集(无需源代码)
  • G-Helper:华硕笔记本轻量级性能管理工具完全指南
  • 百度网盘解析工具终极指南:如何轻松获取真实下载地址
  • 手机号码归属地查询系统:3步快速定位与地图可视化方案
  • LoadRunner性能测试实战:从核心组件到高频问题排查指南
  • 暗黑3自动化宏工具终极指南:告别手动操作,解放你的双手
  • 告别百度网盘限速:3分钟掌握真实下载链接解析技巧
  • 低成本6DoF运动跟踪方案:IMU与MCU的优化实践
  • 戴尔G15散热控制终极指南:开源温度管理工具完全解析
  • IntelliJ IDEA折叠边界失效真相(官方Bug追踪编号IDEA-32891):如何绕过2023.3.2+版本大纲丢失问题
  • DownKyi视频下载神器:5个实用技巧快速掌握B站内容收藏
  • 关于哈希函数的分布特性与性能影响分析的技术7
  • Storprototrace在生产环境的部署实践:大规模存储集群监控终极指南
  • 手机号码定位系统:3分钟实现精准地理位置查询
  • Mem Reduct:终极免费内存清理工具,让你的Windows电脑运行如飞
  • 如何快速解决iSulad授权配置的10个常见问题:openEuler authz插件终极指南