qt c++实现的ai贪吃蛇吃满屏幕,超详细!(一)基本组件

这是我作为小白的第一篇博客。由于疫情宅在家里没事干,我重写了大二上的贪吃蛇ai,实现了吃满屏幕。效果图如下,即使是很大的地图(41*41=1681)也能近似吃满。

在这里插入图片描述
现在看来仍有许多不足之处。话不多说,直接开始。

软件用的是qt,下面我假设你已经熟悉了qt的特性,比如信号和槽机制和重写事件等。

游戏基本组件类

基本组件有地图类、蛇类、食物类这3个类。我把它们都放到component.h中。

由于地图、食物和蛇都是一个个的小方块,我用了一个unit_block类存位置。

地图类mapOfGame的设置随机地形:随机墙的数量和每个墙的方块数,先随机找一个空格(0-420,0-420)作为每个墙的初始位置,然后在这个空格附近随机找一个方向生成一个新的墙方块,再在这个新的墙方块附近随机找一个方向生成新的墙方块……这样生成的地形比较随机,但有一个问题,就是会出现一些“不好”的地形,比如空格附近4个方向都是墙或者空格附近3个方向是墙,这些位置蛇肯定走不到,所以我用了optimization把这些地方排除掉了。即使是这样,也会有一些地形不太合理,比如这种情况:
蛇不能进入围成的正方形区域
由于随机地形不是我关注的重点,这个小bug就留给读者来解决吧。

enum direction{
    
    UP,DOWN,LEFT,RIGHT}; //蛇运动的方向
enum snakeState{
    
    NORMAL,REVERSE};//蛇状态:正向、反向

struct unit_block{
    
      //小正方形,边长为10
    int x;
    int y;
    unit_block(){
    
    }
    unit_block(int m,int n):x(m),y(n){
    
    }
    bool operator==(const unit_block &obj)const{
    
    
        if(x==obj.x&&y==obj.y) return true;
        else return false;
    }
};

class mapOfGame{
    
    
private:
    QList<unit_block> wall;
private:
    unit_block setWallBlocks(int x,int y);//用于随机生成墙的方块(上下左右随机生成)
    void optimization();//地图优化

public:
    mapOfGame(){
    
    }
    QList<unit_block> getWall(){
    
    return wall;}
    void setBasicMap(); //设置基本的四周的墙,横坐标0~420,纵坐标0~420
    void setRandomMap();//随机地图 
    void setAiMap();
    void setAiRandomMap();
};

class snake{
    
    
public:
    direction dir;
    QList <unit_block> body;
    snakeState sState=NORMAL;

private:
    void setRandomDir();//设置随机方向
    void setRandomHeadPos(mapOfGame map);//设置随机蛇头位置
    void setRandomHeadPos(snake sk,mapOfGame map);
    bool snakeHeadConflict(int x,int y,mapOfGame map);//判断蛇头是否与地图冲突

public:
    snake(){
    
    }
    ~snake(){
    
    body.clear();}
    void initializeSnake(mapOfGame map); //初始化蛇需要显式调用该函数
    void initializeSnake(snake sk,mapOfGame map);//初始化双人模式中的第二条蛇
    bool collision(mapOfGame map); //判断蛇头是否撞地图和撞自己
    bool collision(snake sk); //判断蛇头是否撞其他的蛇
};

class food{
    
    
public:
    unit_block position;
private:
    bool foodConflict(snake sk,mapOfGame map);//判断食物坐标是否和地图、蛇冲突
public:
    food(){
    
    }
    void initializeFood(snake sk,mapOfGame map); //需要显式调用该函数以初始化食物,设置食物坐标与地图和蛇都不冲突,单人模式
    void initializeFood(snake sk,snake sk2,mapOfGame map); //双人模式
    void initializeFood(snake sk,mapOfGame map,food f);//单人模式设置特殊食物
};

snake类中的有一个sState,这是我实现的特殊食物的功能:正常食物吃了就只能加分、蛇变长的作用,特殊食物吃了后不仅能加分、蛇变长,还会给蛇持续一段时间的“反向”效果,这段时间内按下方向键蛇会反着方向跑,比如按“上”键,蛇往下跑。蛇最开始长度为1,方向随机,单人模式时需要地图来初始化(不然初始化蛇头可能在墙内),双人模式时还需要另一条蛇来初始化。通过collision可以判断游戏是否结束。

food类需要地图、蛇来初始化。

下面是"component.cpp"的代码。

#include "compoments.h"

//****************mapOfGame****************
void mapOfGame::setBasicMap(){
    
     
    wall.clear();
    for(int i=0;i<=420;i=i+10){
    
    
        unit_block w(i,0);
        wall.append(w);
    }
    for(int i=0;i<=420;i=i+10){
    
    
        unit_block w(i,420);
        wall.append(w);
    }
    for(int i=0;i<=420;i=i+10){
    
    
        unit_block w(0,i);
        wall.append(w);
    }
    for(int i=0;i<=420;i=i+10){
    
    
        unit_block w(420,i);
        wall.append(w);
    }
}
void mapOfGame::setRandomMap(){
    
    
    wall.clear();
    setBasicMap();
    int tmp=QRandomGenerator::global()->bounded(5)+20;//生成墙的数量
    for(int i=0;i<tmp;++i){
    
    
        int numOfBlocks=QRandomGenerator::global()->bounded(5)+10;
        unit_block u,tmp;
        u.x=QRandomGenerator::global()->bounded(41)*10+10;
        u.y=QRandomGenerator::global()->bounded(41)*10+10;
        wall.push_back(u);
        for(int j=0;j<numOfBlocks;++j){
    
    
            tmp=setWallBlocks(u.x,u.y);
            u=tmp;
            wall.push_back(tmp);
        }
    }
    for(int i=0;i<10;++i)
    optimization();
}
void mapOfGame::setAiMap(){
    
    
    wall.clear();
    setBasicMap();
    for(int i=10;i<=150;i=i+10){
    
    //i<=70
        for(int j=i;j<=420-i;j=j+10){
    
    
            unit_block u(j,i);
            wall.append(u);
        }
        for(int j=i;j<=420-i;j=j+10){
    
    
            unit_block u(j,420-i);
            wall.append(u);
        }
        for(int j=i;j<=420-i;j=j+10){
    
    
            unit_block u(i,j);
            wall.append(u);
        }
        for(int j=i;j<=420-i;j=j+10){
    
    
            unit_block u(420-i,j);
            wall.append(u);
        }
    }
}

void mapOfGame::setAiRandomMap(){
    
    
    wall.clear();
    setAiMap();
    int tmp=QRandomGenerator::global()->bounded(5)+10;//生成墙的数量
    for(int i=0;i<tmp;++i){
    
    
        int numOfBlocks=QRandomGenerator::global()->bounded(5)+8;
        unit_block u,tmp;
        u.x=QRandomGenerator::global()->bounded(28)*10+70;
        u.y=QRandomGenerator::global()->bounded(28)*10+70;
        wall.push_back(u);
        for(int j=0;j<numOfBlocks;++j){
    
    
            tmp=setWallBlocks(u.x,u.y);
            u=tmp;
            wall.push_back(tmp);
        }
    }
    for(int i=0;i<10;++i)
    optimization();
}

unit_block mapOfGame::setWallBlocks(int x, int y){
    
    
    int tmp=QRandomGenerator::global()->bounded(4);
    unit_block u;
    switch (tmp) {
    
    
    case 0:u.x=x; u.y=y+10;return u;
    case 1:u.x=x; u.y=y-10;return u;
    case 2:u.x=x+10; u.y=y;return u;
    default:u.x=x-10; u.y=y;return u;
    }
}

void mapOfGame::optimization(){
    
    
    for(int i=10;i<420;i=i+10){
    
    
        for(int j=10;j<420;j=j+10){
    
    
            unit_block left(i-10,j),right(i+10,j),up(i,j-10),down(i,j+10),it(i,j);
            if(wall.contains(left)&&wall.contains(right)&&wall.contains(up)&&!wall.contains(it)){
    
    //将三面都是墙而本身不是墙的方块设置为墙
                wall.append(it);
            }
            else if(wall.contains(left)&&wall.contains(right)&&wall.contains(down)&&!wall.contains(it)){
    
    
                wall.append(it);
            }
            else if(wall.contains(left)&&wall.contains(up)&&wall.contains(down)&&!wall.contains(it)){
    
    
                wall.append(it);
            }
            else if(wall.contains(right)&&wall.contains(up)&&wall.contains(down)&&!wall.contains(it)){
    
    
                wall.append(it);
            }
            else if(wall.contains(left)&&wall.contains(right)&&wall.contains(up)&&wall.contains(down)&&!wall.contains(it)){
    
    //将四面都是墙而本身不是墙的方块设置为墙
                wall.append(it);
            }
        }
    }
}

//****************snake****************
void snake::initializeSnake(mapOfGame map){
    
    
    body.clear();   
    setRandomDir();
    setRandomHeadPos(map);
}
void snake::initializeSnake(snake sk, mapOfGame map){
    
    
    body.clear();
    setRandomDir();
    setRandomHeadPos(sk,map);
}

void snake::setRandomDir(){
    
    
    int tmp=QRandomGenerator::global()->bounded(4);//生成随机方向
    switch (tmp) {
    
    
    case 0:dir=UP;break;
    case 1:dir=DOWN;break;
    case 2:dir=LEFT;break;
    case 3:dir=RIGHT;break;
    }
}
void snake::setRandomHeadPos(mapOfGame map){
    
    
    int x,y;
    do{
    
    
        x=QRandomGenerator::global()->bounded(41)*10+10;
        y=QRandomGenerator::global()->bounded(41)*10+10;
    }while(snakeHeadConflict(x,y,map));
    unit_block head(x,y);
    body.append(head);
}
void snake::setRandomHeadPos(snake sk, mapOfGame map){
    
    
    int x,y;
    do{
    
    
        x=QRandomGenerator::global()->bounded(41)*10+10;
        y=QRandomGenerator::global()->bounded(41)*10+10;
    }while(snakeHeadConflict(x,y,map)||(x==sk.body.front().x&&y==sk.body.front().y));
    unit_block head(x,y);
    body.append(head);
}
bool snake::snakeHeadConflict(int x,int y,mapOfGame map){
    
    
    for(auto i:map.getWall()){
    
    
        if(i.x==x&&i.y==y)return true;
    }
    return false;
}

bool snake::collision(mapOfGame map){
    
    
    if(snakeHeadConflict(body.front().x,body.front().y,map))
        return true;
    else if(body.length()>=2){
    
    
        QList<unit_block>::iterator i=body.begin();
        ++i;
        for(;i!=body.end();++i){
    
    
            if(i->x==body.begin()->x&&i->y==body.begin()->y)return true;
        }
        return false;
    }
    else return false;
}
bool snake::collision(snake sk){
    
    
    QList<unit_block>::iterator i=sk.body.begin();
    for(;i!=sk.body.end();++i){
    
    
        if(i->x==body.begin()->x&&i->y==body.begin()->y)
        {
    
       return true; }
    }
    return false;
}

//****************food****************
void food::initializeFood(snake sk, mapOfGame map){
    
    
    position.x=QRandomGenerator::global()->bounded(41)*10+10;
    position.y=QRandomGenerator::global()->bounded(41)*10+10;
    if(foodConflict(sk,map)){
    
    
        initializeFood(sk,map);
    }
}
void food::initializeFood(snake sk, snake sk2, mapOfGame map){
    
    
    position.x=QRandomGenerator::global()->bounded(41)*10+10;
    position.y=QRandomGenerator::global()->bounded(41)*10+10;
    if(foodConflict(sk,map)||foodConflict(sk2,map)){
    
    
        initializeFood(sk,map);
    }
}
void food::initializeFood(snake sk, mapOfGame map, food f){
    
    
    position.x=QRandomGenerator::global()->bounded(41)*10+10;
    position.y=QRandomGenerator::global()->bounded(41)*10+10;
    if(foodConflict(sk,map)||(position.x==f.position.x&&position.y==f.position.y)){
    
    
        initializeFood(sk,map,f);
    }
}
bool food::foodConflict(snake sk, mapOfGame map){
    
    
    for(auto i:sk.body){
    
    
        if(i.x==position.x&&i.y==position.y)return true;
    }
    for(auto i:map.getWall()){
    
    
        if(i.x==position.x&&i.y==position.y)return true;
    }
    return false;
}

下一篇:qt c++实现的ai贪吃蛇吃满屏幕,超详细!(二)ai的具体实现

猜你喜欢

转载自blog.csdn.net/livingsu/article/details/104455053