先容
本项目基于 Qt C++ 实现了一个完备的五子棋游戏,支持 人机对战 和 大家对战 模式,并提供了三种难度选择(简单、中等、困难)。界面雅观,逻辑清晰,是一个综合性很强的 Qt 小项目
标题项目核心功能
- 棋盘绘制:通过 QPainter 实现网格棋盘和棋子的绘制,利用坐标映射鼠标点击位置到棋盘。
- 落子规则:处理处罚玩家或 AI 的落子,并查抄是否得胜。
- 人机对战:根据难度,AI 实现从简单的随机落子到基于评分体系的智能落子。
- 模式切换:支持大家对战和人机对战模式,切换后自动重置棋盘。
- 难度选择:通过下拉框提供“简单”、“中等”、“困难”三种难度。
本项目是一个范例的五子棋游戏,涵盖了 Qt 界面开辟、绘图、变乱处理处罚以及简单的 AI 逻辑实现。通过界面美化和模式选择功能,为玩家提供了精良的用户体验,非常适互助为 Qt 项目标学习和展示案例。
上代码
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include <QPainter>
- #include <QMouseEvent>
- #include <QVector>
- #include <QPushButton>
- #include <QLabel>
- #include <QComboBox>
- QT_BEGIN_NAMESPACE
- namespace Ui { class MainWindow; }
- QT_END_NAMESPACE
- class MainWindow : public QMainWindow {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = nullptr);
- ~MainWindow();
- protected:
- void paintEvent(QPaintEvent *event) override; // 绘制棋盘和棋子
- void mousePressEvent(QMouseEvent *event) override; // 处理鼠标点击落子
- private slots:
- void onNewGameClicked(); // 新游戏按钮事件
- void onSwitchModeClicked(); // 切换模式按钮事件
- void onDifficultyChanged(int index); // 选择难度下拉框事件
- private:
- Ui::MainWindow *ui;
- // 界面控件
- QLabel *modeLabel; // 显示当前模式
- QLabel *statusLabel; // 显示当前轮次
- QComboBox *difficultyComboBox; // 难度选择下拉框
- QPushButton *newGameButton; // 新游戏按钮
- QPushButton *switchModeButton; // 切换模式按钮
- // 游戏状态
- const int gridSize = 40; // 棋盘格子大小
- const int boardSize = 15; // 棋盘行列数
- QVector<QVector<int>> board; // 棋盘状态,0为空,1为黑棋,2为白棋
- bool isBlackTurn = true; // 当前是否轮到黑棋
- bool isPlayerVsAI = false; // 是否是人机对战模式
- int difficultyLevel = 1; // 默认难度为简单模式
- // 内部方法
- void checkWin(int x, int y); // 检查胜负
- void resetGame(); // 重置棋盘
- void aiMove(); // AI 落子逻辑
- int calculateScore(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 = new QPushButton("新游戏", this);
- switchModeButton = new QPushButton("切换模式", this);
- // 初始化标签
- statusLabel = new QLabel("当前轮到黑棋", this);
- modeLabel = new QLabel("当前模式:人人对战", this);
- // 初始化难度选择下拉框
- difficultyComboBox = new QComboBox(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;
- }
- void MainWindow::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)); // 黑色线条,宽度2px
- for (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);
- } else if (board[i][j] == 2) {
- painter.setBrush(Qt::white);
- painter.drawEllipse(gridSize / 2 + i * gridSize - 15,
- gridSize / 2 + j * gridSize - 15, 30, 30);
- }
- }
- }
- }
- void MainWindow::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();
- }
- }
- void MainWindow::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;
- else
- break;
- }
- 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;
- else
- break;
- }
- if (count >= 5) {
- QString winner = (currentPlayer == 1) ? "黑棋" : "白棋";
- QMessageBox::information(this, "游戏结束", winner + " 胜利!");
- resetGame();
- return;
- }
- }
- }
- void MainWindow::resetGame() {
- for (auto &row : board)
- row.fill(0);
- isBlackTurn = true;
- statusLabel->setText("当前轮到黑棋");
- update();
- }
- void MainWindow::onNewGameClicked() {
- resetGame();
- }
- void MainWindow::onSwitchModeClicked() {
- isPlayerVsAI = !isPlayerVsAI; // 切换模式
- QString modeText = isPlayerVsAI ? "人机对战模式" : "人人对战模式";
- modeLabel->setText("当前模式:" + modeText); // 更新模式显示
- QMessageBox::information(this, "模式切换", modeText);
- resetGame(); // 切换模式后重置棋盘
- }
- void MainWindow::onDifficultyChanged(int index) {
- difficultyLevel = index + 1; // 更新难度等级
- QString difficultyText = difficultyComboBox->currentText();
- QMessageBox::information(this, "难度选择", "当前难度:" + difficultyText);
- }
- void MainWindow::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);
- }
- }
- int MainWindow::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++;
- } else if (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++;
- } else if (board[nx][ny] == 0) {
- empty++;
- break;
- } else {
- block++;
- break;
- }
- }
- // 根据连子数、空位数和堵塞情况评估分值
- if (count >= 5) {
- score += 10000; // 连成五子,最高分
- } else if (count == 4 && block == 0) {
- score += 1000; // 活四
- } else if (count == 4 && block == 1) {
- score += 500; // 冲四
- } else if (count == 3 && block == 0) {
- score += 200; // 活三
- } else if (count == 3 && block == 1) {
- score += 50; // 冲三
- } else if (count == 2 && block == 0) {
- score += 10; // 活二
- }
- }
- return score;
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |