对于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.总结:难点、花时间比较久的、逆向软件工程的一些思考
难点:

理解游戏逻辑的实现,尤其是合并和移动的规则。
确保每次移动后都能正确生成新数字并更新分数。
花时间比较久的地方:

处理用户输入和游戏状态的更新,尤其是在不同方向移动时的逻辑。
确保游戏结束条件的准确性和用户体验的流畅性。
逆向软件工程的一些思考:

在分析原始代码时,发现了设计上的不足和潜在的改进空间。
逆向工程不仅是理解代码,更是提升自己的编程能力和设计思维的过程。
通过重构,学习如何将复杂的逻辑分解为更小、更易于管理的函数,提高代码的可维护性和可读性。

posted @ 2025-02-27 17:02  王浩宇2352228  阅读(25)  评论(0)    收藏  举报