Qt、C++实现五子棋人机对战与本地双人对战(高难度AI,极少代码)
#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