快乐的两条贪吃蛇 C++控制台小游戏开发

C++实验课要求通过控制台编写一个简易的贪吃蛇游戏,在2个多小时的艰难探索后,终于完成了。之后习题课讲解这道题时,我望着屏幕上孤零零的贪吃蛇,不免有些难过。于是,我大幅度改造了游戏架构,通过类实现第二条蛇的生成,并编写AI控制程序。以下是实现的C++代码和成品下载。

导读


游戏界面

// Snakes.h :头文件
//用于声明Snake的类
#include <vector>
#include <stdexcept> 
using namespace std;
class Snake
{
public:
    void generate(int,int);
    int getX(int);
    int getY(int);
    int getLength();
    void eat();
    void move(int);
private:
    vector<int> snakeX;
    vector<int> snakeY;
    int food = 0;
    int way = 0;
};
// Snakes.cpp 
//说明Snake类的作用
#include <iostream>
#include <Windows.h>
#include <array>
#include <stdio.h>
#include <cstdlib>
#include <ctime>
#include Snakes.h
void Snake::move(int iWay)
{
    if (way != 2 && iWay==1) //防止蛇掉头致死
    {
        way = 1;
    }
    else if (way != 1 && iWay == 2) 
    {
        way = 2;
    }
    else if (way != 4 && iWay == 3) 
    {
        way = 3;
    }
    else if (way != 3 && iWay == 4) 
    {
        way = 4;
    }
    if (way != 0)
    {
        for (size_t i = food; i > 0; i--)//确保每一段蛇身紧跟着前一段蛇身的位置
        {
            if (food == 0)
            {
                break;
            }
            snakeX[i] = snakeX[i - 1];
            snakeY[i] = snakeY[i - 1];
        }
    }
    switch (way)//设别方向并改变坐标
    {
    default:
        break;
    case 0:
        break;
    case 1:
        snakeY[0]--;
        break;
    case 2:
        snakeY[0]++;
        break;
    case 3:
        snakeX[0]--;
        break;
    case 4:
        snakeX[0]++;
        break;
    }

}
void Snake::eat()
{
    snakeX.push_back(snakeX[0]);
    snakeY.push_back(snakeY[0]);
    food++;
}
int Snake::getLength()
{
    return food;
}
int Snake::getX(int i)
{
    return snakeX[i];
}
int Snake::getY(int i)
{
    return snakeY[i];
}
void Snake::generate(int x,int y)//生成蛇
{
    snakeX.push_back(x);
    snakeY.push_back(y);
}
// SnakeMain.cpp : Defines the entry point for the console application.
//两条贪吃蛇的实现
#include <iostream>
#include <Windows.h>
#include <array>
#include <stdio.h>
#include <ctime>
#include Snakes.h
#include <string>
#define KEY_DOWN(vk_code) GetAsyncKeyState(vk_code) & 0x8000 ? 1 : 0  //用于判断按键输入
using namespace std;
const int xb = 40, yb = xb;//边界大小,如果屏幕较小可以适当缩小该数值
array < array<int,xb+2>,yb+2 > map;//二维数组,-1蛇头 0空气 1砖块 2蛇身 3食物 
int iway = 0;
void generateMap()//为map数组赋值,添加砖块
{
    int x=0, y=1;
    while (true)
    {
        x++;
        if (x>xb)//判断坐标,防止越界
        {
            x = 1;
            y++;
        }
        if (y > yb)
        {
            break;
        }
        if (x==1 || x==xb || y==1 ||y==yb)
        {
            map[x][y] = 1;
        }
        else
        {
            map[x][y] = 0;
        }

    }
    map[2 + rand() % (xb - 1)][2 + rand() % (yb - 1)] = 3;
}
void printMap()//在控制台上打印游戏界面,使用string变量统一打印可以缓解屏闪问题
{
    size_t x = 0, y = 1;
    string str;
    while (true)
    {
        x++;
        if (x>xb)
        {
            x = 1;
            y++;
            str = str + \n;
        }
        if (y > yb)
        {
            break;
        }
        switch (map[x][y])
        {
        default:
            break;
        case -1:
            str = str + ■;
            break;
        case 0:
            str = str +   ;
            break;
        case 1:
            str = str + ■;
            break;
        case 2:
            str = str + ■;
            break;
        case 3:
            str = str + ■;
            break;
        }
    }
    system(cls);
    cout << str;
}
void clearMap()//清除残留蛇身信息,使程序更新蛇身位置
{
    size_t x = 0, y = 1;
    while (true)
    {
        x++;
        if (x>xb)
        {
            x = 1;
            y++;
        }
        if (y > yb)
        {
            break;
        }
        if (map[x][y] == 2 || map[x][y]==-1)
        {
            map[x][y] = 0;
        }
    }
}
int main()
{
    generateMap();
    srand(time(0));
    Snake player1;
    Snake player2;//可以多次添加增加更多只蛇
    player1.generate(xb / 2, yb / 2);
    player2.generate(xb / 3, yb / 3);
    while (true)
    {
        printMap();
        Sleep(10);//延迟10秒

        if (KEY_DOWN(VK_UP)) //判断键盘按下按钮
        {
            player1.move(1);
        }
        else if (KEY_DOWN(VK_DOWN)) {
            player1.move(2);
        }
        else if (KEY_DOWN(VK_LEFT)) {
            player1.move(3);
        }
        else if (KEY_DOWN(VK_RIGHT)) {
            player1.move(4);
        }
        else//没按下按钮默认原方向(见Snakes.cpp)
        {
            player1.move(0);
        }

        int ix = 0, iy = 0,oldway=iway;//预先判断AI方向,确保无障碍
        switch (iway)
        {
        default:
            break;
        case 1:
            iy = -1;
            break;
        case 2:
            iy = 1;
            break;
        case 3:
            ix = -1;
            break;
        case 4:
            ix = 1;
            break;
        }
        if (rand() % 4 == 0 || map[player2.getX(0) + ix][player2.getY(0) + iy] != 0)
        {
            while (true)
            {
                ix = 0; iy = 0;
                iway = 1 + rand() % 4;//随机试探方向
                if (oldway == 2 && iway == 1) //防止AI直接掉头
                {
                    iway = 2;
                }
                else if (oldway == 1 && iway == 2) {
                    iway = 1;
                }
                else if (oldway == 3 && iway == 4) {
                    iway = 3;
                }
                else if (oldway == 4 && iway == 3) {
                    iway = 4;
                }
                switch (iway)
                {
                default:
                    break;
                case 1:
                    iy = -1;
                    break;
                case 2:
                    iy = 1;
                    break;
                case 3:
                    ix = -1;
                    break;
                case 4:
                    ix = 1;
                    break;
                }
                if (map[player2.getX(0) + ix][player2.getY(0) + iy] == 3)//如果周围有食物,试探结束
                    break;

                if (map[player2.getX(0) + ix][player2.getY(0) + iy] == 0)//如果周围是空气,试探结束
                    break;
                if (rand() % 10000 == 0)//如果四周都无法通过,则随机选择一个方向
                {
                    break;
                }
            }
        }
        if (map[player2.getX(0) + 1][player2.getY(0)] == 3)//遍历四个方向,如果有食物,则锁定该方向
            iway = 4;
        if (map[player2.getX(0) - 1][player2.getY(0)] == 3)
            iway = 3;
        if (map[player2.getX(0)][player2.getY(0) - 1] == 3)
            iway = 1;
        if (map[player2.getX(0)][player2.getY(0) + 1] == 3)
            iway = 2;
        player2.move(iway);
        if (map[player1.getX(0)][player1.getY(0)] == 3)//玩家吃到食物
        {
            player1.eat();

        }
        if (map[player1.getX(0)][player1.getY(0)] == 1 || map[player1.getX(0)][player1.getY(0)] == 2)//玩家撞到蛇身或墙
        {
            cout << Game over!;
            system(Pause);
            return 0;
        }
        if (map[player2.getX(0)][player2.getY(0)] == 3)//同理
        {
            player2.eat();
        }
        if (map[player2.getX(0)][player2.getY(0)] == 1 || map[player2.getX(0)][player2.getY(0)] == 2)//AI撞到蛇身或墙
        {
            cout << You win!;
            system(Pause);
            return 0;
        }
        clearMap();
        for (size_t i = 1; i <= player1.getLength(); i++)//更新蛇身在地图的位置
        {
            map[player1.getX(i)][player1.getY(i)] = 2;
        }
        for (size_t i = 1; i <= player2.getLength(); i++)
        {
            map[player2.getX(i)][player2.getY(i)] = 2;
        }
        map[player1.getX(0)][player1.getY(0)] = -1;//更新蛇头在地图的位置
        map[player2.getX(0)][player2.getY(0)] = -1;
        if (rand() % 100 > 90)//随机生成食物
        {
            ix = 2 + rand() % (xb - 1);
            iy = 2 + rand() % (yb - 1);
            if (map[ix][iy] == 0)//确保食物在空气中生成
            {
                map[ix][iy] = 3;
            }
        }
    }
    system(Pause);
    return 0;
}


开发界面

成品下载链接:https://pan.baidu.com/s/1NBvh0baBEdp4xgkZFK5cIA 提取码: nrpu

欢迎来到Yari的网站:yar2001 » 快乐的两条贪吃蛇 C++控制台小游戏开发