C语言实现2048小游戏

代码参考:100 行代码撸了一个 2048 的小游戏

规则就是 2084 游戏的规则
j - 左
k - 下
l - 右
i - 上
相同数字移动过程中会合并

这里我把按键修改成了方向键,并加了些注释帮助理解

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

#define GAME_SIZE 4

static void left(int *data)
{
    int i, j, f;//f标志位判断是否经过合并,每一行(列)有且只能合并一次 
    int row = GAME_SIZE;
    while (row--) {
        for (f=0,j=0,i=0; i<GAME_SIZE; i++) {
            if (data[i]) {
                if (!f && j>0 && data[j-1] == data[i]) {
                    data[j-1]+= data[i]; f = 1;//j左边有非0格,且与i格相等才合并 
                } else {
                    data[j++] = data[i]; f = 0;//j记录0格下标 (j在0格和相等合并时不变,否则+1) 
                }
            }
        }
        while (j < GAME_SIZE) data[j++] = 0;//填充剩余格 
        data += GAME_SIZE;//地址偏移4格 
    }
}

static void right(int *data)
{
    int i, j, f;
    int row = GAME_SIZE;
    while (row--) {
        for (j=GAME_SIZE-1,f=0,i=GAME_SIZE-1; i>=0; i--) {
            if (data[i]) {
                if (!f && j<GAME_SIZE-1 && data[j+1] == data[i]) {
                    data[j+1]+= data[i]; f = 1;
                } else {
                    data[j--] = data[i]; f = 0;
                }
            }
        }
        while (j >= 0) data[j--] = 0;
        data += GAME_SIZE;
    }
}

static void up(int *data)
{
    int i, j, f;
    int col = GAME_SIZE;
    while (col--) {
        for (j=0,f=0,i=0; i<GAME_SIZE; i++) {
            if (data[i*GAME_SIZE]) {
                if (!f && j>0 && data[(j-1)*GAME_SIZE] == data[i*GAME_SIZE]) {
                    data[(j-1)*GAME_SIZE]+= data[i*GAME_SIZE]; f = 1;
                } else {
                    data[(j++)*GAME_SIZE] = data[i*GAME_SIZE]; f = 0;
                }
            }
        }
        while (j < GAME_SIZE) data[(j++)*GAME_SIZE] = 0;
        data++;
    }
}

static void down(int *data)
{
    int i, j, f;
    int col = GAME_SIZE;
    while (col--) {
        for (j=GAME_SIZE-1,f=0,i=GAME_SIZE-1; i>=0; i--) {
            if (data[i*GAME_SIZE]) {
                if (!f && j<GAME_SIZE-1 && data[(j+1)*GAME_SIZE] == data[i*GAME_SIZE]) {
                    data[(j+1)*GAME_SIZE]+= data[i*GAME_SIZE]; f = 1;
                } else {
                    data[(j--)*GAME_SIZE] = data[i*GAME_SIZE]; f = 0;
                }
            }
        }
        while (j >= 0) data[(j--)*GAME_SIZE] = 0;
        data++;
    }
}

static int next(int *data){
    int empidx[GAME_SIZE*GAME_SIZE];
    int empnum = 0;
    int max = 0;
    int i,j = 0;
    
    for(j=0,i=0; i<GAME_SIZE*GAME_SIZE; i++){
        if(!data[i]){
            empidx[j++]=i;//记录0格的偏移地址 
            empnum++;//判断有几格为0 
        }
        max = max > data[i] ? max : data[i];//保留最大值判断是否游戏已结束 
    }
    if(empnum){
        data[empidx[rand()%empnum]] = 1;
        return max == 2048 ? 1 : 0;
    }
    else return -1;
}

static void output(int *data){//通过判断i是否被4整除来确定是否换行 
    int i, sum = 0;
    printf("+--------------------+\n\n");
    for (i=1; i<=GAME_SIZE*GAME_SIZE; i++) {
        if(data[i-1]){
            printf("%4d %s", data[i-1], i%GAME_SIZE ? "" : "\n\n");
        }else{
            printf("%s %s", "   .", i%GAME_SIZE ? "" : "\n\n");
        }
        sum+=data[i-1];
    }
    printf("+--------------------+\n\n");
    printf("分数:%d\n\n", sum);
}

int main(){
    int data[GAME_SIZE * GAME_SIZE] = {0};
    int ret = 0;
    
    next(data);
    next(data);
    output(data);
    
    while(1){
        int c;
        if(c=getch()){
            if(c==72||c==75||c==77||c==80) continue;//功能键是两个字节,输入错误则跳过一次 
            c=getch();
            if(c==75) left (data);
            else if(c==77) right(data);
            else if(c==72) up   (data);
            else if(c==80) down (data);
            else if(c=='q') return 0;//q也需要按两次才退出 
            else continue;
        }
        ret = next(data);
        output(data);
        
        if (ret == -1) printf("game over !\n");
        if (ret ==  1) printf("you  win  !\n");
    }
}

我的测试环境为 Dev-C++ 5.11,亲测有效,如果使用linux 下 gcc 编译,需要解决 getch 问题,具体参考原博客

算法主要就是 left right up down 四个函数,这里给出 left 函数的前两位图解

准确的说,i 代表当前遍历格地址, j代表当前填充格地址(如果合并填充j-1格),f为合并标志位(每一行(列)只能合并一次)

 

如果用getch()捕获上、下、左、右键

上、下、左、右键是二个字节的,getch()只读一个字节,ASC码
想要用getch()得到上、下、左、右键的话,要调用二次getch():

可参考博客:C语言使用getch()读取方向键

getch函数在读取一个功能键或者箭头(方向)键盘时,函数会返回两次,第一次调用返回0或者0xE0,第二次调用返回实际的键值,所以使用两次getch()即可。 

posted @ 2018-07-05 01:14  Johnny、  阅读(773)  评论(0编辑  收藏  举报