zoukankan      html  css  js  c++  java
  • 基于STL实现自动贪心寻路算法的贪吃蛇小游戏

    基于STL实现自动贪心寻路算法的贪吃蛇小游戏


    写贪吃蛇小游戏的想法来自CometOJ-Contest#13的B题,当时用STL双端队列维护蛇身的时候觉得非常方便,现在用EasyX图形库实现一下。


    运行截图:

    脑残贪吃蛇

    欢迎界面:

    普通模式:

    无敌模式:


    实现思路:

    代码很短,写的时候基本上是想到哪写到哪。

    现在看的话大概要分成四部分。

    一、初始化,全局控制等

    初始化所有信息,模式选择等。

    二、蛇身

    存储蛇位置,运动方向,控制蛇移动,更改方向等。

    蛇身用std::deque实现,dq的前后端添加及弹出非常方便,不用过多担心溢出问题。

    三、地图

    存储下一个食物的坐标和已经被蛇身覆盖的坐标,对于普通模式可以判断是否会撞到自己的身体。

    四、绘制

    用exayX库在屏幕上绘制已经计算好的图形并显示。


    几个point:

    ✦记录运行方向,可以快速计算蛇头下一个位置,同余坐标即可实现穿墙功能。

    ✦每次移动蛇尾pop一个单位,蛇头push一个单位即可,当蛇头吃到食物则蛇尾不用pop。

    ✦普通模式下蛇头撞向蛇身结束游戏。

    ✦无敌模式下可以蛇身覆盖。

    ✦Q键切换到自动模式。自动寻找食物采用曼哈顿距离贪心算法,无敌模式下没有问题,普通模式下可能会撞死自己(考虑到可以种子填充判断,但是时空复杂度要大一倍,没写(懒))。

    ✦游戏自动加速,初始时游戏速度为10fps,随着分数增加fps线性增加,当fps达到100(10ms/帧)时达到上限,游戏速度不再增加。


    代码:

    /*
        Zeolim - An AC a day keeps the bug away
    */
    ​
    #include <graphics.h> 
    #include <iostream>
    #include "stdafx.h"
    #include <easyx.h>
    #include <random>
    #include <conio.h>
    #include <queue>
    #include <unordered_map>
    #include <time.h>
    #include <map>
    #include <process.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    typedef std::pair <int, int> pii;
    typedef std::vector <int> vi;
    ​
    #define mp(x, y) make_pair(x, y)
    #define fr(x, y, z) for(int x = y; x < z; ++x)
    #define pb(x) push_back(x)
    #define mem(x, y) memset(x, y, sizeof(x))
    ​
    const int ROW = 32;
    const int COL = 48;
    const int lineHigh = 17;
    const int mov[4][2] = { { 1, 0 },{ 0, 1 },{ 0, -1 },{ -1,0 } };
    int gameMode = 0;
    int runSpd = 100;
    ​
    int coler[7][3] =
    {
        { 85,107,47 }, //WALL
        { 245,245,245 }, //Back
        { 211,211,211 } // line
    };
    ​
    bool fucked, autoC; //记录是否死亡,和是否开启自动模式
    ​
    struct rmap
    {
        int dmap[COL][ROW];
        int x, y;
        void init() //初始化地图
        {
            memset(dmap, 0, sizeof(dmap));
            getFood();
        }
        void getFood() //生成下一个食物
        {
            do
            {
                x = rand() % COL, y = rand() % ROW;
            } while (dmap[x][y] != 0);
        }
        bool isFucked(int x, int y) //判断是否死亡
        {
            return dmap[x][y] == 1;
        }
    }MP;
    ​
    struct body
    {
        std::deque < pii > D;
        int dir;
        void init() //初始化蛇身 方向
        {
            D.clear();
            dir = 1; 
            D.push_back(std::make_pair(COL / 2, ROW / 2));
            D.push_back(std::make_pair(COL / 2, ROW / 2 - 1));
            D.push_back(std::make_pair(COL / 2, ROW / 2 - 2));
            for (auto x : D)
                ++MP.dmap[x.first][x.second];
        }
        void move() //向当前运行方向前方移动一格
        {
            pii p = D.front();
            int x = (p.first + mov[dir][0] + COL) % COL;
            int y = (p.second + mov[dir][1] + ROW) % ROW;
            if (p.first == MP.x && p.second == MP.y) //如果迟到食物 生成新食物
            {
                MP.getFood();
            }
            else //没有吃到尾巴缩短一格
            {
                pii x = D.back();
                --MP.dmap[x.first][x.second];
                D.pop_back();
            }
    ​
            if (gameMode == 1 && MP.isFucked(x, y)) //判断死亡
            {
                fucked = 1;
            }
            
            ++MP.dmap[x][y];     //更新蛇头
            D.push_front(std::make_pair(x, y));
        }
        void ChangeDir(char x) //更新方向
        {
            int _dir = -1;
            if (x == 'A' || x == 'a' && dir != 0)
                _dir = 3;
            if (x == 'D' || x == 'd' && dir != 3)
                _dir = 0;
            if (x == 'W' || x == 'w' && dir != 1)
                _dir = 2;
            if (x == 'S' || x == 's' && dir != 2)
                _dir = 1;
            if (_dir != -1)
                dir = _dir;
        }
    }Snk;
    ​
    void drawMap()
    {
        BeginBatchDraw(); //开始绘制
        setbkcolor(RGB(coler[1][0], coler[1][1], coler[1][2])); //绘制背景色
        cleardevice();
    ​
        Snk.move(); //蛇移动
        setlinecolor(RGB(211, 211, 211));  
        for (int i = 0; i < COL; ++i)  //绘制网格
        {
            for (int j = 0; j < ROW; ++j)
            {
                rectangle(i * 10, j * 10 + lineHigh, i * 10 + 10, j * 10 + 10 + lineHigh);
            }
        }
    ​
        setlinecolor(RGB(30, 144, 255));
        setfillcolor(RGB(176, 224, 230));
    ​
        for (int i = 0; i < Snk.D.size(); ++i) //绘制蛇 注意坐标等比例扩大
        {
            pii x = Snk.D[i];
            fillrectangle(x.first * 10, x.second * 10 + lineHigh, x.first * 10 + 10, x.second * 10 + 10 + lineHigh);
        }
    ​
        setlinecolor(RGB(255, 69, 0));
        setfillcolor(RGB(255, 140, 0));
        fillcircle(MP.x * 10 + 5, MP.y * 10 + 5 + lineHigh, 5); //绘制食物
    ​
        setlinecolor(RGB(30, 144, 255));
        setfillcolor(RGB(225, 255, 255));
        pii x = Snk.D.front();
        fillrectangle(x.first * 10, x.second * 10 + lineHigh, x.first * 10 + 10, x.second * 10 + 10 + lineHigh);
    ​
        WCHAR text[200];
        settextcolor(RGB(0, 0, 0)); //绘制分数,模式等信息
        wsprintf(text, L"Score: %06d     GameSpeed: %06d     (q) AutoMode: (%d)     (e)Exit", Snk.D.size(), runSpd, autoC);
        outtextxy(20, 0, text);
        EndBatchDraw(); //结束绘制
    }
    ​
    void drawWelcome()
    {
        BeginBatchDraw();
        setbkcolor(RGB(coler[1][0], coler[1][1], coler[1][2]));
        cleardevice();
    ​
        WCHAR text[50];
        settextcolor(RGB(0, 0, 0));
        wsprintf(text, L"Input Game Mode:  1.Normal  2.Who`s your daddy!");
        outtextxy(COL / 2 * 3, ROW / 2 * 8, text);
    ​
        wsprintf(text, L"Auther: Zeolim");
        outtextxy(COL / 2 * 8, ROW / 2 * 11, text);
        EndBatchDraw();
    ​
        do //获取键盘输入并更改模式
        {
            char x = _getch();
            if (x == '1')
                gameMode = 1;
            if (x == '2')
                gameMode = 2;
        } while (gameMode == 0);
    ​
        fucked = 0, autoC = 0;
    }
    ​
    void autoChange() //曼哈顿距离贪心
    {
        int x = Snk.D.front().first, y = Snk.D.front().second;
        int nowdis = abs(x - MP.x) + abs(y - MP.y), rdis = 1000, p = -1;
        for (int i = 0; i < 4; ++i)
        {
            if (i + Snk.dir == 3)
                continue;
            else
            {
                int rx = (x + mov[i][0] + COL) % COL, ry = (y + mov[i][1] + ROW) % ROW;
                if (gameMode == 1 && MP.isFucked(rx, ry)) //模式1 下一格必死不能走 
                    continue;
                if (abs(rx - MP.x) + abs(ry - MP.y) < rdis) //贪心选择更新
                {
                    rdis = abs(rx - MP.x) + abs(ry - MP.y);
                    p = i;
                }
            }
        }
        if (p != -1) //更新方向
            Snk.dir = p;
    }
    ​
    ​
    int main()
    {
        srand(time(0));
        initgraph(COL * 10, ROW * 10 + lineHigh);
    ​
        while (true)
        {
            gameMode = 0;
            MP.init();
            Snk.init();
    ​
            drawWelcome(); //绘制主页面
    ​
            double t1, t2;
    ​
            t1 = GetTickCount();
            while (true)
            {
                runSpd = max(10, 100 - int(Snk.D.size()));
                t2 = GetTickCount();
    ​
                if (t2 >= t1 + runSpd)
                {
                    t1 = GetTickCount();
    ​
                    drawMap();
    ​
                    if (_kbhit()) //keyboard hit 该函数为非阻塞函数 未敲击时不影响其他函数继续运行
                    {
                        auto x = _getch(); //获取一个字符
                        if (x == 'q' || x == 'Q') //更新模式
                            autoC ^= 1;
                        else if (x == 'e' || x == 'E') //退出到主菜单
                            break;
                        else
                        {
                            Snk.ChangeDir(x); //更新方向 wasd
                            continue;
                        }
                    }
                    if (autoC) //如果自动 则寻找下一个位置
                    {
                        autoChange();
                    }
                }
    ​
                if (fucked) //死亡退出
                {
                    MessageBox(GetHWnd(), L"You fucked!", L"SORRY", MB_OK);
                    break;
                }
            }
        }
    ​
        return 0;
    }

    Done!

  • 相关阅读:
    linux学习笔记---grep
    node.js读取到的文件列表
    node 按行读取文件
    NodeJS遍历文件生产文件列表
    常用linux命令行
    2017/11/13
    Linux下输出 excel文件
    位映射对大数据的排重
    算法中的渐进记号
    KMP算法原理
  • 原文地址:https://www.cnblogs.com/zeolim/p/12270314.html
Copyright © 2011-2022 走看看