0


Qt、C++实现五子棋人机对战与本地双人对战(高难度AI,极少代码)

介绍

本项目基于 Qt C++ 实现了一个完整的五子棋游戏,支持 人机对战 和 人人对战 模式,并提供了三种难度选择(简单、中等、困难)。界面美观,逻辑清晰,是一个综合性很强的 Qt 小项目

标题项目核心功能

  1. 棋盘绘制:通过 QPainter 实现网格棋盘和棋子的绘制,使用坐标映射鼠标点击位置到棋盘。
  2. 落子规则:处理玩家或 AI 的落子,并检查是否获胜。
  3. 人机对战:根据难度,AI 实现从简单的随机落子到基于评分系统的智能落子。
  4. 模式切换:支持人人对战和人机对战模式,切换后自动重置棋盘。
  5. 难度选择:通过下拉框提供“简单”、“中等”、“困难”三种难度。

本项目是一个典型的五子棋游戏,涵盖了 Qt 界面开发、绘图、事件处理以及简单的 AI 逻辑实现。通过界面美化和模式选择功能,为玩家提供了良好的用户体验,非常适合作为 Qt 项目的学习和展示案例。

在这里插入图片描述

上代码

#ifndefMAINWINDOW_H#defineMAINWINDOW_H#include<QMainWindow>#include<QPainter>#include<QMouseEvent>#include<QVector>#include<QPushButton>#include<QLabel>#include<QComboBox>

QT_BEGIN_NAMESPACE
namespace Ui {classMainWindow;}
QT_END_NAMESPACE

classMainWindow:publicQMainWindow{
    Q_OBJECT

public:explicitMainWindow(QWidget *parent =nullptr);~MainWindow();protected:voidpaintEvent(QPaintEvent *event) override;// 绘制棋盘和棋子voidmousePressEvent(QMouseEvent *event) override;// 处理鼠标点击落子private slots:voidonNewGameClicked();// 新游戏按钮事件voidonSwitchModeClicked();// 切换模式按钮事件voidonDifficultyChanged(int index);// 选择难度下拉框事件private:
    Ui::MainWindow *ui;// 界面控件
    QLabel *modeLabel;// 显示当前模式
    QLabel *statusLabel;// 显示当前轮次
    QComboBox *difficultyComboBox;// 难度选择下拉框
    QPushButton *newGameButton;// 新游戏按钮
    QPushButton *switchModeButton;// 切换模式按钮// 游戏状态constint gridSize =40;// 棋盘格子大小constint boardSize =15;// 棋盘行列数
    QVector<QVector<int>> board;// 棋盘状态,0为空,1为黑棋,2为白棋bool isBlackTurn =true;// 当前是否轮到黑棋bool isPlayerVsAI =false;// 是否是人机对战模式int difficultyLevel =1;// 默认难度为简单模式// 内部方法voidcheckWin(int x,int y);// 检查胜负voidresetGame();// 重置棋盘voidaiMove();// AI 落子逻辑intcalculateScore(int x,int y,int player);// 计算分数};#endif// MAINWINDOW_H
#include"mainwindow.h"#include"ui_mainwindow.h"#include<QMessageBox>#include<cstdlib>// 用于随机数生成(AI 落子)MainWindow::MainWindow(QWidget *parent):QMainWindow(parent),ui(new Ui::MainWindow),board(boardSize, QVector<int>(boardSize,0)){
    ui->setupUi(this);// 设置窗口大小setFixedSize(gridSize * boardSize, gridSize * boardSize +80);// 初始化按钮
    newGameButton =newQPushButton("新游戏",this);
    switchModeButton =newQPushButton("切换模式",this);// 初始化标签
    statusLabel =newQLabel("当前轮到黑棋",this);
    modeLabel =newQLabel("当前模式:人人对战",this);// 初始化难度选择下拉框
    difficultyComboBox =newQComboBox(this);
    difficultyComboBox->addItem("简单");
    difficultyComboBox->addItem("中等");
    difficultyComboBox->addItem("困难");
    difficultyComboBox->setCurrentIndex(difficultyLevel -1);// 默认选中“简单”// 设置控件位置
    newGameButton->setGeometry(10, gridSize * boardSize +10,100,30);
    switchModeButton->setGeometry(120, gridSize * boardSize +10,100,30);
    difficultyComboBox->setGeometry(240, gridSize * boardSize +10,80,30);
    statusLabel->setGeometry(330, gridSize * boardSize +10,120,30);
    modeLabel->setGeometry(10, gridSize * boardSize +50,200,30);// 设置按钮样式
    QString buttonStyle ="QPushButton {""   background-color: #87CEFA;"// 浅蓝色背景"   color: white;"// 白色文字"   border-radius: 10px;"// 圆角"   font-size: 14px;"// 字体大小"   padding: 5px 10px;""}""QPushButton:hover {""   background-color: #4682B4;"// Hover时深蓝色"}";

    newGameButton->setStyleSheet(buttonStyle);
    switchModeButton->setStyleSheet(buttonStyle);// 设置下拉框样式
    difficultyComboBox->setStyleSheet("QComboBox {""   border: 2px solid #4682B4;"// 深蓝色边框"   border-radius: 5px;""   padding: 3px 8px;""   font-size: 14px;""}""QComboBox::drop-down {""   border: none;""}""QComboBox:hover {""   border-color: #87CEFA;"// Hover时浅蓝边框"}");// 设置标签样式
    QString labelStyle ="QLabel {""   font-size: 16px;""   color: #2F4F4F;"// 深灰色文字"   font-weight: bold;""}";

    statusLabel->setStyleSheet(labelStyle);
    modeLabel->setStyleSheet(labelStyle);// 绑定信号与槽connect(newGameButton,&QPushButton::clicked,this,&MainWindow::onNewGameClicked);connect(switchModeButton,&QPushButton::clicked,this,&MainWindow::onSwitchModeClicked);connect(difficultyComboBox,QOverload<int>::of(&QComboBox::currentIndexChanged),this,&MainWindow::onDifficultyChanged);}MainWindow::~MainWindow(){delete ui;}voidMainWindow::paintEvent(QPaintEvent *){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);// 绘制棋盘背景
    painter.setBrush(QColor(245,222,179));// 棋盘为浅棕色
    painter.drawRect(0,0, gridSize * boardSize, gridSize * boardSize);// 绘制棋盘线条
    painter.setPen(QPen(Qt::black,2));// 黑色线条,宽度2pxfor(int i =0; i < boardSize;++i){
        painter.drawLine(gridSize /2, gridSize /2+ i * gridSize,
                         gridSize /2+(boardSize -1)* gridSize, gridSize /2+ i * gridSize);
        painter.drawLine(gridSize /2+ i * gridSize, gridSize /2,
                         gridSize /2+ i * gridSize, gridSize /2+(boardSize -1)* gridSize);}// 绘制棋子for(int i =0; i < boardSize;++i){for(int j =0; j < boardSize;++j){if(board[i][j]==1){
                painter.setBrush(Qt::black);
                painter.drawEllipse(gridSize /2+ i * gridSize -15,
                                    gridSize /2+ j * gridSize -15,30,30);}elseif(board[i][j]==2){
                painter.setBrush(Qt::white);
                painter.drawEllipse(gridSize /2+ i * gridSize -15,
                                    gridSize /2+ j * gridSize -15,30,30);}}}}voidMainWindow::mousePressEvent(QMouseEvent *event){if(!isBlackTurn && isPlayerVsAI)return;// 人机对战时禁止白棋玩家操作// 计算点击的位置对应的棋盘格子int x =(event->x()- gridSize /2+ gridSize /2)/ gridSize;int y =(event->y()- gridSize /2+ gridSize /2)/ gridSize;// 检查点击是否在有效范围内,且是否未落子if(x <0|| x >= boardSize || y <0|| y >= boardSize || board[x][y]!=0)return;// 记录当前落子
    board[x][y]= isBlackTurn ?1:2;
    isBlackTurn =!isBlackTurn;// 更新提示信息
    statusLabel->setText(isBlackTurn ?"当前轮到黑棋":"当前轮到白棋");update();// 更新界面checkWin(x, y);// 如果是人机对战模式,AI 落子if(!isBlackTurn && isPlayerVsAI){aiMove();}}voidMainWindow::checkWin(int x,int y){int directions[4][2]={{1,0},{0,1},{1,1},{1,-1}};int currentPlayer = board[x][y];for(auto&dir : directions){int count =1;for(int i =1; i <5;++i){int nx = x + i * dir[0], ny = y + i * dir[1];if(nx >=0&& nx < boardSize && ny >=0&& ny < boardSize && board[nx][ny]== currentPlayer)++count;elsebreak;}for(int i =1; i <5;++i){int nx = x - i * dir[0], ny = y - i * dir[1];if(nx >=0&& nx < boardSize && ny >=0&& ny < boardSize && board[nx][ny]== currentPlayer)++count;elsebreak;}if(count >=5){
            QString winner =(currentPlayer ==1)?"黑棋":"白棋";QMessageBox::information(this,"游戏结束", winner +" 胜利!");resetGame();return;}}}voidMainWindow::resetGame(){for(auto&row : board)
        row.fill(0);
    isBlackTurn =true;
    statusLabel->setText("当前轮到黑棋");update();}voidMainWindow::onNewGameClicked(){resetGame();}voidMainWindow::onSwitchModeClicked(){
    isPlayerVsAI =!isPlayerVsAI;// 切换模式
    QString modeText = isPlayerVsAI ?"人机对战模式":"人人对战模式";
    modeLabel->setText("当前模式:"+ modeText);// 更新模式显示QMessageBox::information(this,"模式切换", modeText);resetGame();// 切换模式后重置棋盘}voidMainWindow::onDifficultyChanged(int index){
    difficultyLevel = index +1;// 更新难度等级
    QString difficultyText = difficultyComboBox->currentText();QMessageBox::information(this,"难度选择","当前难度:"+ difficultyText);}voidMainWindow::aiMove(){int bestX =-1, bestY =-1;if(difficultyLevel ==1){// 简单模式:随机选择空位while(true){int x =rand()% boardSize;int y =rand()% boardSize;if(board[x][y]==0){
                bestX = x;
                bestY = y;break;}}}else{// 中等/困难模式:计算最佳位置int maxScore =-1;
        QVector<QVector<int>>score(boardSize, QVector<int>(boardSize,0));for(int x =0; x < boardSize;++x){for(int y =0; y < boardSize;++y){if(board[x][y]!=0)continue;int baseScore =calculateScore(x, y,2)+calculateScore(x, y,1);if(difficultyLevel ==3){// 困难模式:增加中心权重int distanceToCenter =abs(x - boardSize /2)+abs(y - boardSize /2);
                    baseScore +=(boardSize - distanceToCenter)*5;}

                score[x][y]= baseScore;if(baseScore > maxScore){
                    maxScore = baseScore;
                    bestX = x;
                    bestY = y;}}}}// 在最佳位置落子if(bestX !=-1&& bestY !=-1){
        board[bestX][bestY]=2;
        isBlackTurn =true;
        statusLabel->setText("当前轮到黑棋");update();checkWin(bestX, bestY);}}intMainWindow::calculateScore(int x,int y,int player){int directions[4][2]={{1,0},{0,1},{1,1},{1,-1}};int score =0;for(auto&dir : directions){int count =1;// 当前玩家的连子数int block =0;// 是否被堵住:0为两端均开,1为一端堵,2为两端堵int empty =0;// 连子中的空位数// 正向检查for(int i =1; i <5;++i){int nx = x + i * dir[0];int ny = y + i * dir[1];if(nx <0|| nx >= boardSize || ny <0|| ny >= boardSize){
                block++;// 超出棋盘视为堵住break;}if(board[nx][ny]== player){
                count++;}elseif(board[nx][ny]==0){
                empty++;break;// 停止正向检查}else{
                block++;break;// 对方棋子,视为堵住}}// 反向检查for(int i =1; i <5;++i){int nx = x - i * dir[0];int ny = y - i * dir[1];if(nx <0|| nx >= boardSize || ny <0|| ny >= boardSize){
                block++;break;}if(board[nx][ny]== player){
                count++;}elseif(board[nx][ny]==0){
                empty++;break;}else{
                block++;break;}}// 根据连子数、空位数和堵塞情况评估分值if(count >=5){
            score +=10000;// 连成五子,最高分}elseif(count ==4&& block ==0){
            score +=1000;// 活四}elseif(count ==4&& block ==1){
            score +=500;// 冲四}elseif(count ==3&& block ==0){
            score +=200;// 活三}elseif(count ==3&& block ==1){
            score +=50;// 冲三}elseif(count ==2&& block ==0){
            score +=10;// 活二}}return score;}
标签: qt c++ 人工智能

本文转载自: https://blog.csdn.net/LuXiaoXin1999/article/details/143889440
版权归原作者 Qt云程序员 所有, 如有侵权,请联系我们删除。

“Qt、C++实现五子棋人机对战与本地双人对战(高难度AI,极少代码)”的评论:

还没有评论