c++算法实现ai贪吃蛇

说明:
c++算法实现贪吃蛇
效果图:

step0:游戏规则和思路

用c++写贪吃蛇ai算法

0.在控制台展示位置坐标,把游戏界面作为x轴和y轴,左下角为(0,0) ,右上角为(x_max,y_max)
1. ​让ai自动控制蛇的移动,通过吃掉地图上的食物(如苹果🍎)使蛇身变长。
2. ​ai需要避免碰撞,蛇头不能触碰墙壁、障碍物或自身身体,否则游戏结束。
3. ​尽可能延长蛇身并存活更长时间。
4.ai必须保持智能,聪明,不能犯低级错误

为了实现一个智能的贪吃蛇AI,我们需要分步骤构建游戏框架并实现AI算法。以下是详细的实现过程:

第一步:游戏框架和基本结构
我们首先定义游戏中的基本元素,包括点、方向、蛇和游戏状态。


第二步:AI算法实现
AI的核心是路径寻找和决策逻辑,使用BFS寻找最短路径,洪水填充评估生存空间。

第三步:主函数和游戏循环
最后,设置游戏的主循环,处理输入、更新和渲染

说明
路径寻找:使用BFS算法寻找当前蛇头到食物的最短路径,确保路径安全。
生存策略:当无法找到路径时,使用洪水填充算法评估每个方向的可行区域,选择最安全的方向。
碰撞检测:精确模拟蛇移动后的身体状态,避免自碰撞和墙壁碰撞。
动态更新:每次移动后更新蛇的状态,检查是否吃到食物或发生碰撞。
该实现能够确保AI智能地寻找食物并尽可能延长生存时间,同时避免低级错误。


# 贪吃蛇AI实现思路分析
## 第一步:游戏框架构建
1. ​**基础数据结构**​
   - `Point`结构体表示坐标点,实现哈希支持容器操作
   - `Direction`枚举定义移动方向,配套`opposite()`获取反向

2. ​**蛇类实现(Snake)​**​
   - 使用`deque`存储身体坐标
   - 方向控制逻辑:禁止立即反向移动
   - 移动机制:
     - `getNextHead()`计算头部预测位置
     - `move()`处理身体增长/正常移动

3. ​**游戏状态管理(Game)​**​
   - 地图边界管理(x_max/y_max)
   - 食物生成:
     - 随机生成并排除与蛇身重叠
   - 碰撞检测:
     - 边界碰撞
     - 自碰撞检测
   - 状态更新:
     - 同步食物增长与身体变化

## 第二步:AI核心算法
1. ​**决策流程**​
   ```mermaid
   graph TD
     A[获取可能方向] --> B{路径可达?}
     B -- 是 --> C[选择最短路径]
     B -- 否 --> D[洪水填充找安全区]
     C --> E[执行最优方向]
     D --> E

​2.路径寻找策略​
BFS最短路径算法:
考虑模拟移动后的蛇身作为障碍
四方向广度优先搜索
模拟:
完全克隆蛇对象
预测未来n步的障碍物分布

3.​生存策略保障​
洪水填充算法:
评估每个方向的可行区域面积
优先选择最大安全区域

碰撞预判:
提前模拟移动后的自碰撞
排除会导致立即死亡的方向

第三步:游戏循环控制
​渲染系统​

控制台字符图形化:
'O'表示蛇头
'o'表示蛇身
'F'表示食物
坐标系处理:

Y轴反向显示(符合控制台从上到下特性)

2.​运行逻辑​
while(true){
    渲染界面 -> AI决策 -> 更新状态 -> 碰撞检测
}

3.​失败处理​
碰撞即时检测机制
游戏终止条件:
自碰撞/越界立即结束


结束提示显示"Game Over"

实现特点
​双策略保障系统​

主策略:积极寻食的最短路径
备用策略:生存优先的安全区域

​精确状态模拟​
完全克隆游戏对象进行预判
考虑食物增长带来的身体变化

​高效算法组合​
BFS时间复杂度:O(n)
洪水填充空间评估:O(n)
方向选择复杂度:O(4)

step1:C:\Users\wangrusheng\source\repos\CMakeProject1\CMakeProject1\CMakeProject1.cpp

#include <iostream>
#include <vector>
#include <deque>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <thread>
#include <chrono>
#include <cstdlib>
#include <ctime>

struct Point {
    int x, y;
    Point(int x_, int y_) : x(x_), y(y_) {}
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
};

namespace std {
    template<> struct hash<Point> {
        size_t operator()(const Point& p) const {
            return hash<int>()(p.x) ^ hash<int>()(p.y);
        }
    };
}

enum Direction { UP, DOWN, LEFT, RIGHT };

Direction opposite(Direction dir) {
    switch (dir) {
    case UP: return DOWN;
    case DOWN: return UP;
    case LEFT: return RIGHT;
    case RIGHT: return LEFT;
    }
}

class Snake {
public:
    std::deque<Point> body;
    Direction dir;
    bool grow;

    Snake(int startX, int startY) : dir(RIGHT), grow(false) {
        body.emplace_back(startX, startY);
    }

    Point getHead() const { return body.back(); }

    void changeDirection(Direction newDir) {
        if (newDir != opposite(dir)) {
            dir = newDir;
        }
    }

    Point getNextHead() const {
        const Point& head = body.back();
        switch (dir) {
        case UP:    return Point(head.x, head.y + 1);
        case DOWN:  return Point(head.x, head.y - 1);
        case LEFT:  return Point(head.x - 1, head.y);
        case RIGHT: return Point(head.x + 1, head.y);
        }
        return head;
    }

    void move(bool willGrow) {
        body.push_back(getNextHead());
        if (!willGrow) body.pop_front();
        else grow = false;
    }
};

class Game {
public:
    int x_max, y_max;
    Snake snake;
    Point food;
    std::vector<Point> obstacles;

    Game(int x, int y) : x_max(x), y_max(y), snake(x / 2, y / 2), food(0, 0) {
        spawnFood();
    }

    void spawnFood() {
        do {
            food.x = rand() % x_max;
            food.y = rand() % y_max;
        } while (std::find(snake.body.begin(), snake.body.end(), food) != snake.body.end());
    }

    bool isCollision() {
        const Point& head = snake.getHead();
        if (head.x < 0 || head.y < 0 || head.x >= x_max || head.y >= y_max) return true;
        for (auto it = snake.body.begin(); it != prev(snake.body.end()); ++it)
            if (*it == head) return true;
        return false;
    }

    void update() {
        bool willGrow = snake.getHead() == food;
        snake.move(willGrow);
        if (willGrow) spawnFood();
    }
};

class AI {
public:
    Direction getNextDirection(const Game& game) {
        const auto& snake = game.snake;
        auto possibleDirs = getPossibleDirections(snake, game.x_max, game.y_max, game.food);
        if (possibleDirs.empty()) return snake.dir;

        Direction bestDir = possibleDirs.front();
        int shortest = INT_MAX;
        for (Direction dir : possibleDirs) {
            int pathLen = simulatePath(snake, dir, game);
            if (pathLen != -1 && pathLen < shortest) {
                shortest = pathLen;
                bestDir = dir;
            }
        }

        if (shortest != INT_MAX) return bestDir;
        return findSafestDirection(snake, possibleDirs, game);
    }

private:
    std::vector<Direction> getPossibleDirections(const Snake& snake, int x_max, int y_max, const Point& food) {
        std::vector<Direction> dirs;
        const Point head = snake.getHead();

        for (Direction dir : {UP, DOWN, LEFT, RIGHT}) {
            if (dir == opposite(snake.dir)) continue;

            Point newHead = moveHead(head, dir);
            if (newHead.x < 0 || newHead.y < 0 || newHead.x >= x_max || newHead.y >= y_max) continue;

            bool willGrow = newHead == food;
            auto newBody = simulateBody(snake.body, newHead, willGrow);
            if (!hasCollision(newHead, newBody)) dirs.push_back(dir);
        }
        return dirs;
    }

    int simulatePath(const Snake& snake, Direction dir, const Game& game) {
        Snake simSnake = snake;
        simSnake.changeDirection(dir);
        Point newHead = simSnake.getNextHead();
        bool willGrow = newHead == game.food;
        auto newBody = simulateBody(simSnake.body, newHead, willGrow);

        std::vector<Point> obstacles(newBody.begin(), newBody.end() - 1);
        return bfs(newHead, game.food, obstacles, game.x_max, game.y_max);
    }

    int bfs(const Point& start, const Point& end, const std::vector<Point>& obstacles, int x_max, int y_max) {
        std::queue<Point> q;
        std::unordered_map<Point, int> dist;
        q.push(start);
        dist[start] = 0;

        while (!q.empty()) {
            Point p = q.front(); q.pop();
            if (p == end) return dist[p];

            for (Direction dir : {UP, DOWN, LEFT, RIGHT}) {
                Point next = moveHead(p, dir);
                if (next.x < 0 || next.y < 0 || next.x >= x_max || next.y >= y_max) continue;
                if (dist.count(next) || std::find(obstacles.begin(), obstacles.end(), next) != obstacles.end()) continue;

                dist[next] = dist[p] + 1;
                q.push(next);
            }
        }
        return -1;
    }

    Direction findSafestDirection(const Snake& snake, const std::vector<Direction>& dirs, const Game& game) {
        Direction bestDir = dirs.front();
        int maxArea = 0;

        for (Direction dir : dirs) {
            Snake simSnake = snake;
            simSnake.changeDirection(dir);
            Point newHead = simSnake.getNextHead();
            bool willGrow = newHead == game.food;
            auto newBody = simulateBody(simSnake.body, newHead, willGrow);

            std::vector<Point> obstacles(newBody.begin(), newBody.end() - 1);
            int area = floodFill(newHead, obstacles, game.x_max, game.y_max);
            if (area > maxArea) {
                maxArea = area;
                bestDir = dir;
            }
        }
        return bestDir;
    }

    int floodFill(const Point& start, const std::vector<Point>& obstacles, int x_max, int y_max) {
        std::unordered_set<Point> visited;
        std::queue<Point> q;
        q.push(start);
        visited.insert(start);

        int count = 0;
        while (!q.empty()) {
            Point p = q.front(); q.pop();
            count++;

            for (Direction dir : {UP, DOWN, LEFT, RIGHT}) {
                Point next = moveHead(p, dir);
                if (next.x < 0 || next.y < 0 || next.x >= x_max || next.y >= y_max) continue;
                if (visited.count(next) || std::find(obstacles.begin(), obstacles.end(), next) != obstacles.end()) continue;

                visited.insert(next);
                q.push(next);
            }
        }
        return count;
    }

    Point moveHead(const Point& p, Direction dir) const {
        switch (dir) {
        case UP:    return Point(p.x, p.y + 1);
        case DOWN:  return Point(p.x, p.y - 1);
        case LEFT:  return Point(p.x - 1, p.y);
        case RIGHT: return Point(p.x + 1, p.y);
        }
        return p;
    }

    std::deque<Point> simulateBody(const std::deque<Point>& body, const Point& newHead, bool grow) const {
        std::deque<Point> newBody = body;
        newBody.push_back(newHead);
        if (!grow) newBody.pop_front();
        return newBody;
    }

    bool hasCollision(const Point& head, const std::deque<Point>& body) const {
        for (auto it = body.begin(); it != prev(body.end()); ++it)
            if (*it == head) return true;
        return false;
    }
};

int main() {
    srand(time(nullptr));
    const int WIDTH = 20, HEIGHT = 20;
    Game game(WIDTH, HEIGHT);
    AI ai;

    while (true) {
        system("cls");
        for (int y = HEIGHT - 1; y >= 0; --y) {
            for (int x = 0; x < WIDTH; ++x) {
                Point p(x, y);
                if (p == game.snake.getHead()) std::cout << "O";
                else if (std::find(game.snake.body.begin(), game.snake.body.end(), p) != game.snake.body.end()) std::cout << "o";
                else if (p == game.food) std::cout << "F";
                else std::cout << ".";
            }
            std::cout << "\n";
        }

        Direction dir = ai.getNextDirection(game);
        game.snake.changeDirection(dir);
        game.update();

        if (game.isCollision()) {
            std::cout << "Game Over!\n";
            break;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
    return 0;
}

////我是分割线
另一种实现方案
step101:

#include <iostream>
#include <vector>
#include <deque>
#include <queue>
#include <unordered_set>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <conio.h>

using namespace std;

// 方向枚举
enum class Direction { UP, DOWN, LEFT, RIGHT };

// 坐标点结构体
struct Point {
    int x, y;
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
    bool operator!=(const Point& other) const {
        return !(*this == other);
    }
};

// 哈希函数用于unordered_set
namespace std {
    template<> struct hash<Point> {
        size_t operator()(const Point& p) const {
            return hash<int>()(p.x) ^ hash<int>()(p.y);
        }
    };
}

// 蛇类
class Snake {
private:
    deque<Point> body;
    Direction direction;
    bool growNextMove = false;

public:
    Snake(const Point& startPos) {
        body.push_back(startPos);
        direction = Direction::RIGHT;
    }

    // 获取蛇头位置
    Point getHead() const {
        return body.front();
    }

    // 获取蛇身
    const deque<Point>& getBody() const {
        return body;
    }

    // 设置方向(不能直接反向)
    void setDirection(Direction newDir) {
        if ((direction == Direction::UP && newDir != Direction::DOWN) ||
            (direction == Direction::DOWN && newDir != Direction::UP) ||
            (direction == Direction::LEFT && newDir != Direction::RIGHT) ||
            (direction == Direction::RIGHT && newDir != Direction::LEFT)) {
            direction = newDir;
        }
    }

    // 获取下一个头部位置
    Point getNextHead() const {
        Point head = getHead();
        switch (direction) {
        case Direction::UP: return Point(head.x, head.y + 1);
        case Direction::DOWN: return Point(head.x, head.y - 1);
        case Direction::LEFT: return Point(head.x - 1, head.y);
        case Direction::RIGHT: return Point(head.x + 1, head.y);
        }
        return head;
    }

    // 移动蛇
    void move() {
        Point newHead = getNextHead();
        body.push_front(newHead);
        if (growNextMove) {
            growNextMove = false;
        }
        else {
            body.pop_back();
        }
    }

    // 标记下次移动时增长
    void grow() {
        growNextMove = true;
    }

    // 检查是否与自身碰撞
    bool checkSelfCollision() const {
        Point head = getHead();
        for (size_t i = 1; i < body.size(); ++i) {
            if (body[i] == head) {
                return true;
            }
        }
        return false;
    }

    // 克隆蛇(用于AI预测)
    Snake clone() const {
        Snake newSnake(body.front());
        newSnake.body = deque<Point>(body.begin(), body.end());
        newSnake.direction = direction;
        newSnake.growNextMove = growNextMove;
        return newSnake;
    }
};

// 游戏类
class Game {
private:
    int width, height;
    Snake snake;
    Point food;
    bool gameOver;
    int score;

    // 生成新食物
    void spawnFood() {
        unordered_set<Point> occupied;
        for (const auto& p : snake.getBody()) {
            occupied.insert(p);
        }

        vector<Point> available;
        for (int x = 0; x < width; ++x) {
            for (int y = 0; y < height; ++y) {
                Point p(x, y);
                if (occupied.find(p) == occupied.end()) {
                    available.push_back(p);
                }
            }
        }

        if (!available.empty()) {
            food = available[rand() % available.size()];
        }
        else {
            // 没有可用空间,游戏胜利
            gameOver = true;
        }
    }

    // 绘制游戏界面
    void draw() {
        system("cls");
        cout << "Score: " << score << endl;

        // 绘制上边界
        cout << "+";
        for (int x = 0; x < width; ++x) cout << "-";
        cout << "+" << endl;

        // 绘制游戏区域
        for (int y = height - 1; y >= 0; --y) {
            cout << "|";
            for (int x = 0; x < width; ++x) {
                Point p(x, y);
                if (p == snake.getHead()) {
                    cout << "O";
                }
                else if (find(snake.getBody().begin(), snake.getBody().end(), p) != snake.getBody().end()) {
                    cout << "o";
                }
                else if (p == food) {
                    cout << "F";
                }
                else {
                    cout << " ";
                }
            }
            cout << "|" << endl;
        }

        // 绘制下边界
        cout << "+";
        for (int x = 0; x < width; ++x) cout << "-";
        cout << "+" << endl;

        // 调试信息:显示坐标
        cout << "Head: (" << snake.getHead().x << "," << snake.getHead().y << ") ";
        cout << "Food: (" << food.x << "," << food.y << ")" << endl;
    }

    // 使用BFS寻找最短路径
    Direction findPathToFood() {
        Point target = food;
        Point start = snake.getHead();

        // 障碍物集合(蛇身)
        unordered_set<Point> obstacles;
        for (const auto& p : snake.getBody()) {
            if (p != start) { // 头部不算障碍物
                obstacles.insert(p);
            }
        }

        // BFS队列:存储(位置, 初始方向)
        queue<pair<Point, Direction>> q;
        // 访问标记
        unordered_set<Point> visited;

        // 初始化四个方向
        vector<pair<Point, Direction>> neighbors = {
            {Point(start.x, start.y + 1), Direction::UP},
            {Point(start.x, start.y - 1), Direction::DOWN},
            {Point(start.x - 1, start.y), Direction::LEFT},
            {Point(start.x + 1, start.y), Direction::RIGHT}
        };

        for (const auto& neighbor : neighbors) {
            Point next = neighbor.first;
            Direction dir = neighbor.second;
            if (next.x >= 0 && next.x < width && next.y >= 0 && next.y < height &&
                obstacles.find(next) == obstacles.end()) {
                q.push({ next, dir });
                visited.insert(next);
                if (next == target) {
                    return dir;
                }
            }
        }

        // BFS搜索
        while (!q.empty()) {
            auto current = q.front();
            q.pop();

            vector<pair<Point, Direction>> nextNeighbors = {
                {Point(current.first.x, current.first.y + 1), current.second},
                {Point(current.first.x, current.first.y - 1), current.second},
                {Point(current.first.x - 1, current.first.y), current.second},
                {Point(current.first.x + 1, current.first.y), current.second}
            };

            for (const auto& neighbor : nextNeighbors) {
                Point next = neighbor.first;
                Direction dir = neighbor.second;
                if (next.x >= 0 && next.x < width && next.y >= 0 && next.y < height &&
                    obstacles.find(next) == obstacles.end() &&
                    visited.find(next) == visited.end()) {

                    if (next == target) {
                        return dir;
                    }

                    visited.insert(next);
                    q.push({ next, dir });
                }
            }
        }

        // 没有找到路径,返回无效方向
        return Direction::UP; // 默认值,实际会被后续逻辑覆盖
    }

    // 洪水填充评估安全区域大小
    int floodFillSize(const Point& start, const unordered_set<Point>& obstacles) {
        unordered_set<Point> visited;
        queue<Point> q;
        q.push(start);
        visited.insert(start);
        int count = 0;

        while (!q.empty()) {
            Point p = q.front();
            q.pop();
            count++;

            vector<Point> neighbors = {
                Point(p.x, p.y + 1), // UP
                Point(p.x, p.y - 1), // DOWN
                Point(p.x - 1, p.y), // LEFT
                Point(p.x + 1, p.y)  // RIGHT
            };

            for (const auto& next : neighbors) {
                if (next.x >= 0 && next.x < width && next.y >= 0 && next.y < height &&
                    obstacles.find(next) == obstacles.end() &&
                    visited.find(next) == visited.end()) {
                    visited.insert(next);
                    q.push(next);
                }
            }
        }

        return count;
    }

    // 选择最安全的方向
    Direction findSafestDirection() {
        Point head = snake.getHead();
        Direction bestDir = Direction::UP;
        int maxSpace = -1;

        // 障碍物集合(蛇身)
        unordered_set<Point> obstacles;
        for (const auto& p : snake.getBody()) {
            if (p != head) { // 头部不算障碍物
                obstacles.insert(p);
            }
        }

        // 测试四个方向
        vector<pair<Point, Direction>> neighbors = {
            {Point(head.x, head.y + 1), Direction::UP},
            {Point(head.x, head.y - 1), Direction::DOWN},
            {Point(head.x - 1, head.y), Direction::LEFT},
            {Point(head.x + 1, head.y), Direction::RIGHT}
        };

        for (const auto& neighbor : neighbors) {
            Point next = neighbor.first;
            Direction dir = neighbor.second;
            if (next.x >= 0 && next.x < width && next.y >= 0 && next.y < height &&
                obstacles.find(next) == obstacles.end()) {

                int space = floodFillSize(next, obstacles);
                if (space > maxSpace) {
                    maxSpace = space;
                    bestDir = dir;
                }
            }
        }

        return bestDir;
    }

public:
    Game(int w, int h) : width(w), height(h), snake(Point(w / 2, h / 2)), gameOver(false), score(0) {
        srand(static_cast<unsigned int>(time(nullptr)));
        spawnFood();
    }

    // AI决策
    void aiMove() {
        // 尝试寻找食物路径
        Direction dir = findPathToFood();

        // 验证路径是否安全
        Snake testSnake = snake.clone();
        testSnake.setDirection(dir);
        Point nextHead = testSnake.getNextHead();

        // 检查是否会撞墙或自身
        bool validMove = true;
        if (nextHead.x < 0 || nextHead.x >= width || nextHead.y < 0 || nextHead.y >= height) {
            validMove = false;
        }
        else {
            for (const auto& p : snake.getBody()) {
                if (p == nextHead) {
                    validMove = false;
                    break;
                }
            }
        }

        if (validMove) {
            snake.setDirection(dir);
        }
        else {
            // 找不到安全路径,选择最安全的方向
            snake.setDirection(findSafestDirection());
        }
    }

    // 更新游戏状态
    void update() {
        // 移动蛇
        snake.move();

        // 检查碰撞
        Point head = snake.getHead();
        if (head.x < 0 || head.x >= width || head.y < 0 || head.y >= height) {
            gameOver = true;
            return;
        }

        if (snake.checkSelfCollision()) {
            gameOver = true;
            return;
        }

        // 检查是否吃到食物
        if (head == food) {
            snake.grow();
            score++;
            spawnFood();
        }
    }

    // 运行游戏
    void run() {
        while (!gameOver) {
            draw();
            aiMove();
            update();
            Sleep(200); // 控制游戏速度
        }
        cout << "Game Over! Final Score: " << score << endl;
    }
};

int main() {
    Game game(20, 20);
    game.run();
    return 0;
}

end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值