失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > C++ Qt实现经典五子棋小游戏(双人游戏 人机大战)

C++ Qt实现经典五子棋小游戏(双人游戏 人机大战)

时间:2019-09-22 04:04:55

相关推荐

C++ Qt实现经典五子棋小游戏(双人游戏 人机大战)

原创,转载请注明出处。

这里用Qt实现了五子棋,可进行双人游戏,人机对战,悔棋等操作,是C++,Qt的必备练手项目,界面设置如下图:

添加三个类,分别为游戏总控制类gamewidget 、界面类boardwidget和AI控制类gomokuai。代码分别如下:

boardwidget.h:

#ifndef BOARDWIDGET_H#define BOARDWIDGET_H#include <QWidget>#include <QStack>#include <QPoint>#include <QSet>typedef int (*Board)[15];class BoardWidget : public QWidget{Q_OBJECTpublic:explicit BoardWidget(QWidget *parent = nullptr);//设置棋盘控件接受的下棋方,在ai模式中只接受白色方,双人模式中都接受void setReceivePlayers(const QSet<int> &value);//获取棋盘信息Board getBoard();protected:void paintEvent(QPaintEvent *event);void mouseReleaseEvent(QMouseEvent *event);void mouseMoveEvent(QMouseEvent *event);private://初始化棋盘控件void initBoard();//切换下一位下棋者void switchNextPlayer();//检查是否有获胜方,或者平局void checkWinner();//判断从(x, y)处开始,是否有五个同色棋子在一条线上bool isFivePieceFrom(int x, int y);//判断从(x, y)处开始,向下是否有五个同色棋子bool isVFivePieceFrom(int x, int y);//判断从(x, y)处开始,向下是否有五个同色棋子bool isHFivePieceFrom(int x, int y);//判断从(x, y)处开始,右上方向是否有五个同色棋子bool isFSFivePieceFrom(int x, int y);//判断(x, y)处开始, 右下方向是否有五个同色棋子bool isBSFivePieceFrom(int x, int y);signals:void gameOver(int winner);void turnNextPlayer(int player);public slots://清除棋盘信息,开始新游戏void newGame();//落子void downPiece(int x, int y);void undo(int steps);//悔棋public:static const QSize WIDGET_SIZE; //棋盘控件大小static const QSize CELL_SIZE; //棋盘单元格大小static const QPoint START_POS;//棋盘单元格开始位置static const QPoint ROW_NUM_START;//行标号开始位置static const QPoint CLU_NUM_START;//列标号开始位置static const int BOARD_WIDTH = 15;//棋盘列数static const int BOARD_HEIGHT = 15;//棋盘行数static const int NO_PIECE = 0;//棋子标志,表示无子static const int WHITE_PIECE = 1; //棋子标志, 表示白子static const int BLACK_PIECE = 2; //棋子标志,表示黑子static const bool WHITE_PLAYER = true; //棋手标志, 表示白方static const bool BLACK_PLAYER = false; //棋手标志, 表示黑方void setTrackPos(const QPoint &value); //设置当前鼠标所在棋盘中的位置private:bool endGame; //游戏是否结束标志int board[BOARD_WIDTH][BOARD_HEIGHT]; //棋盘信息int nextPlayer; //表示下一位棋手QPoint trackPos; //记录鼠标在棋盘中的位置QVector<QPoint> winPoses; //获胜的五子位置QSet<int> receivePlayers; //棋盘接受点击下棋的棋手QStack<QPoint> dropedPieces;//每一步落子位置};#endif // BOARDWIDGET_H

boardwidget.cpp:

#include "boardwidget.h"#include <QPainter>#include <QMouseEvent>#include <QFile>#include <QDataStream>/*类静态数据成员定义*/const QSize BoardWidget::WIDGET_SIZE(430, 430);const QSize BoardWidget::CELL_SIZE(25, 25);const QPoint BoardWidget::START_POS(40, 40);const QPoint BoardWidget::ROW_NUM_START(15, 45);const QPoint BoardWidget::CLU_NUM_START(39, 25);const int BoardWidget::BOARD_WIDTH;const int BoardWidget::BOARD_HEIGHT;const int BoardWidget::NO_PIECE;const int BoardWidget::WHITE_PIECE;const int BoardWidget::BLACK_PIECE;const bool BoardWidget::WHITE_PLAYER;const bool BoardWidget::BLACK_PLAYER;BoardWidget::BoardWidget(QWidget *parent) :QWidget(parent),trackPos(28, 28){setFixedSize(WIDGET_SIZE);setMouseTracking(true);initBoard();}void BoardWidget::paintEvent(QPaintEvent *event){QPainter painter(this);painter.fillRect(0, 0, width(), height(), Qt::lightGray);//背景颜色for (int i = 0; i < BOARD_WIDTH; i++){painter.drawText(CLU_NUM_START + QPoint(i * CELL_SIZE.width(), 0),QString::number(i + 1));}for (int i = 0; i < BOARD_HEIGHT; i++){painter.drawText(ROW_NUM_START + QPoint(0, i * CELL_SIZE.height()),QString::number(i + 1));}for (int i = 0; i < BOARD_WIDTH - 1; i++)//绘制棋盘格子{for (int j = 0; j < BOARD_HEIGHT - 1; j++){painter.drawRect(QRect(START_POS + QPoint(i * CELL_SIZE.width(), j * CELL_SIZE.height()),CELL_SIZE));}}painter.setPen(Qt::red);QPoint poses[12] = {trackPos + QPoint(0, 8),trackPos,trackPos + QPoint(8, 0),trackPos + QPoint(17, 0),trackPos + QPoint(25, 0),trackPos + QPoint(25, 8),trackPos + QPoint(25, 17),trackPos + QPoint(25, 25),trackPos + QPoint(17, 25),trackPos + QPoint(8, 25),trackPos + QPoint(0, 25),trackPos + QPoint(0, 17)};painter.drawPolyline(poses, 3);painter.drawPolyline(poses + 3, 3);painter.drawPolyline(poses + 6, 3);painter.drawPolyline(poses + 9, 3);painter.setPen(Qt::NoPen);for (int i = 0; i < BOARD_WIDTH; i++)//绘制棋子{for (int j = 0; j < BOARD_HEIGHT; j++){if (board[i][j] != NO_PIECE){QColor color = (board[i][j] == WHITE_PIECE) ? Qt::white : Qt::black;painter.setBrush(QBrush(color));painter.drawEllipse(START_POS.x() - CELL_SIZE.width()/2 + i*CELL_SIZE.width(),START_POS.y() - CELL_SIZE.height()/2 + j*CELL_SIZE.height(),CELL_SIZE.width(), CELL_SIZE.height());}}}painter.setPen(Qt::red);if (!dropedPieces.isEmpty()){QPoint lastPos = dropedPieces.top();QPoint drawPos = START_POS + QPoint(lastPos.x() * CELL_SIZE.width(), lastPos.y() * CELL_SIZE.height());painter.drawLine(drawPos + QPoint(0, 5), drawPos + QPoint(0, -5));painter.drawLine(drawPos + QPoint(5, 0), drawPos + QPoint(-5, 0));}for (QPoint pos : winPoses){QPoint drawPos = START_POS + QPoint(pos.x() * CELL_SIZE.width(), pos.y() * CELL_SIZE.height());painter.drawLine(drawPos + QPoint(0, 5), drawPos + QPoint(0, -5));painter.drawLine(drawPos + QPoint(5, 0), drawPos + QPoint(-5, 0));}}void BoardWidget::mouseReleaseEvent(QMouseEvent *event){if (receivePlayers.contains(nextPlayer) && !endGame){QPoint pos = event->pos() - START_POS;int x = pos.x();int y = pos.y();int pieceX = x / CELL_SIZE.width();int pieceY = y / CELL_SIZE.height();int offsetX = x % CELL_SIZE.width();int offsetY = y % CELL_SIZE.height();if (offsetX > CELL_SIZE.width() / 2){pieceX++;}if (offsetY > CELL_SIZE.height() / 2){pieceY++;}downPiece(pieceX, pieceY);}}void BoardWidget::mouseMoveEvent(QMouseEvent *event){QPoint pos = event->pos() - START_POS + QPoint(CELL_SIZE.width()/2, CELL_SIZE.height()/2);int x = pos.x();int y = pos.y();//超过范围if (x < 0 || x >= CELL_SIZE.width() * BOARD_WIDTH ||y < 0 || y >= CELL_SIZE.height() * BOARD_HEIGHT){return;}int offsetX = x % CELL_SIZE.width();int offsetY = y % CELL_SIZE.height();setTrackPos(QPoint(x - offsetX, y - offsetY) + START_POS - QPoint(CELL_SIZE.width()/2, CELL_SIZE.height()/2));}void BoardWidget::initBoard(){receivePlayers << WHITE_PLAYER << BLACK_PLAYER;newGame();}void BoardWidget::downPiece(int x, int y){if (x >= 0 && x < BOARD_WIDTH && y >= 0 && y < BOARD_HEIGHT && board[x][y] == NO_PIECE){dropedPieces.push(QPoint(x, y));board[x][y] = (nextPlayer == WHITE_PLAYER) ? WHITE_PIECE : BLACK_PIECE;update();checkWinner();if (!endGame){switchNextPlayer();}}}void BoardWidget::undo(int steps){if (!endGame){for (int i = 0; i < steps && !dropedPieces.isEmpty(); i++){QPoint pos = dropedPieces.pop();board[pos.x()][pos.y()] = NO_PIECE;}update();if(steps==2){nextPlayer=BLACK_PLAYER;}switchNextPlayer();}}void BoardWidget::setTrackPos(const QPoint &value){trackPos = value;update();}void BoardWidget::setReceivePlayers(const QSet<int> &value){receivePlayers = value;}Board BoardWidget::getBoard(){return board;}void BoardWidget::switchNextPlayer(){nextPlayer = !nextPlayer;emit turnNextPlayer(nextPlayer);}void BoardWidget::checkWinner(){bool fullPieces = true;for (int i = 0; i < BOARD_WIDTH; i++){for (int j = 0; j < BOARD_HEIGHT; j++){if (board[i][j] == NO_PIECE){fullPieces = false;}if (board[i][j] != NO_PIECE && isFivePieceFrom(i, j)){bool winner = (board[i][j] == WHITE_PIECE) ? WHITE_PLAYER : BLACK_PLAYER;endGame = true;emit gameOver(winner);}}}if (fullPieces){endGame = true;emit gameOver(2); //代表和棋}}bool BoardWidget::isFivePieceFrom(int x, int y){return isVFivePieceFrom(x, y) || isHFivePieceFrom(x, y) || isFSFivePieceFrom(x, y) || isBSFivePieceFrom(x, y);}bool BoardWidget::isVFivePieceFrom(int x, int y){int piece = board[x][y];for (int i = 1; i < 5; i++){if (y + i >= BOARD_HEIGHT || board[x][y + i] != piece){return false;}}winPoses.clear();for (int i = 0; i < 5; i++){winPoses.append(QPoint(x, y + i));}return true;}bool BoardWidget::isHFivePieceFrom(int x, int y){int piece = board[x][y];for (int i = 1; i < 5; i++){if (x + i >= BOARD_WIDTH || board[x + i][y] != piece){return false;}}winPoses.clear();for (int i = 0; i < 5; i++){winPoses.append(QPoint(x + i, y));}return true;}bool BoardWidget::isFSFivePieceFrom(int x, int y){int piece = board[x][y];for (int i = 1; i < 5; i++){if (x + i >= BOARD_WIDTH || y - i < 0 || board[x + i][y - i] != piece){return false;}}winPoses.clear();for (int i = 0; i < 5; i++){winPoses.append(QPoint(x + i, y - i));}return true;}bool BoardWidget::isBSFivePieceFrom(int x, int y){int piece = board[x][y];for (int i = 1; i < 5; i++){if (x + i >= BOARD_WIDTH || y + i >= BOARD_HEIGHT || board[x + i][y + i] != piece){return false;}}winPoses.clear();for (int i = 0; i < 5; i++){winPoses.append(QPoint(x + i, y + i));}return true;}void BoardWidget::newGame(){for (int i = 0; i < BOARD_WIDTH; i++){for (int j = 0; j < BOARD_HEIGHT; j++){board[i][j] = NO_PIECE;}}winPoses.clear();dropedPieces.clear();nextPlayer = BLACK_PLAYER;endGame = false;update();emit turnNextPlayer(nextPlayer);}

gamewidget.h :

#ifndef GAMEWIDGET_H#define GAMEWIDGET_H#include <QWidget>#include <QLabel>#include <QPushButton>#include "boardwidget.h"#include "gomokuai.h"class GameWidget : public QWidget{Q_OBJECTpublic:GameWidget(QWidget *parent = 0);~GameWidget();private://初始化游戏窗口界面void initWidget();//显示获胜者void showWinner(int winner);//切换playerLabel标签显示内容void switchPlayerLabel(bool player);//切换至双人对战模式void switchHumanGame();//切换到Ai对战模式void switchAiGame();//接受下棋信号,如果为ai模式且轮到ai时,搜索最佳位置下棋void nextDropPiece(bool player);//悔棋void undo();//新游戏;void newGame();private:GomokuAi *ai;//ai 对象指针bool isGameWithAi;//是否为ai对战模式BoardWidget *boardWidget; //棋盘控件QLabel *playerLabel; //用于提示下一步轮哪一方QLabel *winLabel; //提示获胜的标签QPushButton *newGameBtn; //新游戏按钮QPushButton *humanGameBtn; //双人游戏按钮QPushButton *aiGameBtn;//ai对战按钮QPushButton *undoBtn; //悔棋按钮};#endif // GAMEWIDGET_H

gamewidget.cpp:

#include "gamewidget.h"#include <QPushButton>#include <QHBoxLayout>#include <QMessageBox>#include <QFileDialog>#include <QLabel>#include <QDebug>GameWidget::GameWidget(QWidget *parent): QWidget(parent){ai = new GomokuAi(this);isGameWithAi = false;initWidget();switchHumanGame();}GameWidget::~GameWidget(){}void GameWidget::initWidget(){setWindowTitle("五子棋");//创建水平布局作为总布局QHBoxLayout *mainLayout = new QHBoxLayout(this);//棋盘控件boardWidget = new BoardWidget(this);mainLayout->addWidget(boardWidget);//创建垂直布局作为界面右边标签和菜单的子布局QVBoxLayout *vLayout = new QVBoxLayout();playerLabel = new QLabel("轮到白方落子", this);playerLabel->setFont(QFont("微软雅黑", 25));vLayout->addWidget(playerLabel);vLayout->addStretch();winLabel = new QLabel(this);winLabel->setFont(QFont("微软雅黑", 20));vLayout->addWidget(winLabel);vLayout->addStretch();//添加一个伸缩因子//创建栅格布局作为菜单按钮的布局QGridLayout *gLayout = new QGridLayout();newGameBtn = new QPushButton("新游戏");humanGameBtn = new QPushButton("双人游戏");aiGameBtn = new QPushButton("人机对战");undoBtn = new QPushButton("悔棋");gLayout->addWidget(newGameBtn, 0, 0);gLayout->addWidget(humanGameBtn, 0, 1);gLayout->addWidget(aiGameBtn, 0, 2);gLayout->addWidget(undoBtn, 0, 3);vLayout->addLayout(gLayout);mainLayout->addLayout(vLayout);connect(boardWidget, &BoardWidget::gameOver, this, &GameWidget::showWinner);connect(boardWidget, &BoardWidget::turnNextPlayer, this, &GameWidget::switchPlayerLabel, Qt::QueuedConnection);connect(boardWidget, &BoardWidget::turnNextPlayer, this, &GameWidget::nextDropPiece, Qt::QueuedConnection);connect(newGameBtn, &QPushButton::clicked, this, &GameWidget::newGame);connect(humanGameBtn, &QPushButton::clicked, this, &GameWidget::switchHumanGame);connect(aiGameBtn, &QPushButton::clicked, this, &GameWidget::switchAiGame);connect(undoBtn, &QPushButton::clicked, this, &GameWidget::undo);}void GameWidget::showWinner(int winner){if (winner != 2){QString playerName = (winner == BoardWidget::WHITE_PLAYER) ? "白方" : "黑方";QMessageBox::information(this, "游戏结束", tr("恭喜%1获胜!!").arg(playerName), QMessageBox::Ok);}else{QMessageBox::information(this, "游戏结束", "和棋!", QMessageBox::Ok);}}void GameWidget::switchPlayerLabel(bool player){QString playerName = (player == BoardWidget::WHITE_PLAYER) ? "白方" : "黑方";QString labelText = tr("轮到%1落子").arg(playerName);playerLabel->setText(labelText);}void GameWidget::switchHumanGame(){humanGameBtn->setEnabled(false);aiGameBtn->setEnabled(true);QSet<int> receivePlayers;receivePlayers << BoardWidget::WHITE_PLAYER << BoardWidget::BLACK_PLAYER;boardWidget->setReceivePlayers(receivePlayers);isGameWithAi = false;boardWidget->newGame();}void GameWidget::switchAiGame(){aiGameBtn->setEnabled(false);humanGameBtn->setEnabled(true);QSet<int> receivePlayers;receivePlayers << BoardWidget::WHITE_PLAYER;boardWidget->setReceivePlayers(receivePlayers);isGameWithAi = true;boardWidget->newGame();}void GameWidget::nextDropPiece(bool player){if (isGameWithAi && player == GomokuAi::AI_PLAYER){QPoint pos = ai->searchAGoodPos(boardWidget->getBoard());boardWidget->downPiece(pos.x(), pos.y());}}void GameWidget::undo(){if (isGameWithAi){boardWidget->undo(2);}else{boardWidget->undo(1);}}void GameWidget::newGame(){winLabel->setText("");boardWidget->newGame();}

gomokuai.h:

#ifndef GOMOKUAI_H#define GOMOKUAI_H#include <QObject>#include "boardwidget.h"class GomokuAi : public QObject{Q_OBJECTpublic:explicit GomokuAi(QObject *parent = nullptr);private://返回棋盘上所有空的位置QVector<QPoint> getAllDropPos();//返回该位置的相对于player的分数int getPieceScore(int x, int y, int player);//返回该位置的分数,越靠近中间分数越大int getPosValue(int x, int y);//返回该位置dire方向上的棋型int getChessType(int x, int y, int dire, int pieceColor);//返回此处棋型//获取该位置该方向上pieceColor颜色的连续棋子数, pieceEnd为结束位置棋子int getLinePieceNum(int x, int y, int dire, int pieceColor, int &pieceEnd);bool isBeyond(int x, int y);signals:public slots:QPoint searchAGoodPos(Board nBoard);public:static const int CHESS_VALUES[9];static const int MIN_VALUE = -2000000;static const int AI_PLAYER = BoardWidget::BLACK_PLAYER;private:int board[BoardWidget::BOARD_WIDTH][BoardWidget::BOARD_HEIGHT];QPoint bestPos;};#endif // GOMOKUAI_H

gomokuai.cpp:

#include "gomokuai.h"#include <QDebug>#include <cmath>const int GomokuAi::CHESS_VALUES[9] = {0, 50, 100, 200, 500, 650, 2500, 10000, 30000};const int GomokuAi::MIN_VALUE;const int GomokuAi::AI_PLAYER;GomokuAi::GomokuAi(QObject *parent) : QObject(parent){}QVector<QPoint> GomokuAi::getAllDropPos(){QVector<QPoint> allPos;for (int i = 0; i < BoardWidget::BOARD_WIDTH; i++){for (int j = 0; j < BoardWidget::BOARD_HEIGHT; j++){if (board[i][j] == BoardWidget::NO_PIECE){allPos.push_back(QPoint(i, j));}}}return allPos;}int GomokuAi::getPieceScore(int x, int y, int player){int value = 0;int piece = (player == BoardWidget::WHITE_PLAYER) ? BoardWidget::WHITE_PIECE : BoardWidget::BLACK_PIECE;for (int i = 0; i < 8; i++){int t = getChessType(x, y, i, piece);value += CHESS_VALUES[t];}value += getPosValue(x, y);return value;}int GomokuAi::getPosValue(int x, int y){int valuex =BoardWidget::BOARD_WIDTH - std::abs(x - BoardWidget::BOARD_WIDTH / 2);int valuey =BoardWidget::BOARD_HEIGHT - std::abs(y - BoardWidget::BOARD_HEIGHT / 2);return valuex + valuey;}int GomokuAi::getChessType(int x, int y, int dire, int piece){int chessType = 0;int end = BoardWidget::NO_PIECE;int num = getLinePieceNum(x, y, dire, piece, end);if (num == 5){chessType = 8;}else if (num == 4 && end == BoardWidget::NO_PIECE){chessType = 7;}else if (num == 4 && end != BoardWidget::NO_PIECE){chessType = 6;}else if (num == 3 && end == BoardWidget::NO_PIECE){chessType = 5;}else if (num == 3 && end != BoardWidget::NO_PIECE){chessType = 4;}else if (num == 2 && end == BoardWidget::NO_PIECE){chessType = 3;}else if (num == 2 && end != BoardWidget::NO_PIECE){chessType = 2;}else if (num == 1 && end == BoardWidget::NO_PIECE){chessType = 1;}else{chessType = 0;}return chessType;}int GomokuAi::getLinePieceNum(int x, int y, int dire, int pieceColo, int &pieceEnd){int offset[8][2] = {{1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}};int num = 0;x += offset[dire][0];y += offset[dire][1];if (isBeyond(x, y) || board[x][y] != pieceColo){return 0;}int pieceStart = board[x][y];while (!isBeyond(x, y) && board[x][y] == pieceStart){x += offset[dire][0];y += offset[dire][1];num++;}pieceEnd = board[x][y];//终止处的棋子return num;}bool GomokuAi::isBeyond(int x, int y){return x < 0 || x >= BoardWidget::BOARD_WIDTH || y < 0 || y >= BoardWidget::BOARD_HEIGHT;}QPoint GomokuAi::searchAGoodPos(Board nBoard){memcpy(board, nBoard, sizeof(board));int bestScore = MIN_VALUE;for (QPoint pos : getAllDropPos()){int value = getPieceScore(pos.x(), pos.y(), AI_PLAYER);int oppoValue = getPieceScore(pos.x(), pos.y(), !AI_PLAYER);int totalValue = std::max(value, oppoValue * 4 / 5);if (totalValue > bestScore){bestScore = totalValue;bestPos = pos;}}return bestPos;}

欢迎学习交流~

源码下载:/download/weixin_45737857/12060550

如果觉得《C++ Qt实现经典五子棋小游戏(双人游戏 人机大战)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。