记录大二上学期的期末大作业——2048

前言

我的第一篇blog,在我的 拖延 精益求精之下,终于诞生出来了。由于是大二上学期期末做的大作业,代码水平还不够高,所以CV了许多大佬的2048,经过我的一番 魔改 整合修改,所呈现出来了现在的2048。下面先展示一下最终的效果图(●’◡’●)

最开始的2048(小黑框)

最开始的2048是没有加入图形库的,只是简简单单的在小黑框做了一个简易到不行的2048
Alt

第二版的2048(C语言-Easyx)

在这里插入图片描述
因为能力有限,所以当时做出来的图形页面实在是惨不忍睹的丑。😭
Alt

最后的qq2048(C++ - Easyx)

Alt
Alt

2048的基本算法介绍(第一版)

先开始第一版的介绍,但是这些算法的思想和大致的代码,在后面两版都是基本不变的。

1. 随机数(2,4,8…如何去达到随机出现的概率)
根据生成的随机数,对一定的值进行取模,达到生成一定概率的数。
例如:我们设定4出现的概率为1/9,于是可以利用系统提供的随机数函数生成一个数,然后对9取余。得到的数若大于0则在游戏面板空格处生成一个2,若余数等于0,则生成4。

void add_rand_num() {
    srand((unsigned int)time(0));
    int n = rand() % get_null_count(); /* 确定在何处空位置生成随机数 */
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            /* 定位待生成的位置 */
            if (board[i][j] == 0 && n-- == 0) {//如果n=3,则在第三个空格生成随机数 
                board[i][j] = (rand() % 9 ? 2 : 4); /* 生成数字2或4,生成概率为8:1 */
                return;
            }
        }
    }
}

2. 方块的移动
在一行中,用b[4]表示一行的一维数组,使用两个下标变量来遍历列项,这里使用jk,其中j总在k的后面,用来寻找k项后面第一个不为0的数字,而k项用于表示当前待比较的项,总是和j项之间隔着若干个数字0,或者干脆紧挨着。不失一般性,考虑往左滑动时,初始情况下j等于1,而k等于0,接着判断j项数字是否大于0,若是,则判断j项和k项数字的关系,
分成3种情况处理,分别是
(合并)P1: b[k]==b[j]
(移动)P2: b[k]==0
(碰撞)P3: b[k]!=0且b[k]!=b[j]
若否,则j自加1,然后继续寻找k项后面第一个不为0的数字。

/*
 * 如下四个函数,实现上下左右移动时数字面板的变化算法
 * 左和右移动的本质一样,区别仅仅是列项的遍历方向相反
 * 上和下移动的本质一样,区别仅仅是行项的遍历方向相反
 * 左和上移动的本质也一样,区别仅仅是遍历时行和列互换
*/
/*  左移 函数定义 */
void move_left() {
    /* 变量i用来遍历行项的下标,并且在移动时所有行相互独立,互不影响 */
    for (int i = 0; i < 4; ++i) {
        /* 变量j为列下标,变量k为待比较(合并)项的下标,循环进入时k<j */
        for (int j = 1, k = 0; j < 4; ++j) {
            if (board[i][j] > 0) /* 找出k后面第一个不为空的项,下标为j,之后分三种情况 */
            {
                if (board[i][k] == board[i][j]) {
                    /* 情况1:k项和j项相等,此时合并方块并计分 */
                    board[i][k++] *= 2;
                    board[i][j] = 0;
                    if_need_add_num = 1; /* 需要生成随机数和刷新界面 */
                }
                else if (board[i][k] == 0) {
                    /* 情况2:k项为空,则把j项赋值给k项,相当于j方块移动到k方块 */
                    board[i][k] = board[i][j];
                    board[i][j] = 0;
                    if_need_add_num = 1;
                }
                else {
                    /* 情况3:k项不为空,且和j项不相等,此时把j项赋值给k+1项,相当于移动到k+1的位置 */
                    board[i][++k] = board[i][j];
                    if (j != k) {
                        /* 判断j项和k项是否原先就挨在一起,若不是则把j项赋值为空(值为0) */
                        board[i][j] = 0;
                        if_need_add_num = 1;
                    }
                }
            }
        }
    }
}
/* 右移 函数定义 */
void move_right() {
    /* 仿照左移操作,区别仅仅是j和k都反向遍历 */
    for (int i = 0; i < 4; ++i) {
        for (int j = 2, k = 3; j >= 0; --j) {
            if (board[i][j] > 0) {
                if (board[i][k] == board[i][j]) {
                    board[i][k--] *= 2;
                    board[i][j] = 0;
                    if_need_add_num = 1;
                }
                else if (board[i][k] == 0) {
                    board[i][k] = board[i][j];
                    board[i][j] = 0;
                    if_need_add_num = 1;
                }
                else {
                    board[i][--k] = board[i][j];
                    if (j != k) {
                        board[i][j] = 0;
                        if_need_add_num = 1;
                    }
                }
            }
        }
    }
}
/* 上移 函数定义 */
void move_up() {
    /* 仿照左移操作,区别仅仅是行列互换后遍历*/
    for (int i = 0; i < 4; ++i) {
        for (int j = 1, k = 0; j < 4; ++j) {
            if (board[j][i] > 0) {
                if (board[k][i] == board[j][i]) {
                    board[k++][i] *= 2;
                    board[j][i] = 0;
                    if_need_add_num = 1;
                }
                else if (board[k][i] == 0) {
                    board[k][i] = board[j][i];
                    board[j][i] = 0;
                    if_need_add_num = 1;
                }
                else {
                    board[++k][i] = board[j][i];
                    if (j != k) {
                        board[j][i] = 0;
                        if_need_add_num = 1;
                    }
                }
            }
        }
    }
}

/* 下移 函数定义 */
void move_down() {
 /* 仿照左移操作,区别仅仅是行列互换后遍历,且j和k都反向遍历 */
    for (int i = 0; i < 4; ++i) {
        for (int j = 2, k = 3; j >= 0; --j) {
            if (board[j][i] > 0) {
                if (board[k][i] == board[j][i]) {
                    board[k--][i] *= 2;
                    board[j][i] = 0;
                    if_need_add_num = 1;
                }
                else if (board[k][i] == 0) {
                    board[k][i] = board[j][i];
                    board[j][i] = 0;
                    if_need_add_num = 1;
                }
                else {
                    board[--k][i] = board[j][i];
                    if (j != k) {
                        board[j][i] = 0;
                        if_need_add_num = 1;
                    }
                }
            }
        }
    }
}

3. 游戏页面的展示refresh_show();
根据数字长度、汉字长度、空格长度…一个个尝试出来的

/* 刷新界面 函数定义 */
void refresh_show() {
    clear_screen();

    printf("\n\n\n\n");
    printf("                              	 GAME: 2048     \n");
    printf("               --------------------------------------------------");

    /* 绘制方格和数字 */
    printf("\n\n                             ┌────┬────┬────┬────┐\n");
    int i;
    for (i = 0; i < 4; ++i) {
        printf("                             │");
        int j;
        for (j = 0; j < 4; ++j) {
            if (board[i][j] != 0) {
                if (board[i][j] < 10) {   //当元素小于10时,打印 
                    printf("  %d │", board[i][j]);
                }
                else if (board[i][j] < 100) {   //当元素大于10小于100时打印 
                    printf(" %d │", board[i][j]);
                }
                else if (board[i][j] < 1000) {  //当元素大于100小于1000时打印 
                    printf(" %d│", board[i][j]);
                }
                else if (board[i][j] < 10000) { //当元素大于1000小于10000时打印 
                    printf("%4d│", board[i][j]);
                }
                else {
                    int n = board[i][j];
                    int k;
                    for (k = 1; k < 20; ++k) {
                        n = n >> 1;
                        if (n == 1) {
                            printf("2^%02d│", k); //超过四位的数字用2的幂形式表示,如2^13形式 
                            break;
                        }
                    }
                }
            }
            else printf("    │");
        }

        if (i < 3) {
            printf("\n                             ├────┼────┼────┼────┤\n");
        }
        else {
            printf("\n                             └────┴────┴────┴────┘\n");
        }
    }
    printf("\n");
    printf("               --------------------------------------------------\n");
    printf("                  [W]:UP [S]:DOWN [A]:LEFT [D]:RIGHT [Q]:EXIT");
    }

4. 记录最高分

Write_max(int);  Read_max(); max_score;   
static const char* score_max = "2048游戏-最高记录.txt";

用文件来记录最高分并在游戏开始调用出最高分的记录
5.获取空位置
根据随机函数生成一个数,然后对空格数取余,然后在第余数个空格出生成数字。

int get_null_count() {
    int n = 0;
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            board[i][j] == 0 ? ++n : 1; //n为空格计数器 
        }
    }
    return n;
}

6.判断游戏是否结束

/* 检查游戏是否结束 函数定义 */
void check_game_over() {
    int i;
    for (i = 0; i < 4; ++i) {
        int j;
        for (j = 0; j < 3; ++j) {
            /* 横向和纵向比较挨着的两个元素是否相等,若有相等则游戏不结束 */
            if (board[i][j] == board[i][j + 1] || board[j][i] == board[j + 1][i]) {
                if_game_over = 0;
                return;
            }
        }
    }
    if_game_over = 1;
}

还有零零碎碎的一些刷新函数、重置游戏的函数、介绍规则的、鼠标的按键的……之后我把源码上传到GitHub上。到时候放链接。

2048的基本函数介绍(第二版)

第二版的伪代码展示

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值