五子棋AI(C++、QT)

学习视频链接

五子棋-3_bilibili_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1ZK4y1G7Am?p=3&vd_source=0471cde1c644648fafd07b54e303c905

目录

一、基本框架 

1.1 绘制棋盘和初始化 

1.2 显示落点

1.3 落子功能

1.4 判断输赢

二、五子棋 Ai

2.1 Ai策略

2.2 代码


一、基本框架 

1.1 绘制棋盘和初始化 

#ifndef GAMEMODEL_H
#define GAMEMODEL_H

#include <vector>

// 游戏类型,双人还是AI(目前固定让AI下黑子)
enum GameType
{
    MAN, // 双人模式
    AI   // 人机对弈模式
};

// 游戏状态
enum GameStatus
{
    PLAYING, // 游戏中
    WIN,     // 赢了
    DEAD     // 和棋
};

// 棋盘尺寸
const int BOARD_GRAD_SIZE = 15;

const int MARGIN = 30;       // 棋盘边缘空隙
const int CHESS_PADTUS = 15; // 棋子半径
const int MARK_SIZE = 6;     // 落子标记边长
const int BLOCK_SIZE = 40;   // 格子的大小
const int POS_OFFSET = BLOCK_SIZE * 0.4; // 20 鼠标点击的模糊距离上限

const int AI_THINK_TIME = 700;  // AI下棋思考时间

class GameModel
{
public:
    GameModel();
    ~GameModel();

public:
    // 存储当前游戏棋盘和棋子的情况,空白为0,黑子1,白子-1
    std::vector<std::vector<int>> gameMapVec;

    // 存储各个点位的评分情况,作为AI下棋依据
    std::vector<std::vector<int>> scoreMapVec;

    // 标示下棋方,true:黑棋方 false:AI白棋方
    bool playerFlag;

    GameType gameType;  // 游戏模式:人机对弈,还是双人

    GameStatus gameStatus;  // 游戏状态

    void startGame(GameType type);         // 开始游戏
    void calculateScore();                 // 计算评分
    void actionByPerson(int row, int col); // 人执行下棋
    void actionByAI(int &clickRow, int &clickCol); // 机器执行下棋
    void updateGameMap(int row, int col);  // 每次落子后更新游戏棋盘
    bool isWin(int row, int col);          // 判断游戏是否胜利
    bool isDeadGame();                     // 判断是否和棋
};

#endif // GAMEMODEL_H

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPainter>
#include "gamemodel.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void paintEvent(QPaintEvent *event);
    void initGame();
    void initAIGame();

private:
    Ui::MainWindow *ui;
    GameModel *game;    // 游戏指针
    GameType game_type; // 存储游戏类型
};
#endif // MAINWINDOW_H

#include "gamemodel.h"

GameModel::GameModel()
{

}

GameModel::~GameModel()
{

}

void GameModel::startGame(GameType type)
{
    gameType = type;

    //初始棋盘
    gameMapVec.clear() ;
    for(int i = 0; i < BOARD_GRAD_SIZE; i++)
    {
        std::vector<int> lineBoard;
        for(int j = 0; j < BOARD_GRAD_SIZE; j++)
            lineBoard.push_back(0);
        gameMapVec.push_back(lineBoard);
    }

    // 如果是AI模式, 需要初始化评分数组
    if(gameType == AI)
    {
        scoreMapVec.clear() ;
        for(int i = 0; i < BOARD_GRAD_SIZE; i++)
        {
            std::vector<int> lineScores;
            for(int j = 0; j < BOARD_GRAD_SIZE; j++)
                lineScores.push_back(0);
            scoreMapVec.push_back(lineScores);
        }
    }

    // 轮到黑方下棋为true,白方为false
    playerFlag = true ;
}

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setFixedSize(MARGIN * 2 + BLOCK_SIZE * BOARD_GRAD_SIZE, MARGIN * 2 + BLOCK_SIZE * BOARD_GRAD_SIZE);
    initGame();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //绘制棋盘
    painter.setRenderHint (QPainter::Antialiasing, true);  // 抗锯齿
    for (int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
    {
        // 从左到右,第(i+1)条竖线
        painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
        // 从上到下,第(i+1)条横线
        painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
    }
}

void MainWindow::initGame()
{
    game = new GameModel;
    initAIGame();
}

void MainWindow::initAIGame()
{
    game_type = AI;
    game->gameStatus = PLAYING;
    // 在数据模型中进行初始化功能
    game->startGame(game_type);
    update();
}

1.2 显示落点

添加头文件  #include <QMouseEvent>、#include <math.h>

重写鼠标移动事件方法

打开自动检测鼠标移动的按钮

 

给鼠标移动事件添加逻辑

1、准备需要的变量

2、检测是离哪个点最近

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    // 通过鼠标的位置确定落子的标记
    int x = event->x();
    int y = event->y();

    // 棋盘边缘不能落子
    if(x >= MARGIN + BLOCK_SIZE / 2 && x < size().width() - MARGIN - BLOCK_SIZE / 2 &&
       y >= MARGIN + BLOCK_SIZE / 2 && y < size().height() - MARGIN - BLOCK_SIZE / 2)
    {
        // 获取最近的左上角的点
        // add by rock
        int col = (x - MARGIN) / BLOCK_SIZE;
        int row = (y - MARGIN) / BLOCK_SIZE;

        int leftTopPosX = MARGIN + BLOCK_SIZE * col;
        int leftTopPosY = MARGIN + BLOCK_SIZE * row;

        //根据距离算出合适的点击位置,一 共四个点,根据半径距离选最近的
        clickPosRow = -1; //初始化最终的值
        clickPosCol = -1;
        int len = 0;      // 计算完后取整就可以了
        selectPos = false;

        //确定一个误差在范围内的点,且只可能确定一个出来
        len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY) * (y - leftTopPosY));
        if(len < POS_OFFSET)
        {
            clickPosRow = row;
            clickPosCol = col;
            if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
                selectPos = true;
            }
        }

        len = sqrt((x - leftTopPosX - BLOCK_SIZE) * (x - leftTopPosX - BLOCK_SIZE) + (y - leftTopPosY - BLOCK_SIZE) * (y - leftTopPosY - BLOCK_SIZE));
        if(len < POS_OFFSET)
        {
            clickPosRow = row;
            clickPosCol = col + 1;
            if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
                selectPos = true;
            }
        }

        len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY - BLOCK_SIZE) * (y - leftTopPosY - BLOCK_SIZE));
        if(len < POS_OFFSET)
        {
            clickPosRow = row + 1;
            clickPosCol = col;
            if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
                selectPos = true;
            }
        }

        len = sqrt((x - leftTopPosX - BLOCK_SIZE) * (x - leftTopPosX - BLOCK_SIZE) + (y - leftTopPosY) * (y - leftTopPosY));
        if(len < POS_OFFSET)
        {
            clickPosRow = row + 1;
            clickPosCol = col + 1;
            if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
                selectPos = true;
            }
        }
    }

    // 存了坐标后要重绘
    update();
}

3、绘制落点

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //绘制棋盘
    painter.setRenderHint (QPainter::Antialiasing, true);  // 抗锯齿
    for(int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
    {
        // 从左到右,第(i+1)条竖线
        painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
        // 从上到下,第(i+1)条横线
        painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
    }
    // 绘制选中点
    QBrush brush;
    brush.setStyle(Qt::SolidPattern);
    // 绘制落子标记(防止鼠标出框越界)
    if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
       clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
       game->gameMapVec[clickPosRow][clickPosCol] == 0)
    {
        if(game->playerFlag) {
            brush.setColor(Qt::black);
        }
        else {
            brush.setColor(Qt::white);
        }
        painter.setBrush(brush);
        painter.drawRect(MARGIN + BLOCK_SIZE * clickPosCol - MARK_SIZE / 2, MARGIN + BLOCK_SIZE * clickPosRow - MARK_SIZE, 8, 8);
    }

}

1.3 落子功能

1、重写鼠标释放事件

void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if(selectPos == false) {
        return;
    } else {
        selectPos = false;
    }

    chessOneByPerson();
    
    if(game_type == AI) {  // 人机模式
        // AI下棋
    }
}

2、写下棋的方法

void MainWindow::chessOneByPerson()
{
    // 根据当前存储的坐标下子
    // 只有有效点击才下子,并且该处没有子
    if(clickPosRow != -1 && clickPosCol != -1 && game->gameMapVec[clickPosRow][clickPosRow] == 0)
    {
        game->actionByPerson(clickPosRow, clickPosCol);

        // 播放落子音效,待实现

        //重绘
        update() ;
    }
}

void GameModel::actionByPerson(int row, int col)
{
    updateGameMap(row, col);
}

void GameModel::updateGameMap(int row, int col)
{
    if(playerFlag)
        gameMapVec[row][col] = 1;
    else
        gameMapVec[row][col] = -1;
    // 换手
    playerFlag = !playerFlag;
}

3、绘制棋子

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //绘制棋盘
    painter.setRenderHint (QPainter::Antialiasing, true);  // 抗锯齿
    for(int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
    {
        // 从左到右,第(i+1)条竖线
        painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
        // 从上到下,第(i+1)条横线
        painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
    }
    // 绘制选中点
    QBrush brush;
    brush.setStyle(Qt::SolidPattern);
    // 绘制落子标记(防止鼠标出框越界)
    if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
       clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
       game->gameMapVec[clickPosRow][clickPosCol] == 0)
    {
        if(game->playerFlag) {
            brush.setColor(Qt::black);
        }
        else {
            brush.setColor(Qt::white);
        }
        painter.setBrush(brush);
        painter.drawRect(MARGIN + BLOCK_SIZE * clickPosCol - MARK_SIZE / 2, MARGIN + BLOCK_SIZE * clickPosRow - MARK_SIZE, 8, 8);
    }

    //绘制棋子
    for(int i = 0; i < BOARD_GRAD_SIZE; i++)
        for(int j = 0; j < BOARD_GRAD_SIZE; j++)
        {
            if(game->gameMapVec[i][j] == 1)
            {
                // brush.setColor(Qt::white);
                brush.setColor(Qt::black);
                painter.setBrush(brush);
                painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
            }
            else if(game->gameMapVec[i][j] == -1)
            {
                //brush.setColor(Qt::black);
                brush.setColor(Qt::white);
                painter.setBrush(brush);
                painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
            }
        }
}

1.4 判断输赢

bool GameModel::isWin(int row, int col)
{
    // 横竖斜四种大情况,每种情况都根据当前落子往后遍历5个棋子,有一种符合就算赢
    // 水平方向
    for(int i = 0; i < 5; i++)
    {
        // 往左5个,往右匹配4个子,20种情况
        if(col - i > 0 && col - i + 4 < BOARD_GRAD_SIZE &&
           gameMapVec[row][col - i] == gameMapVec[row][col - i + 1] &&
           gameMapVec[row][col - i] == gameMapVec[row][col - i + 2] &&
           gameMapVec[row][col - i] == gameMapVec[row][col - i + 3] &&
           gameMapVec[row][col - i] == gameMapVec[row][col - i + 4]) {
            return true;
        }
    }

    // 竖直方向(上下延伸4个)
    for(int i = 0; i < 5; i++)
    {
        if(row - i > 0 && row - i + 4 < BOARD_GRAD_SIZE &&
           gameMapVec[row - i][col] == gameMapVec[row - i + 1][col] &&
           gameMapVec[row - i][col] == gameMapVec[row - i + 2][col] &&
           gameMapVec[row - i][col] == gameMapVec[row - i + 3][col] &&
           gameMapVec[row - i][col] == gameMapVec[row - i + 4][col]) {
            return true;
        }
    }

    // "/"方向(上下延伸4个)
    for(int i = 0; i < 5; i++)
    {
        if(row + i < BOARD_GRAD_SIZE && row + i - 4 > 0 &&
           col - i > 0 && col - i + 4 < BOARD_GRAD_SIZE &&
           gameMapVec[row + i][col - i] == gameMapVec[row + i - 1][col - i + 1] &&
           gameMapVec[row + i][col - i] == gameMapVec[row + i - 2][col - i + 2] &&
           gameMapVec[row + i][col - i] == gameMapVec[row + i - 3][col - i + 3] &&
           gameMapVec[row + i][col - i] == gameMapVec[row + i - 4][col - i + 4]) {
            return true;
        }
    }

    // "\"方向(上下延伸4个)
    for(int i = 0; i < 5; i++)
    {
        if(row - i > 0 && row + i + 4 < BOARD_GRAD_SIZE &&
           col - i > 0 && col - i + 4 < BOARD_GRAD_SIZE &&
           gameMapVec[row - i][col - i] == gameMapVec[row - i + 1][col - i + 1] &&
           gameMapVec[row - i][col - i] == gameMapVec[row - i + 2][col - i + 2] &&
           gameMapVec[row - i][col - i] == gameMapVec[row - i + 3][col - i + 3] &&
           gameMapVec[row - i][col - i] == gameMapVec[row - i + 4][col - i + 4]) {
            return true;
        }
    }

    return false;
}

在重绘函数里面判断输赢

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //绘制棋盘
    painter.setRenderHint (QPainter::Antialiasing, true);  // 抗锯齿
    for(int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
    {
        // 从左到右,第(i+1)条竖线
        painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
        // 从上到下,第(i+1)条横线
        painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
    }
    // 绘制选中点
    QBrush brush;
    brush.setStyle(Qt::SolidPattern);
    // 绘制落子标记(防止鼠标出框越界)
    if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
       clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
       game->gameMapVec[clickPosRow][clickPosCol] == 0)
    {
        if(game->playerFlag) {
            brush.setColor(Qt::black);
        }
        else {
            brush.setColor(Qt::white);
        }
        painter.setBrush(brush);
        painter.drawRect(MARGIN + BLOCK_SIZE * clickPosCol - MARK_SIZE / 2, MARGIN + BLOCK_SIZE * clickPosRow - MARK_SIZE, 8, 8);
    }

    //绘制棋子
    for(int i = 0; i < BOARD_GRAD_SIZE; i++)
        for(int j = 0; j < BOARD_GRAD_SIZE; j++)
        {
            if(game->gameMapVec[i][j] == 1)
            {
                // brush.setColor(Qt::white);
                brush.setColor(Qt::black);
                painter.setBrush(brush);
                painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
            }
            else if(game->gameMapVec[i][j] == -1)
            {
                //brush.setColor(Qt::black);
                brush.setColor(Qt::white);
                painter.setBrush(brush);
                painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
            }
        }

    // 判断输赢
    if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
       clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
       (game->gameMapVec[clickPosRow][clickPosCol] == 1 ||
        game->gameMapVec[clickPosRow][clickPosCol] == -1))
    {
        if(game->isWin(clickPosRow, clickPosCol) && game->gameStatus == PLAYING)
        {
            game->gameStatus = WIN;
            QString str;
            if(game->gameMapVec[clickPosRow][clickPosCol] == 1) {
                str = "黑棋";
            }
            if(game->gameMapVec[clickPosRow][clickPosCol] == -1) {
                str = "白棋";
            }
            QMessageBox::StandardButton btnValue = QMessageBox::information(this, "五子棋嬴家", str + "胜利");

            // 重置游戏状态,否则容易死循环
            if(btnValue == QMessageBox::Ok) {
                game->startGame(game_type);
                game->gameStatus = PLAYING;
            }
        }
    }
}

二、五子棋 Ai

2.1 Ai策略

所有的空白点往8个方向寻找对方子弟的个数,如果子弟个数比较多,优先堵住

这个 Ai 是以防守为主的,如果对方没有对白棋构成威胁的旗形,白棋才会主动出击

2.2 代码

// 最关键的计算评分
void GameModel::calculateScore()
{
    //統計玩家或者電腦連成的子
    int personNum = 0; //玩家連成子的個數
    int botNum = 0;   //AI連成子的個數
    int emptyNum = 0;   //各方向空白位的個數

    //清空評分數組
    scoreMapVec.clear();
    for(int i=0;i<BOARD_GRAD_SIZE;i++){
        std::vector<int> lineScores;
        for(int j=0;j<BOARD_GRAD_SIZE;j++){
            lineScores.push_back(0);
        }
        scoreMapVec.push_back(lineScores);
    }
    //計分
    /*計分個人理解:
     * 遍歷每一個格子,判斷哪些是空白的點(即為0的點),以該點為中心,判斷周圍的八個點向外延伸的四格裡,
     * 有多少個是黑子、白子、空白,以此作為依據來評分。下方算法是以守為主,所以守的分數>攻的分數
     */
    for(int row=0;row<BOARD_GRAD_SIZE;row++){
        for(int col=0;col<BOARD_GRAD_SIZE;col++){
            //空白點才算
            if(row>0 && col>0 && gameMapVec[row][col]==0){
                //遍歷周圍8個方向
                for(int y=-1;y<=1;y++){
                    for(int x=-1;x<=1;x++){
                        //重置
                        personNum = 0;
                        botNum = 0;
                        emptyNum = 0;
                        //原坐標不算
                        if(!(y==0 && x==0)){
                            //每個方向延伸4個子

                            //對玩家黑子評分(正反兩個方向)
                            for(int i=1;i<=4;i++){
                                if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                   col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                   gameMapVec[row+i*y][col+i*x]==1){ //真人玩家的子
                                    personNum++;
                                }
                                else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                        col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                        gameMapVec[row+i*y][col+i*x]==0){ //空白位
                                    emptyNum++;
                                    break;
                                }
                                else{ //出邊界,或有白子
                                        break;
                                    }
                            }
                            for(int i=1;i<=4;i++){
                                if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                   col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                   gameMapVec[row-i*y][col-i*x]==1){ //真人玩家的子
                                    personNum++;
                                }
                                else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                        col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                        gameMapVec[row-i*y][col-i*x]==0){ //空白位
                                    emptyNum++;
                                    break;
                                }
                                else{ //出邊界,或有白子
                                    break;
                                }
                            }
                                if(personNum == 1){                 //殺2
                                    scoreMapVec[row][col]+=10;
                                }else if(personNum == 2){           //殺3
                                    if(emptyNum == 1)
                                        scoreMapVec[row][col]+=30;
                                    else if(emptyNum == 2)
                                        scoreMapVec[row][col]+=40;
                                }else if(personNum == 3){           //殺4
                                    //量變空位不一樣,優先級不一樣
                                    if(emptyNum == 1)
                                        scoreMapVec[row][col]+=60;
                                    else if(emptyNum == 2)
                                        scoreMapVec[row][col]+=110;
                                }else if(personNum == 4){           //殺5
                                    scoreMapVec[row][col]+=10100;
                                }

                                //進行一次清空
                                emptyNum = 0;

                                //對AI白子評分
                                for(int i=1;i<=4;i++){
                                    if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                       col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                       gameMapVec[row+i*y][col+i*x]==-1){ //AI的子
                                        botNum++;
                                    }else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                             col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                             gameMapVec[row+i*y][col+i*x]==0){ //空白位
                                        emptyNum++;
                                        break;
                                    }else{ //出邊界
                                        break;
                                    }
                                }
                                for(int i=1;i<=4;i++){
                                    if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                       col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                       gameMapVec[row-i*y][col-i*x]==-1){ //AI的子
                                        botNum++;
                                    }else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                             col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                             gameMapVec[row-i*y][col-i*x]==0){ //空白位
                                        emptyNum++;
                                        break;
                                    }else{ //出邊界
                                        break;
                                    }
                                }
                                if(botNum == 0){
                                    scoreMapVec[row][col]+=5;  //活1
                                }else if(botNum == 1){
                                    scoreMapVec[row][col]+=10; //活2
                                }else if(botNum == 2){         //活3
                                    if(emptyNum == 1)
                                        scoreMapVec[row][col]+=25;
                                    else if(emptyNum == 2)
                                        scoreMapVec[row][col]+=50;
                                }else if(botNum == 3){         //活4
                                    if(emptyNum == 1)
                                        scoreMapVec[row][col]+=55;
                                    else if(emptyNum == 2)
                                        scoreMapVec[row][col]+=100;
                                }else if(botNum >= 4){         //活5
                                    scoreMapVec[row][col]+=20000;
                                }


                            }
                        }
                    }
                }
            }
        }
}

每次 AI 思考的时间为 AI_THINK_TIME

写槽函数

加入头文件 #include <time.h> #include <stdlib.h>,写 AI 下棋方法

void GameModel::actionByAI(int &clickRow, int &clickCol)
{
    //計算評分
        calculateScore();

        //從評分中找出最大分數的位置
        int maxScore = 0;
        std::vector<std::pair<int,int>> maxPoints;
        for(int row = 1;row<BOARD_GRAD_SIZE;row++){
            for(int col = 1;col<BOARD_GRAD_SIZE;col++){
                //前提是這個坐標是空的
                if(gameMapVec[row][col] == 0){
                    if(scoreMapVec[row][col]>maxScore){     //找最大數和坐標
                        maxPoints.clear();
                        maxScore = scoreMapVec[row][col];
                        maxPoints.push_back(std::make_pair(row,col));
                    }else if(scoreMapVec[row][col] == maxScore){   //如果有多個最大值就將他們存儲起來,在後面的代碼隨機抽1個
                        maxPoints.push_back(std::make_pair(row,col));
                    }
                }
            }
        }
        //隨機落子,如果有多個點的話
        srand((unsigned)time(0));
        int index = rand()%maxPoints.size();
        std::pair<int,int> pointPair = maxPoints.at(index);
        clickRow = pointPair.first;
        clickCol = pointPair.second;
        updateGameMap(clickRow,clickCol);
}
  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
五子棋AI算法C是一个用于实现五子棋AI的程序代码。它包含了一系列函数和算法来评估棋局、选择最佳下棋位置以及实现自动下棋。 在这个算法中,棋盘一般是15x15的大小,但可以根据需要进行修改。为了简化描述,作者将连五、活四、冲四、活三、眠三、活二、眠二等特殊棋型统称为特殊棋型。 对于棋盘上的一串棋子,有横向、竖向、左上到右下和左下到右上四种方向,分别命名为横(LAY)、竖(STAND)、主对角(MAIN)和副对角(VICE)。 在程序中,有当前棋子的上一个和下一个的概念,例如在横向上,上一个是左边一个,下一个是右边一个。 该算法还包括函数getChessChain(),用于返回某个空位棋子ce周围长度为n的横向、竖向、主对角和副对角的棋链。 总的来说,五子棋AI算法C通过评估棋局、分析棋型和选择最佳下棋位置来实现自动下棋。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [c++ 五子棋AI 算法及实现 详细解析](https://blog.csdn.net/weixin_44062380/article/details/105881036)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

herb.dr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值