对于C语言实现的小游戏2048的重构与更新
1.代码/原代码来源与归属
原代码来源于CSDN博主@布衣龙少,网页链接:https://blog.csdn.net/2401_83827692/article/details/140548434?ops_request_misc=&request_id=&biz_id=102&utm_term=2048&utm_medium=distribute.pc_search_result.none-task-blog-2blogsobaiduweb~default-3-140548434.nonecase&spm=1018.2226.3001.4450。
2.运行环境以及运行结果截图
- 运行环境
操作系统:Windows 11
编译器:Visual Studio 2022
编码语言:C - 运行结果
原代码:
点击查看代码
//show((struct data *)p);显示棋盘数据情况
//right((struct data*)p);向右移动
//left((struct data*)p);向左移动
//up(struct data*p);//向上移动
//down(struct data*p);//向下移动
#include "mode.h"
int main()
{
struct data *p = calloc(16,sizeof(struct data));//申请空间进行存放数据
int grade = 0;
for(int i=0;i<4;i++)//初始化数据
{
for (int j=0;j<4;j++)
{
(p+(4*i+j))->x=i;
(p+(4*i+j))->y=j;
(p+(4*i+j))->score=0;
//printf("score0 = %d\n", (p+4*i+j)->score);
}
//printf("\n");
}
while(1)
{
newNum(p,&grade);//产生随机数
char dierction;//存储空间移动的存储
printf("请输入移动方向");
scanf("%c",&dierction);
while(getchar()!='\n');//去除scanf的缓存空间
if(dierction=='!')//方向移动
break;//退出游戏
else if(dierction=='w')
up(p,&grade);
else if(dierction=='s')
down(p,&grade);
else if(dierction=='a')
left(p,&grade);
else if(dierction=='d')
right(p,&grade);
//判断胜负
int temp=0,win=0;
for(int i=0;i<16;i++)
{
if((p+i)->score!=0)//判断格子有数据的个数
{
temp++;
}
if((p+i)->score==2048)//判断是否有数为2048
{
win++;
}
}
if(win>0)
{
printf("游戏胜利");//数据达到2048游戏胜利
break;
}
if(temp>14)//数据大于14就不能生成两个随机数了,失败
{
printf("游戏失败");
break;
}
}
return 0;
}
点击查看代码
#include "mode.h"
void newNum(struct data *p,int *grade)
{
srand(time(NULL));
while (1)
{
int x = rand() % 4;
int y = rand() % 4;
if ((p+4*x+y)->score == 0) //查看随机的坐标是否等于0
{
if (rand() % 10 <5) //判断随机数取10余数是否<5,来限制4出现的几率为50%
{
(p+4*x+y)->score = 4;
break;
}
else
{
(p+4*x+y)->score = 2; //否则2出现的几率为50%
break;
}
}
}
while (1)
{
int x = rand() % 4;
int y = rand() % 4;
if ((p+4*x+y)->score == 0) //查看随机的坐标是否等于0
{
if (rand() % 10 <5) //判断随机数取10余数是否<5,来限制4出现的几率为50%
{
(p+4*x+y)->score = 4;
}
else
{
(p+4*x+y)->score = 2; //否则2出现的几率为50%
}
break;
}
}
show(p, grade);
}
/*
显示当前棋盘数字的情况
struct data *p接收数组首地址
*/
void show(struct data *p,int *grade)
{
printf("分数为%d\n",*grade);
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
printf("%d\t",(p+(4*i+j))->score);
}
printf("\n");
}
}
/*
棋子向右移动
struct data *p接收数组首地址
*/
void right(struct data*p,int *grade)//向右移动操作
{
printf("向右移动\n");
for(int k=0;k<3;k++)
{ //数值移动
for(int x=0;x<4;x++)
{
for(int y=0;y<3;y++)
{ //左右相邻格子数值相同时并且值小于2048将二者相加
if((p+4*x+y)->score==(p+4*x+y+1)->score&&(p+4*x+y)->score<2048)
{
*grade+=(p+4*x+y)->score;
(p+4*x+y)->score=0;//后面的一位置为0
(p+4*x+y+1)->score*=2;//前面的值是原来的二倍
}
}
}
//位置移动
for(int x=0;x<4;x++)
{
for(int y=0;y<3;y++)
{
if((p+4*x+y+1)->score==0)//当前面是数为0是互换两者数值
{
(p+4*x+y+1)->score=(p+4*x+y)->score;
(p+4*x+y)->score=0;
}
}
}
}
}
/*
棋子向左移动
struct data *p接收数组首地址
*/
void left(struct data*p,int *grade)//向左移动操作
{
printf("向左移动\n");
for(int k=0;k<3;k++)
{ //数值移动
for(int x=0;x<4;x++)
{
for(int y=3;y>0;y--)
{ //左右相邻格子数值相同时并且值小于2048将二者相加
if((p+4*x+y)->score==(p+4*x+y-1)->score&&(p+4*x+y)->score<2048)
{
*grade+=(p+4*x+y)->score;
(p+4*x+y)->score=0;//后面的一位置为0
(p+4*x+y-1)->score*=2;//前面的值是原来的二倍
}
}
}
//位置移动
for(int x=0;x<4;x++)
{
for(int y=3;y>0;y--)
{
if((p+4*x+y-1)->score==0)//当前面是数为0是互换两者数值
{
(p+4*x+y-1)->score=(p+4*x+y)->score;
(p+4*x+y)->score=0;
}
}
}
}
}
/*
棋子向下移动
struct data *p接收数组首地址
*/
void down(struct data*p,int *grade)//向下移动操作
{
printf("向下移动\n");
for(int k=0;k<3;k++)
{ //数值移动
for(int y=0;y<4;y++)
{
for(int x=0;x<3;x++)
{ //上下相邻格子数值相同时并且值小于2048将二者相加
if((p+4*x+y)->score==(p+4*(1+x)+y)->score&&(p+4*x+y)->score<2048)
{
*grade+=(p+4*x+y)->score;
(p+4*x+y)->score=0;//后面的一位置为0
(p+4*(x+1)+y)->score*=2;//前面的值是原来的二倍
}
}
}
//位置移动
for(int y=0;y<4;y++)
{
for(int x=0;x<4;x++)
{
if((p+4*(x+1)+y)->score==0)//当前面是数为0是互换两者数值
{
(p+4*(x+1)+y)->score=(p+4*x+y)->score;
(p+4*x+y)->score=0;
}
}
}
}
}
/*
棋子向上移动
struct data *p接收数组首地址
*/
void up(struct data*p,int *grade)//向上移动操作
{
printf("向上移动\n");
for(int k=0;k<3;k++)
{ //数值移动
for(int y=0;y<4;y++)
{
for(int x=3;x>0;x--)
{ //上下相邻格子数值相同时并且值小于2048将二者相加
if((p+4*x+y)->score==(p+4*(x-1)+y)->score&&(p+4*x+y)->score<2048)
{
*grade+=(p+4*x+y)->score;
(p+4*x+y)->score=0;//后面的一位置为0
(p+4*(x-1)+y)->score*=2;//前面的值是原来的二倍
}
}
}
//位置移动
for(int y=0;y<4;y++)
{
for(int x=3;x>0;x--)
{
if((p+4*(x-1)+y)->score==0)//当前面是数为0是互换两者数值
{
(p+4*(x-1)+y)->score=(p+4*x+y)->score;
(p+4*x+y)->score=0;
}
}
}
}
}
点击查看代码
#ifndef __MODE__H
#define __MODE__H
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct data//结构体存数据
{
int x;
int y;
int score;
};
void show(struct data* p,int *grade);//显示棋盘数据
void right(struct data*p,int *grade);//向右移动
void left(struct data*p,int *grade);//向左移动
void up(struct data*p,int *grade);//向上移动
void down(struct data*p,int *grade);//向下移动
void newNum(struct data*p,int *grade);//产生随机数
#endif

3.主要问题列表
代码可读性差: 原始代码中缺乏注释,变量命名不够清晰。
改善: 添加详细注释并优化变量命名。
游戏逻辑不完善: 原始代码未能处理所有可能的用户输入,导致游戏体验不佳。
改善: 增加输入验证,确保用户只能输入有效方向。
内存管理: 原始代码未考虑内存使用情况,可能导致资源浪费。
改善: 通过结构体管理游戏状态,减少不必要的内存操作。
游戏存在更高挑战上限:原程序会在出现数字2048后自动结束,且没有高分记录。
改善:在达到2048之后用户可以自选是否继续挑战直到所有各自填满(即游戏真正结束),并加入了记录用户最高分的功能
4.新代码附上
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SIZE 4 // 棋盘尺寸
#define WINNING_SCORE 2048 // 胜利分数
struct data {
int board[SIZE][SIZE]; // 棋盘
int score; // 当前分数
int continueGame; // 控制游戏是否继续
int bestScore; // 最高分数
double bestTime; // 用时
};
// 函数声明
void show(struct data* p);
void up(struct data* p);
void down(struct data* p);
void left(struct data* p);
void right(struct data* p);
void newNum(struct data* p);
int canContinue(struct data* p);
void initializeGame(struct data* p);
void gameLoop(struct data* p);
// 初始化游戏
void initializeGame(struct data* p) {
p->score = 0;
p->continueGame = 1;
p->bestScore = 0;
p->bestTime = 0.0;
// 初始化棋盘为0
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
p->board[i][j] = 0;
}
}
newNum(p); // 产生初始数字
newNum(p);
}
// 显示棋盘和分数
void show(struct data* p) {
printf("\n");
printf("+----------------------+\n");
for (int i = 0; i < SIZE; i++) {
printf("|");
for (int j = 0; j < SIZE; j++) {
if (p->board[i][j] == 0)
printf(" |"); // 空白位置
else
printf("%4d |", p->board[i][j]); // 显示数字,右对齐
}
printf("\n");
printf("+----------------------+\n");
}
printf("当前分数: %d\n", p->score);
printf("最高分数: %d\n", p->bestScore);
// 检查是否获胜
if (p->score >= WINNING_SCORE) {
printf("恭喜你获胜!\n");
p->continueGame = 0; // 游戏结束
}
}
// 随机产生新数字
void newNum(struct data* p) {
int emptyCells[SIZE * SIZE][2]; // 保存空格
int count = 0;
// 找到所有空位置
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (p->board[i][j] == 0) {
emptyCells[count][0] = i;
emptyCells[count][1] = j;
count++;
}
}
}
// 随机选择一个空位置生成2或4
if (count > 0) {
int randomIndex = rand() % count;
int r = emptyCells[randomIndex][0];
int c = emptyCells[randomIndex][1];
p->board[r][c] = (rand() % 2 + 1) * 2; // 生成2或4
}
}
// 检查是否可以继续游戏
int canContinue(struct data* p) {
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (p->board[i][j] == 0) {
return 1; // 有空格可以继续
}
if (j < SIZE - 1 && p->board[i][j] == p->board[i][j + 1]) {
return 1; // 可以合并
}
if (i < SIZE - 1 && p->board[i][j] == p->board[i + 1][j]) {
return 1; // 可以合并
}
}
}
return 0; // 没有可用步骤
}
// 向上移动
void up(struct data* p) {
for (int j = 0; j < SIZE; j++) {
for (int i = 1; i < SIZE; i++) {
if (p->board[i][j] != 0) {
int k = i;
while (k > 0 && p->board[k - 1][j] == 0) {
p->board[k - 1][j] = p->board[k][j];
p->board[k][j] = 0;
k--;
}
if (k > 0 && p->board[k - 1][j] == p->board[k][j]) {
p->board[k - 1][j] *= 2; // 合并
p->score += p->board[k - 1][j];
p->board[k][j] = 0; // 移动后设为0
}
}
}
}
}
// 向下移动
void down(struct data* p) {
for (int j = 0; j < SIZE; j++) {
for (int i = SIZE - 2; i >= 0; i--) {
if (p->board[i][j] != 0) {
int k = i;
while (k < SIZE - 1 && p->board[k + 1][j] == 0) {
p->board[k + 1][j] = p->board[k][j];
p->board[k][j] = 0;
k++;
}
if (k < SIZE - 1 && p->board[k + 1][j] == p->board[k][j]) {
p->board[k + 1][j] *= 2; // 合并
p->score += p->board[k + 1][j];
p->board[k][j] = 0; // 移动后设为0
}
}
}
}
}
// 向左移动
void left(struct data* p) {
for (int i = 0; i < SIZE; i++) {
for (int j = 1; j < SIZE; j++) {
if (p->board[i][j] != 0) {
int k = j;
while (k > 0 && p->board[i][k - 1] == 0) {
p->board[i][k - 1] = p->board[i][k];
p->board[i][k] = 0;
k--;
}
if (k > 0 && p->board[i][k - 1] == p->board[i][k]) {
p->board[i][k - 1] *= 2; // 合并
p->score += p->board[i][k - 1];
p->board[i][k] = 0; // 移动后设为0
}
}
}
}
}
// 向右移动
void right(struct data* p) {
for (int i = 0; i < SIZE; i++) {
for (int j = SIZE - 2; j >= 0; j--) {
if (p->board[i][j] != 0) {
int k = j;
while (k < SIZE - 1 && p->board[i][k + 1] == 0) {
p->board[i][k + 1] = p->board[i][k];
p->board[i][k] = 0;
k++;
}
if (k < SIZE - 1 && p->board[i][k + 1] == p->board[i][k]) {
p->board[i][k + 1] *= 2; // 合并
p->score += p->board[i][k + 1];
p->board[i][k] = 0; // 移动后设为0
}
}
}
}
}
// 主游戏循环
void gameLoop(struct data* p) {
initializeGame(p); // 初始化游戏
while (p->continueGame) {
show(p); // 显示当前状态
char input;
printf("输入方向(w/a/s/d): ");
scanf_s(" %c", &input);
switch (input) {
case 'w': up(p); break; // 上
case 'a': left(p); break; // 左
case 's': down(p); break; // 下
case 'd': right(p); break; // 右
default: printf("无效输入!\n"); break;
}
newNum(p); // 产生新的数
// 检查游戏是否结束
if (!canContinue(p)) {
printf("没有可用的步骤,游戏结束!\n");
p->continueGame = 0; // 结束游戏
}
}
printf("游戏结束!最佳成绩: %d, 用时: %.2f秒\n", p->bestScore, p->bestTime);
}
int main() {
srand(time(NULL)); // 设置随机种子
struct data gameData;
gameLoop(&gameData); // 启动游戏循环
return 0;
}
点击查看代码
// 显示棋盘和分数
void show(struct data* p) {
printf("\n");
printf("+----------------------+\n");
for (int i = 0; i < SIZE; i++) {
printf("|");
for (int j = 0; j < SIZE; j++) {
if (p->board[i][j] == 0)
printf(" |"); // 空白位置
else
printf("%4d |", p->board[i][j]); // 显示数字,右对齐
}
printf("\n");
printf("+----------------------+\n");
}
printf("当前分数: %d\n", p->score);
printf("最高分数: %d\n", p->bestScore);
// 检查是否获胜
if (p->score >= WINNING_SCORE) {
printf("恭喜你获胜!\n");
p->continueGame = 0; // 游戏结束
}
}
// 主游戏循环
void gameLoop(struct data* p) {
initializeGame(p); // 初始化游戏
while (p->continueGame) {
show(p); // 显示当前状态
char input;
printf("输入方向(w/a/s/d): ");
scanf(" %c", &input);
switch (input) {
case 'w': up(p); break; // 上
case 'a': left(p); break; // 左
case 's': down(p); break; // 下
case 'd': right(p); break; // 右
default: printf("无效输入!\n"); break; // 增加无效输入提示
}
newNum(p); // 产生新的数
// 检查游戏是否结束
if (!canContinue(p)) {
printf("没有可用的步骤,游戏结束!\n");
p->continueGame = 0; // 结束游戏
}
}
printf("游戏结束!最佳成绩: %d, 用时: %.2f秒\n", p->bestScore, p->bestTime);
}
5.重构的代码的测试截图

6.总结:难点、花时间比较久的、逆向软件工程的一些思考
难点:
理解游戏逻辑的实现,尤其是合并和移动的规则。
确保每次移动后都能正确生成新数字并更新分数。
花时间比较久的地方:
处理用户输入和游戏状态的更新,尤其是在不同方向移动时的逻辑。
确保游戏结束条件的准确性和用户体验的流畅性。
逆向软件工程的一些思考:
在分析原始代码时,发现了设计上的不足和潜在的改进空间。
逆向工程不仅是理解代码,更是提升自己的编程能力和设计思维的过程。
通过重构,学习如何将复杂的逻辑分解为更小、更易于管理的函数,提高代码的可维护性和可读性。

浙公网安备 33010602011771号