Using Qt to achieve minesweeping

github source code: Qt minesweeper source code

Minesweeper is a small game that many people have played, including me. Unfortunately, win10 does not have many classic games that came with XP before, so I use Qt to implement minesweeping, and want to learn more about Qt's related mechanisms.

Preview effect:
Insert picture description here
Insert picture description here

1. The basic components of the game

Minefields are small squares. It is easy to think that these are all primitives . The primitives have many parameters to indicate their state (whether they are mined, marked, etc.), and the mouse clicks on a particular primitive to trigger The mouse function of this primitive, thereby changing its state.

The picture material comes from someone else’s blog, I used it directly:
Insert picture description here

myItem (graphic element class)

The primitive class myItems is inherited from QObject (signal and slot mechanism) and QGraphicsPixmapItem (converted to pixel primitives). Set the scene by placing the primitives in the scene QGraphicsScene, and then set the ui component QGraphicsView->setScene, so that each of the primitives can be displayed.

class myItem : public QObject,public QGraphicsPixmapItem
{
    
    
    Q_OBJECT
//省略其他函数和信号
protected:
     void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
private:  //参数
     int row;
     int col;
     bool sweeped=false;//是否已经挖掘
     bool mine=false;//有无地雷
     bool mark=false;//有无旗子标记
     bool stepMine=false;//是否踩雷
     int numOfMines=0;//周围地雷个数
};

The myItem class must contain row and col, because once a grid with no mines and the number of surrounding mines is 0, the surrounding map will be automatically opened and a signal needs to be sent. This signal must contain the location information of the grid being dug.

Rewrite the mouse click function mousePressEvent : (I wrote the mouse release function mouseReleaseEvent() at the beginning, but this function does not respond to the situation of right and left and right double clicks, so I gave up)

void myItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
    
    
    if(event->buttons()==(Qt::LeftButton|Qt::RightButton)){
    
    //左右键同时按下
        if((sweeped&&numOfMines!=0)||(!sweeped&&mark)){
    
    //已被挖掘且有数字or未挖掘被标记
            emit doubleClickSignal(row,col);//让scene检查并打开周围的空格,调用myScene.findBlock()
        }
    }else if(event->button()==Qt::LeftButton){
    
    //左键点击
        simulateLeftClick();//模拟左键点击动作
    }else if(event->button()==Qt::RightButton){
    
    
        if(!sweeped){
    
    
            setMark(!mark);
            emit markChangedSignal();//改变数字显示器显示的剩余地雷数
        }
    }
    emit checkSignal();//检测游戏是否成功
}

myScene (scene class)

myScene contains a two-dimensional array of pointers to objects of the myItem class.

class myScene : public QObject, public QGraphicsScene
{
    
    
//省略其他函数和信号
private:  //参数
    static const int MAX_HEIGHT=24;
    static const int MAX_WIDTH=30;

    myItem *items[MAX_WIDTH][MAX_HEIGHT];

    int mine=10;//地雷数
    int height=9;//地图宽高
    int width=9;
};

Note : Every time you initialize, you need to re-allocate space for the items pointer array after calling this->clear(), because clear() will automatically delete the original space.

void myScene::initialize(){
    
    
    this->clear();
    for(int i=0;i<MAX_WIDTH;++i){
    
    //状态初始化
        for(int j=0;j<MAX_HEIGHT;++j){
    
    
             this->items[i][j]=new myItem();
             //省略其他语句
        }
    }
    for(int i=0;i<width;++i){
    
    //显示大小
        for(int j=0;j<height;++j){
    
    
            this->addItem(this->items[i][j]);
        }
    }

    int nx,ny,numOfMines;
    int dx[8]={
    
    -1,-1,-1,0,0,1,1,1};
    int dy[8]={
    
    -1,0,1,-1,1,-1,0,1};
    for(int i=0;i<width;++i){
    
    //设置周围砖块数
        for(int j=0;j<height;++j){
    
    
            numOfMines=0;
            for(int k=0;k<8;++k){
    
    
                nx=i+dx[k];
                ny=j+dy[k];
                if(0<=nx&&nx<width&&0<=ny&&ny<height){
    
    
                    if(this->items[nx][ny]->isMine())numOfMines++;
                }
            }
            this->items[i][j]->setNumOfMines(numOfMines);
        }
    }
    //省略其他语句
}

findBlock() function

The core thing in the whole minesweeper is to automatically open the surrounding grid findBlock (). When left-clicking on a grid that has no mines and the surrounding mine count is not 0, it only opens itself. When you left-click a grid that has no mines and the number of surrounding mines is 0, all the grids around this grid will be automatically opened (simulating a left-click). Then it is easy to implement when using recursion.

void myScene::findBlocks(int row,int col){
    
    
    items[row][col]->setSweeped(true);//模拟点击
    if(items[row][col]->getNumOfMines()!=0)return;//有数字的方块就不展开了

    int nx,ny;
    int dx[8]={
    
    -1,-1,-1,0,0,1,1,1};
    int dy[8]={
    
    -1,0,1,-1,1,-1,0,1};
    for(int i=0;i<8;++i){
    
    
        nx=row+dx[i];
        ny=col+dy[i];
        if(0<=nx&&nx<width&&0<=ny&&ny<height&&!items[nx][ny]->isSweeped()){
    
    //下标合法并且还未被挖
            findBlocks(nx,ny);
        }
    }
}

Two, some interface details

1. How to make the size of the scene scene and the display of the view view roughly the same?

The size of the scene depends on the size of the added primitives, such as adding myScene->addItem(items[0][0), setting the primitive position items[0)[0)->setPos(0,0), for
each picture The meta size is 20x20, so the size of myScene is 20x20. When the size of the scene does not exceed the view, the default setting is to overlap the center of the scene with the center of the view (the center is where the two diagonals of the rectangle overlap), then set the size of the view a little larger than the scene , for example, set it to 23x23 will do.

2. How to remove the scroll box when the view appears?

    this->ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//去掉横向滚动框
    this->ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//去掉竖向滚动框

3. It is not recommended to use the layout of the main window interface, because it is not suitable for management .

It is very convenient to set the position with setGeometry, and then set the fixed size with setFixedSize.

4. Primary, intermediate, advanced, and custom map settings are written in one function .

Write a custom function customSet (int width, int height, int mine), you can easily call.

void MainWindow::on_actionPrimary_triggered()
{
    
    
    customSet(9,9,10);
}
void MainWindow::on_actionMid_triggered()
{
    
    
    customSet(16,16,40);
}
void MainWindow::on_actionSenior_triggered()
{
    
    
    customSet(30,16,99);
}

5. Two digital displays (displaying 3 digits) can also use the same class numScene

Write a numItem primitive class first, and then call this class.

void numItem::setNum(int a){
    
    
    if(a==0)this->setPixmap(QPixmap(":/num/pic/num0.png"));
    else if(a==1)this->setPixmap(QPixmap(":/num/pic/num1.png"));
    else if(a==2)this->setPixmap(QPixmap(":/num/pic/num2.png"));
    else if(a==3)this->setPixmap(QPixmap(":/num/pic/num3.png"));
    else if(a==4)this->setPixmap(QPixmap(":/num/pic/num4.png"));
    else if(a==5)this->setPixmap(QPixmap(":/num/pic/num5.png"));
    else if(a==6)this->setPixmap(QPixmap(":/num/pic/num6.png"));
    else if(a==7)this->setPixmap(QPixmap(":/num/pic/num7.png"));
    else if(a==8)this->setPixmap(QPixmap(":/num/pic/num8.png"));
    else if(a==9)this->setPixmap(QPixmap(":/num/pic/num9.png"));
}

void numScene::setNumer(int num){
    
    
    if(num>=0){
    
    
        this->clear();
        
        int hundred,ten,one;//百位,十位,个位
        hundred=num/100;
        ten=(num%100)/10;
        one=num%10;

        numItem *hunItem,*tenItem,*oneItem;
        hunItem=new numItem;
        tenItem=new numItem;
        oneItem=new numItem;
        hunItem->setPos(1,0);
        tenItem->setPos(20,0);
        oneItem->setPos(39,0);
        hunItem->setNum(hundred);
        tenItem->setNum(ten);
        oneItem->setNum(one);

        this->addItem(hunItem);
        this->addItem(tenItem);
        this->addItem(oneItem);
    }
}

end

Through a very classic game-Minesweeper, my knowledge of Qt has been further improved.

Guess you like

Origin blog.csdn.net/livingsu/article/details/104774193