制作视频


我B站上也有号哒!!快快点个赞吧!!!(如果三连的话,也不是不行,要是这样子的话那我就太爱你啦!)、
这个的话就不是本次讲解的代码,是新的2048
https://www.bilibili.com/video/BV1tD4y1R7BS/

当然我这里也有一个一模一样的视频啦

2048小游戏

仿照RainbowRoad1的代码,进行了一丢丢的小修改
C语言实现2048,在控制台实现,有最基本的功能。
这个本来构思了很久,网上的都是直接枚举,冗余多,但浓缩下来也很长,算法还是比较复杂。经过无数次重写后,完全换了一个算法,很不容易。

顺带把最初的版本编译了,color版本是后来出的,就懒了没有搞出来了

代码解释

不想看解释的直接翻上面找代码

引入头文件

#include<stdlib.h>
#include<conio.h>

首先定义一些变量

/* map 地图;
 * score 分数;
 * game 为0时游戏结束,
 * c 输入缓存;
 * i 循环变量;
 * j 用于检测是否有空地;
 * k 移动是否有效
int map[16] = {0}, score = 0, game = 1, c, i, j, k;

之后我们搞个墙

const char* wall[4] = { "\n----- ----- ----- -----\n","|","|","|" };

move()

函数原型:void move(int i,int v)

参数含义:int i 为要移动的格子,int v 为移动值

函数作用:用于移动

void move(int i,int v) {
    if (i + v < 0 || 15 < i + v || !map[i])return;
    if ((v == 1 || v == -1) && i / 4 - (i + v) / 4)return;
    if (map[i + v] == map[i])map[i + v] *= -2, score += map[i], map[i] = k = 0;
    if (!map[i + v])map[i + v] = map[i], map[i] = k = 0, move(i + v, v);
}

那我们由于是一位数组储存,所以越界判断比较抽象

if (i + v < 0 || 15 < i + v || !map[i])return; 中判断新位置是否上下越界,是否为空地,是则直接return

判断是否左右越界,是则直接返回我们就可以if ((v == 1 || v == -1) && i / 4 - (i + v) / 4)return;

新位置和旧位置不在同一行就是左右越界,也很好理解

if (map[i + v] == map[i])map[i + v] *= -2, score += map[i], map[i] = k = 0; 判断新位置是否和本身的值相同

那我们相同的话就合并了嘛,新位置的值*2,令本身 = 0 && 增加分数(也就是score)

我们为了防止一个数反复合并,所以合并后为负数,后面我们再进行取反

在移动后,利用if (map[i + v] == map[i])map[i + v] *= -2, score += map[i], map[i] = k = 0; 判断新位置是否为空地

如果是空地就还要继续移动,递归调用自己;在此处的map[i] = k = 0,说明下,只有产生了移动k才会等于0,避免没有产生移动还会继续生成

order()

函数原型:void order(int end,int i,int v)

参数含义:begin 迭代;end 结束值;i 迭代方向;v 移动值

函数作用:顺序,按照一定顺序执行移动

void order(int end,int i,int v) {
    for (int begin = end == 16 ? 0 : 15; begin - end; begin += i)move(begin, v);
}

for (int begin = end == 16 ? 0 : 15; begin - end; begin += i)move(begin, v); 只用一次遍历就兼容4种情况(算法由Rainbowroad1想出)

main()

int main() {
    system("mode con: cols=23 lines=9");
    for (srand((unsigned)malloc(1)), c = j = 0; game; j = k = 1, c = _getch()) {
        if (c == 'a' || c == 'w')order(16, 1, c == 'a' ? -1 : -4);
        else if (c == 'd' || c == 's')order(-1, -1, c == 'd' ? 1 : 4);
        for (i = 0; i < 16; i++)map[i] < 0 && (map[i] = -map[i]), map[i] || (j = 0);
        do if (i = rand() % 16, j || k)break;
        while (map[i] || (map[i] = rand() % 5 ? 2 : 4, 0));
        for (i = 0, system("cls"); j && (i < 15 || (game = 0)); i++)
            if (i < 12 && map[i] == map[i + 4] ||
                i + 1 & 3 && map[i] == map[i + 1])break;
        if (game || _cprintf("Game over!"))_cprintf("score:%d", score);
        for (i = 0; i < 16; i++)_cprintf("%s%5d", wall[i & 3], map[i]);
    }
}

system("mode con: cols=23 lines=9"); 设置窗口大小

当然2048少不了循环,我们使用 for (srand((unsigned)malloc(1)), c = j = 0; game; j = k = 1, c = _getch()) 建立游戏主循环

循环初始:初始化随机种子和部分变量;

循环条件:game == 0,游戏结束

循环最后:复位部分变量,并获取一次输入

if (c == 'a' || c == 'w')order(16, 1, c == 'a' ? -1 : -4); 判断输入,根据输入来响应移动(那么我们就用到了order)

接着 下面的那行原理是相同的

之前我们有个变量是写了相反数的以防止反复合并,那么我们在这里在这里就取反吧 for (i = 0; i < 16; i++)map[i] < 0 && (map[i] = -map[i]), map[i] || (j = 0);

同时判断是否有空地,跟前面负数相对应,如果有空地,j = 0

do if (i = rand() % 16, j || k)break; 生成一个新数字;先取一个随机值;如果没有空地或者没有产生移动,直接跳出不生成

while (map[i] || (map[i] = rand() % 5 ? 2 : 4, 0)); 如果对应的位置是空地,则生成新数字;80%概率为2,20%概率为4

有一个细节,最后要记得整个表达式为0,不然会陷入死循环⚠️

for (i = 0, system("cls"); j && (i < 15 || (game = 0)); i++)清屏,判断失败条件(只有场上无空地才开始判断)

为了判断竖向能否合并,if (i < 12 && map[i] == map[i + 4] || i + 1 & 3 && map[i] == map[i + 1])break;最后四个格子跳过判断

注意,最后一个格子不能参与判断,所以我们循环次数是15 != 16

到最后了,if (game || _cprintf("Game over!"))_cprintf("score:%d", score); 打印分数,如果结束额外打印失败提示

for (i = 0; i < 16; i++)_cprintf("%s%5d", wall[i & 3], map[i]);打印地图整体,把墙也打印出来(Rainbowroad1)

那程序就结束啦!(return 0;自己加去吧

2048(old).c

这个就是我这篇文章讲的

#include<stdlib.h>
#include<conio.h>
int map[16] = {0}, score = 0, game = 1, c, i, j, k;
const char* wall[4] = { "\n----- ----- ----- -----\n","|","|","|" };
void move(int i,int v) {
    if (i + v < 0 || 15 < i + v || !map[i])return;
    if ((v == 1 || v == -1) && i / 4 - (i + v) / 4)return;
    if (map[i + v] == map[i])map[i + v] *= -2, score += map[i], map[i] = k = 0;
    if (!map[i + v])map[i + v] = map[i], map[i] = k = 0, move(i + v, v);
}
void order(int end,int i,int v) {
    for (int begin = end == 16 ? 0 : 15; begin - end; begin += i)move(begin, v);
}
int main() {
    system("mode con: cols=23 lines=9");
    for (srand((unsigned)malloc(1)), c = j = 0; game; j = k = 1, c = _getch()) {
        if (c == 'a' || c == 'w')order(16, 1, c == 'a' ? -1 : -4);
        else if (c == 'd' || c == 's')order(-1, -1, c == 'd' ? 1 : 4);
        for (i = 0; i < 16; i++)map[i] < 0 && (map[i] = -map[i]), map[i] || (j = 0);
        do if (i = rand() % 16, j || k)break;
        while (map[i] || (map[i] = rand() % 5 ? 2 : 4, 0));
        for (i = 0, system("cls"); j && (i < 15 || (game = 0)); i++)
            if (i < 12 && map[i] == map[i + 4] ||
                i + 1 & 3 && map[i] == map[i + 1])break;
        if (game || _cprintf("Game over!"))_cprintf("score:%d", score);
        for (i = 0; i < 16; i++)_cprintf("%s%5d", wall[i & 3], map[i]);
    }
}

2048.c

#include<stdlib.h>
#include<conio.h>
int m[36] = { 0 }, score = 0, can = 0, air = 16, c = 0, i = 6, j, *p;
void move(int *q, int v) {
    if (*q < 1 ? 0 : q[v] || (q[v] = *q, move(q + v, v), *q = can = 0))
        q[v] - *q || (q[v] = ~*q, score += 1 << *q, *q = can = 0, ++air);
}
void order(int b, int v) { b - j && ((move(m + b, v), order(b + i, v), 0)); }
int wall() { return _cprintf("\n----- ----- ----- ----- \n"); }
int main() {
    for (p = malloc(1); i--;)m[i] = m[35 - i] = m[i * 6] = m[35 - i * 6] = -1;
    for (srand(p), free(p); (air || can) && c - 27; c = _getch() & 95) {
        c - 'A' && c - 'W' || (j = 30, i = 1, order(6, c - 'A' ? -6 : -1), 0);
        c - 'D' && c - 'S' || (j = 6, i = -1, order(30, c - 'D' ? 6 : 1), 0);
        if (!air || can || (--air, (system("cls"))))continue;
        while (m[i = rand() % 30] || (m[i] = rand() % 5 ? 1 : 2, 0));
        for (p = m + 30; --p - m - 6; *p < -1 && (*p = -*p));
        for (; ++p - m - 31; *p + 1 && _cprintf("%5d|", *p ? 1 << *p : 0))
            *p + 1 && (*p ^ p[1] && *p ^ p[6] || ++can), (p - m) % 6 || wall();
        air || can || _cputs("Game over!"), _cprintf("score:%d", score);
    }
    _cprintf("\nMade by wibus & RainbowRoad1\n");
    for (_cputs("\n### Press space to exit ###\n"); _getch() - ' ';);
}

2048(color).c

#include <windows.h>
#include <conio.h>
int bg[] = {15, 240, 144, 160, 176, 192, 208, 224, 16, 32, 48, 64, 80};
int m[36] = { 0 }, score = 0, can = 0, air = 16, c = 0, i = 6, j, *p;
void move(int *q, int v) {
    if (*q < 1 ? 0 : q[v] || (q[v] = *q, move(q + v, v), *q = can = 0))
        q[v] - *q || (q[v] = ~*q, score += 1 << *q, *q = can = 0, ++air);
}
void order(int b, int v) { b - j && ((move(m + b, v), order(b + i, v), 0)); }
int main() {
    for (p = malloc(1); i--;)m[i] = m[35 - i] = m[i * 6] = m[35 - i * 6] = -1;
    for (srand(p), free(p); (air || can) && c - 27; c = _getch() & 95) {
        c - 'A' && c - 'W' || (j = 30, i = 1, order(6, c - 'A' ? -6 : -1), 0);
        c - 'D' && c - 'S' || (j = 6, i = -1, order(30, c - 'D' ? 6 : 1), 0);
        if (!air || can || (--air, (system("cls"))))continue;
        while (m[i = rand() % 30] || (m[i] = rand() % 5 ? 1 : 2, 0));
        for (p = m + 30; --p - m - 6; *p < -1 && (*p = -*p));
        for (; ++p - m - 31; *p + 1 && _cprintf("%5d", *p ? 1 << *p : 0))
            SetConsoleTextAttribute(GetStdHandle((DWORD)-11), bg[*p % 12 + 1]),
            *p + 1 && (*p ^ p[1] && *p ^ p[6] || ++can),
            (p - m) % 6 || _cputs("\n");
        air || can || _cputs("Game over!"), _cprintf("score:%d", score);
    }
        _cprintf("\nMade by wibus & RainbowRoad1\n");
    for (_cputs("\n### Press space to exit ###\n"); _getch() - ' ';);
}
最后修改:2020 年 10 月 31 日 10 : 34 AM
如果觉得我的文章对你有用,请随意赞赏

发表评论

7 条评论

  1. PuTong

    不错挺好的awa虽然不会C

    1. lingXI - 灵汐
      @PuTong

      az

      1. PuTong
        @lingXI - 灵汐

        阿瓦|´・ω・)ノ

    2. 茶栀
      @PuTong

      淦????

      1. PuTong
        @茶栀

      2. wibus
        @茶栀

        淦????

    3. wibus
      @PuTong

      ٩(ˊᗜˋ*)و

域名代备案/服务器虚拟主机售卖/二级不死域名代制作/
老备案老域名/营业执照代办/QQ互联代申请/
海报宣传图设计/各类程序授权/各类业务

联系QQ:1032066668
点击联系