【C语言】游戏 2048

【C语言】游戏 2048

实现函数列表

  游戏棋盘结构体
  菜单选项枚举
  查找可合并块偏移量结构体
  menu()菜单函数
  InitBoard()初始化棋盘函数
  PrintBoard()显示棋盘函数
  IsWin()判断输赢函数
 IsRound()判断盘内块可否合并函数
  AddBlock()随机增加方块函数
  MergeBlock()合并方块函数
  PlayAct()根据玩家行动调整方块函数
  RecallBoard()回溯一步函数
  Game()游戏实现函数

目前已知BUG

  1.回溯后行动一步再回溯时,无法回溯到上一步棋盘状态。
  2.行动时没有棋子改变位置也会随机增加块。
  
  正在解决……
  如果有小伙伴有好的思路,万望能分享!

实现思路

  在游戏中玩家可控制棋盘中棋子向四个方向滑动,有相同的就合并。合并出2048方块即获胜。
  首先这里有几个难点以及我的解决方法:
  1.判断盘内块可否合并
    一个块可以和上下左右四个方向合并,在做判断时,可以从数组第一个方块开始判断,只判断它他能否和它右边的方块合并,一行判断完再判断下一行,用递归来实现。
这里写图片描述

  2.判断根据玩家指令方向上的方块是否可以合并
    这里我为了减少代码量,构造了一个偏移量结构体,并定义了一个偏移量二维数组,数组的第一维是分别向四个方向判断的方向,数组的第二维则是这个方向上判断需要偏移的所有可能。
    (这样就可以不需要写很多判断语句)
  3.回溯上一次棋盘情况
    因为2048这个游戏只能退回一步,且不能让玩家在游戏过程中频繁的确认是否反悔,所以我这里定义了两个和棋盘数组相同的数组,一个中间数组,一个回溯数组,玩家不选择反悔时,就先将当前棋盘的情况存入中间数组,在玩家进行下一次操作后,再将中间数组的数复制到回溯数组中去。也就是回溯数组中记录的棋盘状态永远比中间数组的慢一拍,整个流程如下图:
这里写图片描述

效果展示

这里写图片描述
这里写图片描述

头文件

#ifndef __2048_H__
#define __2048_H__

#define _CRT_SECURE_NO_WARNINGS 1
#define SIZE_BOARD 4    //游戏棋盘长宽大小
#define INIT_BLOCK 2  //游戏开始时棋盘中有数字的块

#include <stdio.h>
#include <time.h>
#include <conio.h>
#include <Windows.h>

enum MENU   //菜单选项枚举
{
    EXIT,
    PLAY
};

typedef struct DISPLAY   //游戏棋盘结构体
{
    int score;           //分数
    int count;           //棋盘中非零棋子的个数
    int outscore;         //回溯分数
    int board[SIZE_BOARD][SIZE_BOARD];  //棋盘
    int tmp[SIZE_BOARD][SIZE_BOARD];  //中间棋盘
    int recall[SIZE_BOARD][SIZE_BOARD];  //中间棋盘
    int flag[SIZE_BOARD][SIZE_BOARD];  //使一个方块在一回合内只合并一次,已合并过置1,反之
}DISPLAY, *PDISPLAY;

typedef struct POS    //定义查找合并块的偏移量
{
    int x;
    int y;
}POS;

void menu();  //菜单

void InitBoard(PDISPLAY GameBoard);  //初始化游戏棋盘

void PrintBoard(PDISPLAY GameBoard);  //显示棋盘

int IsWin(PDISPLAY GameBoard); // 判断是输是赢

int IsRound(PDISPLAY GameBoard, int x, int y); //判断棋盘内的数周围有没有可以相加的数

void AddBlock(PDISPLAY GameBoard);    //每步增加一个方块

void MergeBlock(PDISPLAY GameBoard, int flag, int uplimit, int i, int j);    //合并方块

int PlayAct(PDISPLAY GameBoard);  //根据玩家行动调整方块

void RecallBoard(PDISPLAY GameBoard, int Command);   //回溯一步棋

void Game();   //游戏实现函数

#endif  //__2048_H__

功能实现函数文件

#include "2048.h"

POS PosXY[4][SIZE_BOARD - 1] = {    //定义默认棋盘大小下可能出现的偏移量    减少代码量
    { { -1, 0 }, { -2, 0 }, { -3, 0 } },
    { { 1, 0 }, { 2, 0 }, { 3, 0 } },
    { { 0, -1 }, { 0, -2 }, { 0, -3 } },
    { { 0, 1 }, { 0, 2 }, { 0, 3 } }
};

void menu()  //菜单
{
    printf("\t*************************\n");
    printf("\t***     1.开始游戏    ***\n");
    printf("\t***                   ***\n");
    printf("\t***     0.退出游戏    ***\n");
    printf("\t*************************\n");
}

void InitBoard(PDISPLAY GameBoard)  //初始化游戏棋盘
{
    int count = INIT_BLOCK;
    int i = 0;
    int j = 0;

    while (count--)
    {
        while (1)
        {
            int x = rand() % SIZE_BOARD;
            int y = rand() % SIZE_BOARD;

            if (GameBoard->board[x][y] == 0)
            {
                int tmp = rand() % 2;

                GameBoard->board[x][y] = ((tmp) ? (2) : (4));
                break;
            }
        }
    }
    for (i = 0; i < SIZE_BOARD; i++)
    {
        for (j = 0; j < SIZE_BOARD; j++)
        {
            GameBoard->tmp[i][j] = GameBoard->board[i][j];
        }
    }
}

void PrintBoard(PDISPLAY GameBoard)  //显示棋盘
{
    int i = 0;
    int j = 0;

    printf("\n\t当前分数:%d", GameBoard->score);
    printf("\n-----------------------------------\n\n");
    printf("\t┏━━┳━━┳━━┳━━┓\n");
    for (i = 0; i < SIZE_BOARD; i++)
    {
        printf("\t┃");
        for (j = 0; j < SIZE_BOARD; j++)
        {
            if (GameBoard->board[i][j] == 0)
            {
                printf("    ┃");
                continue;
            }
            printf("%4d┃", GameBoard->board[i][j]);
        }
        if (i < SIZE_BOARD - 1)
        {
            printf("\n\t┣━━╋━━╋━━╋━━┫\n");
        }
    }

    printf("\n\t┗━━┻━━┻━━┻━━┛\n");
    printf("\n\nW A S D 上 下 左 右\tP 反悔\n");
    printf("\n\n-----------------------------------\n");
}

int IsWin(PDISPLAY GameBoard) // 判断是输是赢
{
    int i = 0;
    int j = 0;

    GameBoard->count = 0;

    for (i = 0; i < SIZE_BOARD; i++)
    {
        for (j = 0; j < SIZE_BOARD; j++)
        {
            if (GameBoard->board[i][j] != 0)
                GameBoard->count++;
            else if (GameBoard->board[i][j] == 2048)    //胜利条件2048
                return 1;
        }
    }

    if (GameBoard->count == SIZE_BOARD*SIZE_BOARD && IsRound(GameBoard, 0, 0) == 0)
    {
        return -1;
    }

    return 0;
}
int IsRound(PDISPLAY GameBoard, int x, int y) //判断棋盘内的数周围有没有可以相加的数
{
    int ret = 0;

    if (x == SIZE_BOARD - 1)
    {
        ret = 0;
    }
    else if (y == SIZE_BOARD - 1)
    {
        IsRound(GameBoard, x + 1, 0);
    }
    else if (GameBoard->board[x][y] == GameBoard->board[x][y + 1])
    {
        ret = 1;
    }
    else if (GameBoard->board[x][y] != GameBoard->board[x][y + 1])
    {
        ret += IsRound(GameBoard, x, y + 1);
    }

    return ret;
}

void AddBlock(PDISPLAY GameBoard)    //每步增加一个方块
{
    while (1)
    {
        int x = rand() % SIZE_BOARD;
        int y = rand() % SIZE_BOARD;

        if (GameBoard->board[x][y] == 0)
        {
            int tmp = rand() % 2;

            GameBoard->board[x][y] = ((tmp) ? (2) : (4));
            break;
        }
    }
}

void MergeBlock(PDISPLAY GameBoard, int flag, int uplimit, int i, int j)    //合并方块
{
    int k = 0;

    if (GameBoard->board[i][j] == 0)
        return;

    for (k = 0; k < uplimit; k++)    //uplimit 控制寻找附近可合并方块的循环次数
    {
        if (GameBoard->board[i + PosXY[flag][k].x][j + PosXY[flag][k].y] == 0)
        {
            if (k == uplimit - 1)
            {
                GameBoard->board[i + PosXY[flag][k].x][j + PosXY[flag][k].y] = GameBoard->board[i][j];  //移动方块
                GameBoard->board[i][j] = 0;
                break;
            }
            continue;
        }
        else if (GameBoard->board[i][j] == GameBoard->board[i + PosXY[flag][k].x][j + PosXY[flag][k].y] &&
            GameBoard->flag[i + PosXY[flag][k].x][j + PosXY[flag][k].y] == 0)
        {
            GameBoard->board[i + PosXY[flag][k].x][j + PosXY[flag][k].y] *= 2;
            GameBoard->board[i][j] = 0;    //合并方块
            GameBoard->score += GameBoard->board[i + PosXY[flag][k].x][j + PosXY[flag][k].y];     //分数增加
            GameBoard->flag[i + PosXY[flag][k].x][j + PosXY[flag][k].y] = 1;
            break;
        }
        else if (GameBoard->board[i][j] != GameBoard->board[i + PosXY[flag][k].x][j + PosXY[flag][k].y] ||
            GameBoard->flag[i + PosXY[flag][k].x][j + PosXY[flag][k].y] == 1)
        {
            if (k == 0)
            {
                break;
            }
            GameBoard->board[i + PosXY[flag][k - 1].x][j + PosXY[flag][k - 1].y] = GameBoard->board[i][j];  //移动方块
            GameBoard->board[i][j] = 0;
            break;
        }
    }
}

int PlayAct(PDISPLAY GameBoard)  //根据玩家行动调整方块
{
    int i = 0;
    int j = 0;
    int ret = 0;

    switch (_getch())
    {
    case 'w':
    case 'W':
    {
        for (i = 1; i < SIZE_BOARD; i++)
        {
            for (j = 0; j < SIZE_BOARD; j++)
            {
                MergeBlock(GameBoard, 0, i, i, j);
            }
        }
    }
    break;
    case 's':
    case 'S':
    {
        for (i = SIZE_BOARD - 2; i >= 0; i--)
        {
            for (j = 0; j < SIZE_BOARD; j++)
            {
                MergeBlock(GameBoard, 1, SIZE_BOARD - i - 1, i, j);
            }
        }
    }
    break;
    case 'a':
    case 'A':
    {
        for (i = 0; i < SIZE_BOARD; i++)
        {
            for (j = 1; j < SIZE_BOARD; j++)
            {
                MergeBlock(GameBoard, 2, j, i, j);
            }
        }
    }
    break;
    case 'd':
    case 'D':
    {
        for (i = 0; i < SIZE_BOARD; i++)
        {
            for (j = SIZE_BOARD - 2; j >= 0; j--)
            {
                MergeBlock(GameBoard, 3, SIZE_BOARD - j - 1, i, j);
            }
        }
    }
    break;
    case 'p':
    case 'P': ret = 1;  //悔一步棋
        break;
    default:  ret = -1;
        break;
    }

    return ret;
}

void RecallBoard(PDISPLAY GameBoard, int Command)   //回溯一步棋
{
    int i = 0;
    int j = 0;

    if (Command)                                 //悔棋
    {
        for (i = 0; i < SIZE_BOARD; i++)
        {
            for (j = 0; j < SIZE_BOARD; j++)
            {
                GameBoard->board[i][j] = GameBoard->recall[i][j];
            }
        }
        GameBoard->score = GameBoard->outscore;
    }
    else    //将上一步棋存放
    {
        for (i = 0; i < SIZE_BOARD; i++)
        {
            for (j = 0; j < SIZE_BOARD; j++)
            {
                GameBoard->recall[i][j] = GameBoard->tmp[i][j];
                GameBoard->tmp[i][j] = GameBoard->board[i][j];
                GameBoard->flag[i][j] = 0;   //标志位置0,以便下一次使用
            }
        }
        GameBoard->outscore = GameBoard->score;
    }
}

void Game()   //游戏实现函数
{
    int count = 0;
    int ret = 0;             //玩家行动的返回值
    DISPLAY GameBoard = { 0 };  //游戏棋盘

    srand((unsigned)time(NULL));

    InitBoard(&GameBoard);  //初始化棋盘

    while (1)
    {
        system("cls");
        PrintBoard(&GameBoard);  //打印棋盘
        ret = PlayAct(&GameBoard);
        if (ret == 1)
        {
            RecallBoard(&GameBoard, ret);
            continue;
        }
        else if (ret == 0)
        {
            AddBlock(&GameBoard);
            RecallBoard(&GameBoard, 0);
        }
        ret = IsWin(&GameBoard);
        if (ret == 1)  //判断输赢 没赢没输返回0,赢了返回1,输了返回-1
        {
            system("cls");
            PrintBoard(&GameBoard);  //打印棋盘
            printf("\n你厉害,真的赢了!\n");
            system("pause");
            return;
        }
        else if (ret == -1)
        {
            system("cls");
            PrintBoard(&GameBoard);  //打印棋盘
            printf("\n呵呵,再接再厉吧!小伙子!\n");
            system("pause");
            return;
        }
    }
}

主函数文件

#include "2048.h"

int main()
{
    int input = 0;

    do
    {
        menu();
        printf("你想干嘛?:>");
        scanf("%d", &input);

        switch (input)
        {
        case PLAY: Game(), system("cls");
            break;
        case EXIT: printf("\n正在退出游戏\n");
            break;
        }
    } while (input);

    return 0;
}



全文完,感谢浏览

猜你喜欢

转载自blog.csdn.net/qq_41866437/article/details/80299351