作业:五子棋游戏开发

项目要求: 使用图像化界面完成五子棋 (人人对战): 可以使用EasyX框架(简单)或者Qt(复杂一点)

功能要求:

  • 下棋功能
  • 判断输赢功能
  • 能够自定义棋盘的大小
  • 能够三个人同时下棋

程序要求:

  • 尽量使用STL库,如数组可以使用vector去替代。
  • 体现面向对象的思想。

github路径:xuanmiao363/QtProject: Qt项目QtProject/五子棋

1、绘制棋盘

(1) 创建空白主窗口

Application --->Qt Widget Application ---> 设置项目名称和路径 ---> 基类选择QMainwindow

  • 如果你需要开发一个具有图形界面的应用程序,与用户进行交互,那么 Qt Widget Application 是更好的选择。
  • 如果你需要开发一个轻量级的命令行工具,或者后台服务程序,那么 Qt Console Application 更适合。

 

(2) 创建棋盘类

 

1、新建棋盘类:chessboard.h 、 chessboard.cpp

2、棋盘类中添加绘画事件成员函数,在cpp文件中实现绘画事件函数

创建画家对象,通过画家抗锯齿、设置棋盘背景颜色、画棋盘线

protected:
void paintEvent(QPaintEvent *event) override;
void ChessBoard::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    /* 创建画家对象 */
    QPainter painter(this); //此处需要#include <QPainter>

    /* 抗锯齿 */
    painter.setRenderHint(QPainter::Antialiasing);

    /* 设置棋盘颜色 */
    QColor background = QColor(205, 175, 149);
    painter.fillRect(rect(), background);

    /* 划棋盘线,常量boardSize、margin和cellSize定义在ChessBoard类中*/
    for(int iLoop=0; iLoop<boardSize; iLoop++)
    {
        painter.drawLine(margin, margin + iLoop*cellSize,
                         margin+(boardSize-1)*cellSize, margin +iLoop*cellSize);

        painter.drawLine(margin + iLoop*cellSize, margin,
                         margin+iLoop*cellSize, margin + (boardSize-1)*cellSize);
    }
}

3、在mainwindow类的构造函数中实例化棋盘对象、设置棋盘布局,窗口名称和大小

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /* 设置mainwindow窗口大小和名称*/
    setFixedSize(800,660);
    setWindowTitle("五子棋");

    /* 棋盘对象 */
    chessBoard = new ChessBoard(this);

    /*设置棋盘布局*/
    QWidget *centerWidget = new QWidget();
    setCentralWidget(centerWidget);
    QBoxLayout *layout = new QVBoxLayout(centerWidget);
    layout->addWidget(chessBoard);
}

 

2、绘制悬停点

(1) 初步绘制悬停点

/* 鼠标移动事件函数 */
void ChessBoard::mouseMoveEvent(QMouseEvent *event)
{
    /* 计算格子交叉点位置,hoverRow和hoverCol定义在chessboard.h文件中 */
    hoverRow = (event->x() - margin) / cellSize;
    hoverCol = (event->y() - margin) / cellSize;

    /* 手动触发绘画事件 */
    update();
}

在棋盘类构造函数中设置鼠标追踪为true

ChessBoard::ChessBoard(QWidget *parent) : QWidget(parent)
{
    /* 设置鼠标追踪为true */
    setMouseTracking(true);
}
void ChessBoard::paintEvent(QPaintEvent *event)
{
    xxx
    xxx
    xxx
        
    /* 绘制悬停点 */
    painter.setBrush(Qt::blue);
    painter.drawEllipse(QPoint(margin + hoverRow*cellSize, margin + hoverCol*cellSize),
                        cellSize/5, cellSize/5);
}

 

(2) 改进绘制悬停点

void ChessBoard::mouseMoveEvent(QMouseEvent *event)
{
    /* 计算格子交叉点位置 */
    if(event->x() > margin - cellSize/2 &&
       event->y() > margin - cellSize/2 )
    {
        // hoverRow,hoverCol,hoverFlag在棋盘类中定义
        hoverRow = (event->x() - margin + cellSize/2) / cellSize;
        hoverCol = (event->y() - margin+ cellSize/2) / cellSize;
    }
    else
    {
        hoverRow = -1;
        hoverCol = -1;
    }

    /* 手动触发绘画事件 */
    if( ((0<=hoverRow) && (hoverRow < boardSize)) &&
        ((0<=hoverCol) && (hoverCol < boardSize)))
    {
        hoverFlag = true;
        update();
    }
    else
    {
        hoverFlag = false;
        update();
    }
}
void ChessBoard::paintEvent(QPaintEvent *event)
{
     xxx
     xxx
     xxx

    /* 绘制悬停点 */
    if( ((0<=hoverRow) && (hoverRow < boardSize)) &&
        ((0<=hoverCol) && (hoverCol < boardSize)) &&
        (hoverFlag == true))
    {
        painter.setBrush(Qt::black);
        painter.drawEllipse(QPoint(margin + hoverRow*cellSize, margin + hoverCol*cellSize),
                            cellSize/4, cellSize/4);
    }
}

 

3、选择玩家

(1)新建玩家类:player.h 、 player.cpp

(2)player.h在类中声明PushButton对象,并增加对应的槽函数

class Player : public QWidget
{
    Q_OBJECT

public:
    explicit Player(QWidget *parent = nullptr);

private:
    QPushButton *btn1;
    QPushButton *btn2;

signals:

/* 按钮按下的槽函数 */
public slots:
    void btn1Clicked();
    void btn2Clicked();
};

(3)player.cpp在构造函数中设置黑棋和白棋按钮,并且设置信号槽函数连接

#include "player.h"
#include <QDebug>

Player::Player(QWidget *parent) : QWidget(parent)
{
    /* 设置黑棋按钮 */
    btn1 = new QPushButton;
    btn1->setParent(parent);
    btn1->setText("黑棋");
    QPalette palette1 = btn1->palette();
    palette1.setColor(QPalette::ButtonText, QColor(255, 255, 255));
    palette1.setColor(QPalette::Button, QColor(0, 0, 0));
    btn1->setPalette(palette1);
    btn1->move(350, 20);
    btn1->resize(100,50);
    btn1->setEnabled(true);

    /* 设置白棋按钮 */
    btn2 = new QPushButton;
    btn2->setParent(parent);
    btn2->setText("白棋");
    QPalette palette2 = btn2->palette();
    palette2.setColor(QPalette::ButtonText, QColor(0, 0, 0));
    palette2.setColor(QPalette::Button, QColor(255, 255, 255));
    btn2->setPalette(palette2);
    btn2->move(350, 700);
    btn2->resize(100,50);
    btn2->setEnabled(true);

    // 连接按钮的点击信号到自定义信号
    connect(btn1, &QPushButton::clicked, this, &Player::btn1Clicked);
    connect(btn2, &QPushButton::clicked, this, &Player::btn2Clicked);
}

 在chessboard.cpp文件中设置pushbutton的槽函数,设置FlagBlack和FlagWhite标记

/* PushButton的槽函数 */
void Player::btn1Clicked()
{
    FlagBlack = 1;
    FlagWhite = 0;
    update();
}

void Player::btn2Clicked()
{
    FlagBlack = 0;
    FlagWhite = 1;
    update();
}

 

4、绘制棋子

(1)  棋盘构造函数中初始化棋子

/* 构造函数 */
ChessBoard::ChessBoard(QWidget *parent) : QWidget(parent)
{
    /* 设置鼠标追踪为true */
    setMouseTracking(true);

    /* 初始化棋子 */
    for(int iLoop=0; iLoop<boardSize; iLoop++ )
    {
        for(int jLoop=0; jLoop<boardSize; jLoop++)
        {
            boardArray[iLoop][jLoop] = EMPTY;
        }
    }
}

(2)在chessboard.h文件中增加鼠标点击事件成员函数,在chessboard.cpp文件中实现

protected:
    void mousePressEvent(QMouseEvent *event) override;
/* 鼠标点击事件函数 */
void ChessBoard::mousePressEvent(QMouseEvent *event)
{
    hoverRow = (event->x() - margin + cellSize/2) / cellSize; //计算当前下棋位置
    hoverCol = (event->y() - margin+ cellSize/2) / cellSize; //计算当前下棋位置

    qDebug() << "FlagBlack=" << FlagBlack <<endl; // FlagBlack是表示下黑棋
    qDebug() << "FlagWhite=" << FlagWhite <<endl; // FlagWhite是表示下白棋

    if( ((0<=hoverRow) && (hoverRow < boardSize)) &&
        ((0<=hoverCol) && (hoverCol < boardSize)) &&
        (FlagBlack == 1))
    {
        if(boardArray[hoverRow][hoverCol] != EMPTY)
        {
            QMessageBox::information(this, "错误", "此处已经有棋子了");
            return;
        }

        boardArray[hoverRow][hoverCol] = BLACKSIDE;
        update();
    }
    else if(((0<=hoverRow) && (hoverRow < boardSize)) &&
            ((0<=hoverCol) && (hoverCol < boardSize)) &&
            (FlagWhite == 1))
    {
        if(boardArray[hoverRow][hoverCol] != EMPTY)
        {
            QMessageBox::information(this, "错误", "此处已经有棋子了");
            return;
        }

        boardArray[hoverRow][hoverCol] = WHITESIDE;
        update();
    }
}
/* 棋盘绘画事件 */
void ChessBoard::paintEvent(QPaintEvent *event)
{
    xxx
    xxx
    xxx
        
    /* 绘制棋子 */
    for(int iLoop=0; iLoop<boardSize; iLoop++ )
    {
        for(int jLoop=0; jLoop<boardSize; jLoop++ )
        {
            if (boardArray[iLoop][jLoop] == WHITESIDE)
            {
                painter.setBrush(Qt::white);
                painter.drawEllipse(QPoint(margin + iLoop*cellSize, margin + jLoop*cellSize),
                                    cellSize/3, cellSize/3);
            }
            else if(boardArray[iLoop][jLoop] == BLACKSIDE)
            {
                painter.setBrush(Qt::black);
                painter.drawEllipse(QPoint(margin + iLoop*cellSize, margin + jLoop*cellSize),
                                    cellSize/3, cellSize/3);
            }
        }
    }
}

 

5、判定胜负

(1)在chessboard.h的ChessBoard类中增加判定胜负的成员函数

class ChessBoard : public QWidget
{
    xxx
    xxx
    xxx
    
private:
    xxx
    xxx
    xxx
    bool bIsCheckBlackWin(int row, int col);
    bool bIsCheckWhiteWin(int row, int col);

signals:
};

(2)实现判定胜负函数

/* 检测白方棋子是否获胜 */
bool ChessBoard::bIsCheckWhiteWin(int row, int col)
{
    for(int i=1; i<5; i++)
    {
        if( boardArray[row+i][col] == WHITESIDE)
        {
            qDebug()<<"右侧棋子是白色的"<< endl;
            WHITECNTRIGHT++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row-i][col] == WHITESIDE)
        {
            qDebug()<<"左侧棋子是白色的"<< endl;
            WHITECNTLEFT++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row][col+i] == WHITESIDE)
        {
            qDebug()<<"上侧棋子是白色的"<< endl;
            WHITECNTUP++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row][col-i] == WHITESIDE)
        {
            qDebug()<<"下侧棋子是白色的"<< endl;
            WHITECNTDOWN++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row+i][col+i] == WHITESIDE)
        {
            qDebug()<<"右上侧棋子是白色的"<< endl;
            WHITECNTRIGHTUP++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row-i][col-i] == WHITESIDE)
        {
            qDebug()<<"左下侧棋子是白色的"<< endl;
            WHITECNTLEFTDOWN++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row+i][col-i] == WHITESIDE)
        {
            qDebug()<<"右下侧棋子是白色的"<< endl;
            WHITECNTRIGHTDOWN++;
        }
        else
        {
            break;
        }
    }

    for(int i=1; i<5; i++)
    {
        if( boardArray[row-i][col+i] == WHITESIDE)
        {
            qDebug()<<"左上侧棋子是白色的"<< endl;
            WHITECNTLEFTUP++;
        }
        else
        {
            break;
        }
    }

    qDebug()<<"右侧棋子个数"<< WHITECNTRIGHT << endl;
    qDebug()<<"左侧棋子个数"<< WHITECNTLEFT << endl;
    qDebug()<<"上侧棋子个数"<< WHITECNTUP << endl;
    qDebug()<<"下侧棋子个数"<< WHITECNTDOWN << endl;
    qDebug()<<"右下侧棋子个数"<< WHITECNTRIGHTUP << endl;
    qDebug()<<"左上侧棋子个数"<< WHITECNTLEFTDOWN << endl;
    qDebug()<<"左下侧棋子个数"<< WHITECNTLEFTUP << endl;
    qDebug()<<"右上侧棋子个数"<< WHITECNTRIGHTDOWN << endl;

    if( (WHITECNTRIGHT + WHITECNTLEFT + 1 >= 5) ||
        (WHITECNTUP + WHITECNTDOWN + 1 >= 5) ||
        (WHITECNTRIGHTUP + WHITECNTLEFTDOWN + 1 >= 5) ||
        (WHITECNTLEFTUP + WHITECNTRIGHTDOWN + 1 >= 5))
    {
        ret = true;
    }
    else
    {
        ret = false;
    }

    WHITECNTRIGHT = 0;
    WHITECNTLEFT = 0;
    WHITECNTUP = 0;
    WHITECNTDOWN = 0;
    WHITECNTRIGHTUP = 0;
    WHITECNTLEFTDOWN = 0;
    WHITECNTRIGHTDOWN = 0;
    WHITECNTLEFTUP = 0;

    return ret;
}

 

实现效果:

posted on 2025-04-14 22:12  轩~邈  阅读(48)  评论(0)    收藏  举报