• QT学习日记21——五子棋AI


    学习视频链接

    五子棋-3_bilibili_哔哩哔哩_bilibilihttps://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
    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::vectorint>> gameMapVec;
    33. // 存储各个点位的评分情况,作为AI下棋依据
    34. std::vectorint>> 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
    4. #include
    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 、#include

    重写鼠标移动事件方法

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

     

    给鼠标移动事件添加逻辑

    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
    11. std::vector<int> lineScores;
    12. for(int j=0;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
    23. for(int col=0;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
    39. col+i*x>0 && col+i*x
    40. gameMapVec[row+i*y][col+i*x]==1){ //真人玩家的子
    41. personNum++;
    42. }
    43. else if(row+i*y>0 && row+i*y
    44. col+i*x>0 && col+i*x
    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
    55. col-i*x>0 && col-i*x
    56. gameMapVec[row-i*y][col-i*x]==1){ //真人玩家的子
    57. personNum++;
    58. }
    59. else if(row-i*y>0 && row-i*y
    60. col-i*x>0 && col-i*x
    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
    90. col+i*x>0 && col+i*x
    91. gameMapVec[row+i*y][col+i*x]==-1){ //AI的子
    92. botNum++;
    93. }else if(row+i*y>0 && row+i*y
    94. col+i*x>0 && col+i*x
    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
    104. col-i*x>0 && col-i*x
    105. gameMapVec[row-i*y][col-i*x]==-1){ //AI的子
    106. botNum++;
    107. }else if(row-i*y>0 && row-i*y
    108. col-i*x>0 && col-i*x
    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 #include ,写 AI 下棋方法

     

    1. void GameModel::actionByAI(int &clickRow, int &clickCol)
    2. {
    3. //計算評分
    4. calculateScore();
    5. //從評分中找出最大分數的位置
    6. int maxScore = 0;
    7. std::vectorint,int>> maxPoints;
    8. for(int row = 1;row
    9. for(int col = 1;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. }
  • 相关阅读:
    datax使用笔记
    Python自动化办公【自动组织文件】
    Go基础学习笔记(二):错误处理和资源管理、Goroutine、Channel、迷宫的广度优先搜索、http及其他标准库
    一招解决vue页面自适应布局
    Unity接入腾讯云
    练习3
    Codeforces Round 895 (Div. 3) A ~ F
    MySQL(十五) 索引的分类
    Java中JVM的xmx和xms配置成一样的好处
    5-3:Spring整合Kafka
  • 原文地址:https://blog.csdn.net/HuanBianCheng27/article/details/126355807