当前位置:   article > 正文

五子棋AI(C++、QT)_五子棋ai算法代码

五子棋ai算法代码

学习视频链接

五子棋-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 绘制棋盘和初始化 

  1. #ifndef GAMEMODEL_H
  2. #define GAMEMODEL_H
  3. #include <vector>
  4. // 游戏类型,双人还是AI(目前固定让AI下黑子)
  5. enum GameType
  6. {
  7. MAN, // 双人模式
  8. AI // 人机对弈模式
  9. };
  10. // 游戏状态
  11. enum GameStatus
  12. {
  13. PLAYING, // 游戏中
  14. WIN, // 赢了
  15. DEAD // 和棋
  16. };
  17. // 棋盘尺寸
  18. const int BOARD_GRAD_SIZE = 15;
  19. const int MARGIN = 30; // 棋盘边缘空隙
  20. const int CHESS_PADTUS = 15; // 棋子半径
  21. const int MARK_SIZE = 6; // 落子标记边长
  22. const int BLOCK_SIZE = 40; // 格子的大小
  23. const int POS_OFFSET = BLOCK_SIZE * 0.4; // 20 鼠标点击的模糊距离上限
  24. const int AI_THINK_TIME = 700; // AI下棋思考时间
  25. class GameModel
  26. {
  27. public:
  28. GameModel();
  29. ~GameModel();
  30. public:
  31. // 存储当前游戏棋盘和棋子的情况,空白为0,黑子1,白子-1
  32. std::vector<std::vector<int>> gameMapVec;
  33. // 存储各个点位的评分情况,作为AI下棋依据
  34. std::vector<std::vector<int>> scoreMapVec;
  35. // 标示下棋方,true:黑棋方 false:AI白棋方
  36. bool playerFlag;
  37. GameType gameType; // 游戏模式:人机对弈,还是双人
  38. GameStatus gameStatus; // 游戏状态
  39. void startGame(GameType type); // 开始游戏
  40. void calculateScore(); // 计算评分
  41. void actionByPerson(int row, int col); // 人执行下棋
  42. void actionByAI(int &clickRow, int &clickCol); // 机器执行下棋
  43. void updateGameMap(int row, int col); // 每次落子后更新游戏棋盘
  44. bool isWin(int row, int col); // 判断游戏是否胜利
  45. bool isDeadGame(); // 判断是否和棋
  46. };
  47. #endif // GAMEMODEL_H

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QPainter>
  5. #include "gamemodel.h"
  6. QT_BEGIN_NAMESPACE
  7. namespace Ui { class MainWindow; }
  8. QT_END_NAMESPACE
  9. class MainWindow : public QMainWindow
  10. {
  11. Q_OBJECT
  12. public:
  13. MainWindow(QWidget *parent = nullptr);
  14. ~MainWindow();
  15. void paintEvent(QPaintEvent *event);
  16. void initGame();
  17. void initAIGame();
  18. private:
  19. Ui::MainWindow *ui;
  20. GameModel *game; // 游戏指针
  21. GameType game_type; // 存储游戏类型
  22. };
  23. #endif // MAINWINDOW_H

  1. #include "gamemodel.h"
  2. GameModel::GameModel()
  3. {
  4. }
  5. GameModel::~GameModel()
  6. {
  7. }
  8. void GameModel::startGame(GameType type)
  9. {
  10. gameType = type;
  11. //初始棋盘
  12. gameMapVec.clear() ;
  13. for(int i = 0; i < BOARD_GRAD_SIZE; i++)
  14. {
  15. std::vector<int> lineBoard;
  16. for(int j = 0; j < BOARD_GRAD_SIZE; j++)
  17. lineBoard.push_back(0);
  18. gameMapVec.push_back(lineBoard);
  19. }
  20. // 如果是AI模式, 需要初始化评分数组
  21. if(gameType == AI)
  22. {
  23. scoreMapVec.clear() ;
  24. for(int i = 0; i < BOARD_GRAD_SIZE; i++)
  25. {
  26. std::vector<int> lineScores;
  27. for(int j = 0; j < BOARD_GRAD_SIZE; j++)
  28. lineScores.push_back(0);
  29. scoreMapVec.push_back(lineScores);
  30. }
  31. }
  32. // 轮到黑方下棋为true,白方为false
  33. playerFlag = true ;
  34. }

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. MainWindow::MainWindow(QWidget *parent)
  4. : QMainWindow(parent)
  5. , ui(new Ui::MainWindow)
  6. {
  7. ui->setupUi(this);
  8. setFixedSize(MARGIN * 2 + BLOCK_SIZE * BOARD_GRAD_SIZE, MARGIN * 2 + BLOCK_SIZE * BOARD_GRAD_SIZE);
  9. initGame();
  10. }
  11. MainWindow::~MainWindow()
  12. {
  13. delete ui;
  14. }
  15. void MainWindow::paintEvent(QPaintEvent *event)
  16. {
  17. QPainter painter(this);
  18. //绘制棋盘
  19. painter.setRenderHint (QPainter::Antialiasing, true); // 抗锯齿
  20. for (int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
  21. {
  22. // 从左到右,第(i+1)条竖线
  23. painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
  24. // 从上到下,第(i+1)条横线
  25. painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
  26. }
  27. }
  28. void MainWindow::initGame()
  29. {
  30. game = new GameModel;
  31. initAIGame();
  32. }
  33. void MainWindow::initAIGame()
  34. {
  35. game_type = AI;
  36. game->gameStatus = PLAYING;
  37. // 在数据模型中进行初始化功能
  38. game->startGame(game_type);
  39. update();
  40. }

1.2 显示落点

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

重写鼠标移动事件方法

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

 

给鼠标移动事件添加逻辑

1、准备需要的变量

2、检测是离哪个点最近

  1. void MainWindow::mouseMoveEvent(QMouseEvent *event)
  2. {
  3. // 通过鼠标的位置确定落子的标记
  4. int x = event->x();
  5. int y = event->y();
  6. // 棋盘边缘不能落子
  7. if(x >= MARGIN + BLOCK_SIZE / 2 && x < size().width() - MARGIN - BLOCK_SIZE / 2 &&
  8. y >= MARGIN + BLOCK_SIZE / 2 && y < size().height() - MARGIN - BLOCK_SIZE / 2)
  9. {
  10. // 获取最近的左上角的点
  11. // add by rock
  12. int col = (x - MARGIN) / BLOCK_SIZE;
  13. int row = (y - MARGIN) / BLOCK_SIZE;
  14. int leftTopPosX = MARGIN + BLOCK_SIZE * col;
  15. int leftTopPosY = MARGIN + BLOCK_SIZE * row;
  16. //根据距离算出合适的点击位置,一 共四个点,根据半径距离选最近的
  17. clickPosRow = -1; //初始化最终的值
  18. clickPosCol = -1;
  19. int len = 0; // 计算完后取整就可以了
  20. selectPos = false;
  21. //确定一个误差在范围内的点,且只可能确定一个出来
  22. len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY) * (y - leftTopPosY));
  23. if(len < POS_OFFSET)
  24. {
  25. clickPosRow = row;
  26. clickPosCol = col;
  27. if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
  28. selectPos = true;
  29. }
  30. }
  31. len = sqrt((x - leftTopPosX - BLOCK_SIZE) * (x - leftTopPosX - BLOCK_SIZE) + (y - leftTopPosY - BLOCK_SIZE) * (y - leftTopPosY - BLOCK_SIZE));
  32. if(len < POS_OFFSET)
  33. {
  34. clickPosRow = row;
  35. clickPosCol = col + 1;
  36. if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
  37. selectPos = true;
  38. }
  39. }
  40. len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY - BLOCK_SIZE) * (y - leftTopPosY - BLOCK_SIZE));
  41. if(len < POS_OFFSET)
  42. {
  43. clickPosRow = row + 1;
  44. clickPosCol = col;
  45. if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
  46. selectPos = true;
  47. }
  48. }
  49. len = sqrt((x - leftTopPosX - BLOCK_SIZE) * (x - leftTopPosX - BLOCK_SIZE) + (y - leftTopPosY) * (y - leftTopPosY));
  50. if(len < POS_OFFSET)
  51. {
  52. clickPosRow = row + 1;
  53. clickPosCol = col + 1;
  54. if(game->gameMapVec[clickPosRow][clickPosCol] == 0) {
  55. selectPos = true;
  56. }
  57. }
  58. }
  59. // 存了坐标后要重绘
  60. update();
  61. }

3、绘制落点

  1. void MainWindow::paintEvent(QPaintEvent *event)
  2. {
  3. QPainter painter(this);
  4. //绘制棋盘
  5. painter.setRenderHint (QPainter::Antialiasing, true); // 抗锯齿
  6. for(int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
  7. {
  8. // 从左到右,第(i+1)条竖线
  9. painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
  10. // 从上到下,第(i+1)条横线
  11. painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
  12. }
  13. // 绘制选中点
  14. QBrush brush;
  15. brush.setStyle(Qt::SolidPattern);
  16. // 绘制落子标记(防止鼠标出框越界)
  17. if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
  18. clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
  19. game->gameMapVec[clickPosRow][clickPosCol] == 0)
  20. {
  21. if(game->playerFlag) {
  22. brush.setColor(Qt::black);
  23. }
  24. else {
  25. brush.setColor(Qt::white);
  26. }
  27. painter.setBrush(brush);
  28. painter.drawRect(MARGIN + BLOCK_SIZE * clickPosCol - MARK_SIZE / 2, MARGIN + BLOCK_SIZE * clickPosRow - MARK_SIZE, 8, 8);
  29. }
  30. }

1.3 落子功能

1、重写鼠标释放事件

  1. void MainWindow::mouseReleaseEvent(QMouseEvent *event)
  2. {
  3. if(selectPos == false) {
  4. return;
  5. } else {
  6. selectPos = false;
  7. }
  8. chessOneByPerson();
  9. if(game_type == AI) { // 人机模式
  10. // AI下棋
  11. }
  12. }

2、写下棋的方法

  1. void MainWindow::chessOneByPerson()
  2. {
  3. // 根据当前存储的坐标下子
  4. // 只有有效点击才下子,并且该处没有子
  5. if(clickPosRow != -1 && clickPosCol != -1 && game->gameMapVec[clickPosRow][clickPosRow] == 0)
  6. {
  7. game->actionByPerson(clickPosRow, clickPosCol);
  8. // 播放落子音效,待实现
  9. //重绘
  10. update() ;
  11. }
  12. }

  1. void GameModel::actionByPerson(int row, int col)
  2. {
  3. updateGameMap(row, col);
  4. }
  5. void GameModel::updateGameMap(int row, int col)
  6. {
  7. if(playerFlag)
  8. gameMapVec[row][col] = 1;
  9. else
  10. gameMapVec[row][col] = -1;
  11. // 换手
  12. playerFlag = !playerFlag;
  13. }

3、绘制棋子

  1. void MainWindow::paintEvent(QPaintEvent *event)
  2. {
  3. QPainter painter(this);
  4. //绘制棋盘
  5. painter.setRenderHint (QPainter::Antialiasing, true); // 抗锯齿
  6. for(int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
  7. {
  8. // 从左到右,第(i+1)条竖线
  9. painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
  10. // 从上到下,第(i+1)条横线
  11. painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
  12. }
  13. // 绘制选中点
  14. QBrush brush;
  15. brush.setStyle(Qt::SolidPattern);
  16. // 绘制落子标记(防止鼠标出框越界)
  17. if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
  18. clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
  19. game->gameMapVec[clickPosRow][clickPosCol] == 0)
  20. {
  21. if(game->playerFlag) {
  22. brush.setColor(Qt::black);
  23. }
  24. else {
  25. brush.setColor(Qt::white);
  26. }
  27. painter.setBrush(brush);
  28. painter.drawRect(MARGIN + BLOCK_SIZE * clickPosCol - MARK_SIZE / 2, MARGIN + BLOCK_SIZE * clickPosRow - MARK_SIZE, 8, 8);
  29. }
  30. //绘制棋子
  31. for(int i = 0; i < BOARD_GRAD_SIZE; i++)
  32. for(int j = 0; j < BOARD_GRAD_SIZE; j++)
  33. {
  34. if(game->gameMapVec[i][j] == 1)
  35. {
  36. // brush.setColor(Qt::white);
  37. brush.setColor(Qt::black);
  38. painter.setBrush(brush);
  39. painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
  40. }
  41. else if(game->gameMapVec[i][j] == -1)
  42. {
  43. //brush.setColor(Qt::black);
  44. brush.setColor(Qt::white);
  45. painter.setBrush(brush);
  46. painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
  47. }
  48. }
  49. }

1.4 判断输赢

  1. bool GameModel::isWin(int row, int col)
  2. {
  3. // 横竖斜四种大情况,每种情况都根据当前落子往后遍历5个棋子,有一种符合就算赢
  4. // 水平方向
  5. for(int i = 0; i < 5; i++)
  6. {
  7. // 往左5个,往右匹配4个子,20种情况
  8. if(col - i > 0 && col - i + 4 < BOARD_GRAD_SIZE &&
  9. gameMapVec[row][col - i] == gameMapVec[row][col - i + 1] &&
  10. gameMapVec[row][col - i] == gameMapVec[row][col - i + 2] &&
  11. gameMapVec[row][col - i] == gameMapVec[row][col - i + 3] &&
  12. gameMapVec[row][col - i] == gameMapVec[row][col - i + 4]) {
  13. return true;
  14. }
  15. }
  16. // 竖直方向(上下延伸4个)
  17. for(int i = 0; i < 5; i++)
  18. {
  19. if(row - i > 0 && row - i + 4 < BOARD_GRAD_SIZE &&
  20. gameMapVec[row - i][col] == gameMapVec[row - i + 1][col] &&
  21. gameMapVec[row - i][col] == gameMapVec[row - i + 2][col] &&
  22. gameMapVec[row - i][col] == gameMapVec[row - i + 3][col] &&
  23. gameMapVec[row - i][col] == gameMapVec[row - i + 4][col]) {
  24. return true;
  25. }
  26. }
  27. // "/"方向(上下延伸4个)
  28. for(int i = 0; i < 5; i++)
  29. {
  30. if(row + i < BOARD_GRAD_SIZE && row + i - 4 > 0 &&
  31. col - i > 0 && col - i + 4 < BOARD_GRAD_SIZE &&
  32. gameMapVec[row + i][col - i] == gameMapVec[row + i - 1][col - i + 1] &&
  33. gameMapVec[row + i][col - i] == gameMapVec[row + i - 2][col - i + 2] &&
  34. gameMapVec[row + i][col - i] == gameMapVec[row + i - 3][col - i + 3] &&
  35. gameMapVec[row + i][col - i] == gameMapVec[row + i - 4][col - i + 4]) {
  36. return true;
  37. }
  38. }
  39. // "\"方向(上下延伸4个)
  40. for(int i = 0; i < 5; i++)
  41. {
  42. if(row - i > 0 && row + i + 4 < BOARD_GRAD_SIZE &&
  43. col - i > 0 && col - i + 4 < BOARD_GRAD_SIZE &&
  44. gameMapVec[row - i][col - i] == gameMapVec[row - i + 1][col - i + 1] &&
  45. gameMapVec[row - i][col - i] == gameMapVec[row - i + 2][col - i + 2] &&
  46. gameMapVec[row - i][col - i] == gameMapVec[row - i + 3][col - i + 3] &&
  47. gameMapVec[row - i][col - i] == gameMapVec[row - i + 4][col - i + 4]) {
  48. return true;
  49. }
  50. }
  51. return false;
  52. }

在重绘函数里面判断输赢

  1. void MainWindow::paintEvent(QPaintEvent *event)
  2. {
  3. QPainter painter(this);
  4. //绘制棋盘
  5. painter.setRenderHint (QPainter::Antialiasing, true); // 抗锯齿
  6. for(int i = 0; i < BOARD_GRAD_SIZE + 1; i++)
  7. {
  8. // 从左到右,第(i+1)条竖线
  9. painter.drawLine(MARGIN + BLOCK_SIZE * i, MARGIN, MARGIN + BLOCK_SIZE * i, size().height() - MARGIN);
  10. // 从上到下,第(i+1)条横线
  11. painter.drawLine(MARGIN, MARGIN + BLOCK_SIZE * i, size().width() - MARGIN, MARGIN + BLOCK_SIZE * i);
  12. }
  13. // 绘制选中点
  14. QBrush brush;
  15. brush.setStyle(Qt::SolidPattern);
  16. // 绘制落子标记(防止鼠标出框越界)
  17. if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
  18. clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
  19. game->gameMapVec[clickPosRow][clickPosCol] == 0)
  20. {
  21. if(game->playerFlag) {
  22. brush.setColor(Qt::black);
  23. }
  24. else {
  25. brush.setColor(Qt::white);
  26. }
  27. painter.setBrush(brush);
  28. painter.drawRect(MARGIN + BLOCK_SIZE * clickPosCol - MARK_SIZE / 2, MARGIN + BLOCK_SIZE * clickPosRow - MARK_SIZE, 8, 8);
  29. }
  30. //绘制棋子
  31. for(int i = 0; i < BOARD_GRAD_SIZE; i++)
  32. for(int j = 0; j < BOARD_GRAD_SIZE; j++)
  33. {
  34. if(game->gameMapVec[i][j] == 1)
  35. {
  36. // brush.setColor(Qt::white);
  37. brush.setColor(Qt::black);
  38. painter.setBrush(brush);
  39. painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
  40. }
  41. else if(game->gameMapVec[i][j] == -1)
  42. {
  43. //brush.setColor(Qt::black);
  44. brush.setColor(Qt::white);
  45. painter.setBrush(brush);
  46. painter.drawEllipse(MARGIN + BLOCK_SIZE * j - CHESS_RADTUS/2, MARGIN + BLOCK_SIZE * i - CHESS_RADTUS/2, CHESS_RADTUS, CHESS_RADTUS);
  47. }
  48. }
  49. // 判断输赢
  50. if(clickPosRow > 0 && clickPosRow < BOARD_GRAD_SIZE &&
  51. clickPosCol > 0 && clickPosCol < BOARD_GRAD_SIZE &&
  52. (game->gameMapVec[clickPosRow][clickPosCol] == 1 ||
  53. game->gameMapVec[clickPosRow][clickPosCol] == -1))
  54. {
  55. if(game->isWin(clickPosRow, clickPosCol) && game->gameStatus == PLAYING)
  56. {
  57. game->gameStatus = WIN;
  58. QString str;
  59. if(game->gameMapVec[clickPosRow][clickPosCol] == 1) {
  60. str = "黑棋";
  61. }
  62. if(game->gameMapVec[clickPosRow][clickPosCol] == -1) {
  63. str = "白棋";
  64. }
  65. QMessageBox::StandardButton btnValue = QMessageBox::information(this, "五子棋嬴家", str + "胜利");
  66. // 重置游戏状态,否则容易死循环
  67. if(btnValue == QMessageBox::Ok) {
  68. game->startGame(game_type);
  69. game->gameStatus = PLAYING;
  70. }
  71. }
  72. }
  73. }

二、五子棋 Ai

2.1 Ai策略

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

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

2.2 代码

  1. // 最关键的计算评分
  2. void GameModel::calculateScore()
  3. {
  4. //統計玩家或者電腦連成的子
  5. int personNum = 0; //玩家連成子的個數
  6. int botNum = 0; //AI連成子的個數
  7. int emptyNum = 0; //各方向空白位的個數
  8. //清空評分數組
  9. scoreMapVec.clear();
  10. for(int i=0;i<BOARD_GRAD_SIZE;i++){
  11. std::vector<int> lineScores;
  12. for(int j=0;j<BOARD_GRAD_SIZE;j++){
  13. lineScores.push_back(0);
  14. }
  15. scoreMapVec.push_back(lineScores);
  16. }
  17. //計分
  18. /*計分個人理解:
  19. * 遍歷每一個格子,判斷哪些是空白的點(即為0的點),以該點為中心,判斷周圍的八個點向外延伸的四格裡,
  20. * 有多少個是黑子、白子、空白,以此作為依據來評分。下方算法是以守為主,所以守的分數>攻的分數
  21. */
  22. for(int row=0;row<BOARD_GRAD_SIZE;row++){
  23. for(int col=0;col<BOARD_GRAD_SIZE;col++){
  24. //空白點才算
  25. if(row>0 && col>0 && gameMapVec[row][col]==0){
  26. //遍歷周圍8個方向
  27. for(int y=-1;y<=1;y++){
  28. for(int x=-1;x<=1;x++){
  29. //重置
  30. personNum = 0;
  31. botNum = 0;
  32. emptyNum = 0;
  33. //原坐標不算
  34. if(!(y==0 && x==0)){
  35. //每個方向延伸4個子
  36. //對玩家黑子評分(正反兩個方向)
  37. for(int i=1;i<=4;i++){
  38. if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
  39. col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
  40. gameMapVec[row+i*y][col+i*x]==1){ //真人玩家的子
  41. personNum++;
  42. }
  43. else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
  44. col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
  45. gameMapVec[row+i*y][col+i*x]==0){ //空白位
  46. emptyNum++;
  47. break;
  48. }
  49. else{ //出邊界,或有白子
  50. break;
  51. }
  52. }
  53. for(int i=1;i<=4;i++){
  54. if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
  55. col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
  56. gameMapVec[row-i*y][col-i*x]==1){ //真人玩家的子
  57. personNum++;
  58. }
  59. else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
  60. col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
  61. gameMapVec[row-i*y][col-i*x]==0){ //空白位
  62. emptyNum++;
  63. break;
  64. }
  65. else{ //出邊界,或有白子
  66. break;
  67. }
  68. }
  69. if(personNum == 1){ //殺2
  70. scoreMapVec[row][col]+=10;
  71. }else if(personNum == 2){ //殺3
  72. if(emptyNum == 1)
  73. scoreMapVec[row][col]+=30;
  74. else if(emptyNum == 2)
  75. scoreMapVec[row][col]+=40;
  76. }else if(personNum == 3){ //殺4
  77. //量變空位不一樣,優先級不一樣
  78. if(emptyNum == 1)
  79. scoreMapVec[row][col]+=60;
  80. else if(emptyNum == 2)
  81. scoreMapVec[row][col]+=110;
  82. }else if(personNum == 4){ //殺5
  83. scoreMapVec[row][col]+=10100;
  84. }
  85. //進行一次清空
  86. emptyNum = 0;
  87. //對AI白子評分
  88. for(int i=1;i<=4;i++){
  89. if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
  90. col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
  91. gameMapVec[row+i*y][col+i*x]==-1){ //AI的子
  92. botNum++;
  93. }else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
  94. col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
  95. gameMapVec[row+i*y][col+i*x]==0){ //空白位
  96. emptyNum++;
  97. break;
  98. }else{ //出邊界
  99. break;
  100. }
  101. }
  102. for(int i=1;i<=4;i++){
  103. if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
  104. col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
  105. gameMapVec[row-i*y][col-i*x]==-1){ //AI的子
  106. botNum++;
  107. }else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
  108. col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
  109. gameMapVec[row-i*y][col-i*x]==0){ //空白位
  110. emptyNum++;
  111. break;
  112. }else{ //出邊界
  113. break;
  114. }
  115. }
  116. if(botNum == 0){
  117. scoreMapVec[row][col]+=5; //活1
  118. }else if(botNum == 1){
  119. scoreMapVec[row][col]+=10; //活2
  120. }else if(botNum == 2){ //活3
  121. if(emptyNum == 1)
  122. scoreMapVec[row][col]+=25;
  123. else if(emptyNum == 2)
  124. scoreMapVec[row][col]+=50;
  125. }else if(botNum == 3){ //活4
  126. if(emptyNum == 1)
  127. scoreMapVec[row][col]+=55;
  128. else if(emptyNum == 2)
  129. scoreMapVec[row][col]+=100;
  130. }else if(botNum >= 4){ //活5
  131. scoreMapVec[row][col]+=20000;
  132. }
  133. }
  134. }
  135. }
  136. }
  137. }
  138. }
  139. }

每次 AI 思考的时间为 AI_THINK_TIME

写槽函数

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

  1. void GameModel::actionByAI(int &clickRow, int &clickCol)
  2. {
  3. //計算評分
  4. calculateScore();
  5. //從評分中找出最大分數的位置
  6. int maxScore = 0;
  7. std::vector<std::pair<int,int>> maxPoints;
  8. for(int row = 1;row<BOARD_GRAD_SIZE;row++){
  9. for(int col = 1;col<BOARD_GRAD_SIZE;col++){
  10. //前提是這個坐標是空的
  11. if(gameMapVec[row][col] == 0){
  12. if(scoreMapVec[row][col]>maxScore){ //找最大數和坐標
  13. maxPoints.clear();
  14. maxScore = scoreMapVec[row][col];
  15. maxPoints.push_back(std::make_pair(row,col));
  16. }else if(scoreMapVec[row][col] == maxScore){ //如果有多個最大值就將他們存儲起來,在後面的代碼隨機抽1個
  17. maxPoints.push_back(std::make_pair(row,col));
  18. }
  19. }
  20. }
  21. }
  22. //隨機落子,如果有多個點的話
  23. srand((unsigned)time(0));
  24. int index = rand()%maxPoints.size();
  25. std::pair<int,int> pointPair = maxPoints.at(index);
  26. clickRow = pointPair.first;
  27. clickCol = pointPair.second;
  28. updateGameMap(clickRow,clickCol);
  29. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/612475
推荐阅读
相关标签
  

闽ICP备14008679号