Production of Qt Chinese chess

Keywords: Qt

preparation

  • Familiar with Chinese chess rules
  • Find a group of pictures of Chinese chess board and pieces (if you want to write your own board and pieces)
  • Understand some basic usage of drawing event, timer event, mouse event, etc

Function realization

1,widget.h

Add the functions and variables that will be used later in the header file

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

//enumeration
enum ChessType
{
    NullChess,

    Ju_R,
    Ma_R,
    Xiang_R,
    Shi_R,
    Jiang_R,
    Pao_R,
    Bing_R,

    Ju_B,
    Ma_B,
    Xiang_B,
    Shi_B,
    Jiang_B,
    Pao_B,
    Bing_B,
};

struct Path
{
    int row1,row2,col1,col2;
    ChessType start,end;
};

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void paintEvent(QPaintEvent *event)void mousePressEvent(QMouseEvent *event);
    void timerEvent(QTimerEvent *event);

    void showtime();
    
    void InitChess(); //Initialize chessboard
    void DrawChess(QPainter *paint,int row,int col,ChessType chess);  //Drawing chessboard
    bool PlayChess(ChessType move,int row,int col,ChessType kill); //Set chessboard rules
    bool line(int row,int col); //Judge whether there are other pieces between two pieces
    int Type(ChessType chess)bool PlayMa(int row,int col);
    bool PlayJu(int row,int col);
    bool PlayPao(int row,int col);
    bool PlayShi(ChessType move ,int row,int col);
    bool PlayBing(ChessType move,int row,int col)bool PlayXiang(ChessType move,int row,int col);
    bool PlayJiang(ChessType move,int row,int col);

private:
    Ui::Widget *ui;

    int t;

    int mouserow,mousecol;

    int Jiang_row=9,Jiang_col=4;
    int Shuai_row=0,Shuai_col=4;

    int W,H;   //Width and height

    QPoint stPos,edPos;  //Top left and bottom right
    
    int clect=-1; //When no pieces are selected
    int go=0;//Go chess (at the beginning of the game, the red party goes first, go=0: the red party goes; go=1: the black party goes; go=-1: neither can go; go=-2: the end of the game)
    int jilu=0;//Record which side is playing chess

    int time;
    QString texttime;

    ChessType keep=NullChess;
    
    ChessType chessType[10][9];

    QFont font=QFont("Tahoma",30,75);

    QVector<Path>v;
};
#endif // WIDGET_H
2,widget.cpp

(1) , some header files and initialization of some variables

#include "ui_widget.h"
#include<QPainter>
#include<QDebug>
#include<QMouseEvent>
#include<QVector>
#define dll 30

#pragma execution_character_set("utf-8")

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

    ui->label->setFont(font);
    ui->label_2->setFont(font);

    stPos=QPoint(80,55);  //Coordinates of the upper left point
    edPos=QPoint(717,944);//Coordinates of the lower right point

    W=(edPos.x()-stPos.x())/8;  //Grid width
    H=(edPos.y()-stPos.x())/9;  //Lattice length

    InitChess();  //Import chess pieces
    
    mouserow=-1;  //Initialize mouse click position
    mousecol=-1;

    time=dll;
    showtime();
    startTimer(1000);
}

Widget::~Widget()
{
    delete ui;
}

(2) Initialize the chessboard array, and import the chessboard into the corresponding array position (that is, the position where the chessboard begins)

void Widget::InitChess()
{
    for(int i=0;i<10;i++)
        for(int j=0;j<9;j++)
        {
            chessType[i][j]=NullChess;
        }
    
    chessType[0][0]=Ju_R;
    chessType[0][1]=Ma_R;
    chessType[0][2]=Xiang_R;
    chessType[0][3]=Shi_R;
    chessType[0][4]=Jiang_R;
    for(int i=0;i<4;i++)
        chessType[0][8-i]=chessType[0][i];

    chessType[9][0]=Ju_B;
    chessType[9][1]=Ma_B;
    chessType[9][2]=Xiang_B;
    chessType[9][3]=Shi_B;
    chessType[9][4]=Jiang_B;
    for(int i=0;i<4;i++)
        chessType[9][8-i]=chessType[9][i];

    chessType[2][1]=Pao_R;
    chessType[2][7]=Pao_R;
    chessType[7][1]=Pao_B;
    chessType[7][7]=Pao_B;

    chessType[3][0]=Bing_R;
    chessType[3][2]=Bing_R;
    chessType[3][4]=Bing_R;
    chessType[3][6]=Bing_R;
    chessType[3][8]=Bing_R;
    chessType[6][0]=Bing_B;
    chessType[6][2]=Bing_B;
    chessType[6][4]=Bing_B;
    chessType[6][6]=Bing_B;
    chessType[6][8]=Bing_B;
}

(3) Other elements needed for drawing board, board and game interface

  • Upload corresponding chess resources
void Widget::DrawChess(QPainter *paint,int row,int col,ChessType chess)
{
    QPixmap map;
    switch (chess)
    {
    case Ju_R:
        map=QPixmap(":/chess/res/rr.png");
        break;
    case Ma_R:
        map=QPixmap(":/chess/res/rn.png");
        break;
    case Xiang_R:
        map=QPixmap(":/chess/res/rb.png");
        break;
    case Shi_R:
        map=QPixmap(":/chess/res/ra.png");
        break;
    case Jiang_R:
        map=QPixmap(":/chess/res/rk.png");
        break;
    case Bing_R:
        map=QPixmap(":/chess/res/rp.png");
        break;
    case Pao_R:
        map=QPixmap(":/chess/res/rc.png");
        break;
    case Ju_B:
        map=QPixmap(":/chess/res/br.png");
        break;
    case Ma_B:
        map=QPixmap(":/chess/res/bn.png");
        break;
    case Xiang_B:
        map=QPixmap(":/chess/res/bb.png");
        break;
    case Shi_B:
        map=QPixmap(":/chess/res/ba.png");
        break;
    case Jiang_B:
        map=QPixmap(":/chess/res/bk.png");
        break;
    case Bing_B:
        map=QPixmap(":/chess/res/bp.png");
        break;
    case Pao_B:
        map=QPixmap(":/chess/res/bc.png");
        break;
    }
    QRect rect(stPos.x()+col*W-0.5*W,stPos.y()+row*H-0.5*H,W,H);
    paint->drawPixmap(rect,map);
}
  • Drawing chessboard, chessboard and other function keys
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter paint(this);

    //Upload background
    QRect rect=this->rect();
    rect.setWidth(rect.width()-200);  //Reserve some function keys for repentance
    paint.drawPixmap(rect,QPixmap(":/chess/res/background.png"));
    QPixmap map1(":/chess/res/floor.jpg");
    QRect q(800,0,200,rect.height());
    paint.drawPixmap(q,map1);
    QRect q1(843,80,114,56);
    paint.drawPixmap(q1,QPixmap(":/chess/res/start.jpg")); //Start key
    QRect q2(843,190,114,56);
    paint.drawPixmap(q2,QPixmap(":/chess/res/zhanting.jpg")); //Pause key
    QRect q3(843,300,114,56);
    paint.drawPixmap(q3,QPixmap(":/chess/res/regret.jpg")); //Repentance key
    QRect q4(843,410,114,56);
    paint.drawPixmap(q4,QPixmap(":/chess/res/new.jpg"));  //New local key

    //Draw a selection box (check pieces)
    if(mouserow>=0 && mouserow<=9 && mousecol>=0 && mousecol<=8 && clect==-1)
    {
        QRect rect(stPos.x()+mousecol*W-0.5*W,stPos.y()+mouserow*H-0.5*H,W,H);
        paint.drawPixmap(rect,QPixmap(":/chess/res/selected.png"));
    }

    //Draw chessboard
    for(int i=0;i<10;i++)
        for(int j=0;j<9;j++)
        {
            if(chessType[i][j]!=NullChess)
            {
                DrawChess(&paint,i,j,chessType[i][j]);
            }
        }


}

(4) Set chessboard rules

  • First, divide the chess pieces into red square and black square
int Widget::Type(ChessType chess)
{
    switch(chess)
    {
    case Ju_R:
        return 0;
    case Ma_R:
        return 0;
    case Xiang_R:
        return 0;
    case Shi_R:
        return 0;
    case Jiang_R:
        return 0;
    case Bing_R:
        return 0;
    case Pao_R:
        return 0;
    case Ju_B:
        return 1;
    case Ma_B:
        return 1;
    case Xiang_B:
        return 1;
    case Shi_B:
        return 1;
    case Jiang_B:
        return 1;
    case Bing_B:
        return 1;
    case Pao_B:
        return 1;
    case NullChess:
        return -1;
    }
}
  • Because there are too many rules of chess pieces, it will be very miscellaneous to write them in a function, so we write the rules of each kind of chess piece into a function, and then call them in a general function, so the code looks beautiful and easy to correct
bool Widget::PlayChess(ChessType move,int row,int col,ChessType kill) //row and col are the places where the mouse clicks, move is to select the moving pieces, kill is to select the pieces to be eaten or empty pieces
{
    //You can't eat your own
    if(Type(move)==Type(kill))
    {
        //The selection box jumps over another chess piece clicked by the mouse
        mouserow=row;
        mousecol=col;
        clect=-1;
        repaint();
        clect=1;
        
        return false;
    }
    else
    {
        switch(move)
        {
        case Ju_R:
            return PlayJu(row,col);
            break;
        case Ma_R:
            return PlayMa(row,col);
            break;
        case Xiang_R:
            return PlayXiang(move,row,col);
            break;
        case Shi_R:
            return PlayShi(move,row,col);
            break;
        case Jiang_R:
            return PlayJiang(move,row,col);
            break;
        case Bing_R:
            return PlayBing(move,row,col);
            break;
        case Pao_R:
            return PlayPao(row,col);
            break;
        case Ju_B:
            return PlayJu(row,col);
            break;
        case Ma_B:
            return PlayMa(row,col);
            break;
        case Xiang_B:
            return PlayXiang(move,row,col);
            break;
        case Shi_B:
            return PlayShi(move,row,col);
            break;
        case Jiang_B:
            return PlayJiang(move,row,col);
            break;
        case Bing_B:
            return PlayBing(move,row,col);
            break;
        case Pao_B:
            return PlayPao(row,col);
            break;
        }
    }


}
  • The code of rule part is too long, so only part of it will be shown here, and some will be uploaded to resources
//Car rules
bool Widget::PlayJu(int row,int col)
{
    if(mouserow!=row && mousecol!=col)
        return false;
   else
        return line(row,col);
}

//Judge whether there are other pieces between two pieces
bool Widget::line(int row,int col) //row and col are the rows and columns where the mouse clicks
{
    //Forward (all from the perspective of the red side)
    if(mousecol==col && mouserow<row)
    {
        for(int i=mouserow+1;i<row;i++)
        {
            if(chessType[i][mousecol]!=NullChess)
                return false;
        }
        return true;
    }

    //back off
    if(mousecol==col && mouserow>row)
    {
        for(int i=row+1;i<mouserow;i++)
        {
            if(chessType[i][mousecol]!=NullChess)
                return false;
        }
        return true;
    }

    //towards the left
    if(mouserow==row && mousecol>col)
    {
        for(int i=col+1;i<mousecol;i++)
        {
            if(chessType[mouserow][i]!=NullChess)
                return false;
        }
        return true;
    }

    //towards the right
    if(mouserow==row && mousecol<col)
    {
        for(int i=mousecol+1;i<col;i++)
        {
            if(chessType[mouserow][i]!=NullChess)
                return false;
        }
        return true;
    }
    else return false;
}

//Gun rules
bool Widget::PlayPao(int row,int col)
{
    if(mouserow!=row && mousecol!=col)
        return false;

    if(chessType[row][col]==NullChess)//Don't eat, just move
        return line(row,col);
    else if(chessType[row][col]!=NullChess)//To eat
    {
        if(mousecol==col && mouserow<row)
        {
            int b=0;
            for(int i=mouserow+1;i<row;i++)
            {
                if(chessType[i][mousecol]!=NullChess)
                    ++b;
            }
            if(b==1)
                return true;
            else return false;
        }

        //back off
        if(mousecol==col && mouserow>row)
        {
            int b=0;
            for(int i=row+1;i<mouserow;i++)
            {
                if(chessType[i][mousecol]!=NullChess)
                    ++b;
            }
            if(b==1)
                return true;
            else return false;
        }

        //towards the left
        if(mouserow==row && mousecol>col)
        {
            int b=0;
            for(int i=col+1;i<mousecol;i++)
            {
                if(chessType[mouserow][i]!=NullChess)
                    ++b;
            }
            if(b==1)
                return true;
            else return false;
        }

        //towards the right
        if(mouserow==row && mousecol<col)
        {
            int b=0;
            for(int i=mousecol+1;i<col;i++)
            {
                if(chessType[mouserow][i]!=NullChess)
                    ++b;
            }
            if(b==1)
                return true;
            else return false;
        }

        else return false;
    }

}

(5) Mouse events

  • Using mouse events to play chess
  • Judge win or lose:
    1. If one side makes the general in a straight line after playing chess, and there is no chess in the middle, the other side loses
    2. If you're going to be eaten, you're going to lose
void Widget::mousePressEvent(QMouseEvent *event)
{


   qDebug() <<event->pos();  //Get pixel position of mouse click


   if(event->button()==Qt::LeftButton)
   {


       //Convert pixel coordinates to corresponding rows and columns
       int row =(event->y()-stPos.y()+0.5*H)/H;
       int col =(event->x()-stPos.x()+0.5*W)/W;


       //Click Start
       if(event->x()>=843 && event->x()<=957 && event->y()>=80 && event->y()<=136 && go!=-2)
       {
           go=jilu;
           ui->label_2->setText("Game start");
           if(go==0)
               ui->label->setText("Red Square, please go");
           else if(go==1)
               ui->label->setText("Black side, please go");
       }
       //Click pause
       if(event->x()>=843 && event->x()<=957 && event->y()>=190 && event->y()<=246)
       {
           go=-1;
           ui->label_2->setText("Game pause");
           ui->label->setText(" ");
       }
       //Click regret
       if(event->x()>=843 && event->x()<=957 && event->y()>=300 && event->y()<=356)
       {
           int step=v.size();
           if(step!=0&&(go==0||go==1))
           {
               QVector<Path>::iterator it=v.begin()+step-1;
               chessType[it->row1][it->col1]=it->end;
               chessType[it->row2][it->col2]=it->start;
               mouserow=it->row1;
               mousecol=it->col1;
               v.pop_back();
               if(go==0)
               {
                   ui->label->setText("Black side, please go");
                   go=1;
               }
               else if(go==1)
               {
                   ui->label->setText("Red Square, please go");
                   go=0;
               }
               repaint();
               time=dll;
               jilu=go;
           }


       }


       //Click the new button
       if(event->x()>=843 && event->x()<=957 && event->y()>=410 && event->y()<=466)
       {
           InitChess();
           mouserow=-1;
           mousecol=-1;
           repaint();
           go=0;
           v.clear();
           ui->label_2->setText("Game start");
           ui->label->setText("Red Square, please go");
           time=dll;
           showtime();
       }


       if(row>=0&&row<=9&&col>=0&&col<=8 && go!=-1)
       {
           //Choose chess pieces
           if(clect==-1)
           {
                if(chessType[row][col]!=NullChess && Type(chessType[row][col])==go)
                {
                    mouserow=row;
                    mousecol=col;


                    repaint();


                    clect=1;


                    if(go==0)
                        ui->label->setText("Red Square, please go");
                    else if(go==1)
                        ui->label->setText("Black side, please go");
                }
           }


           //Move chess pieces
           else if(PlayChess(chessType[mouserow][mousecol],row,col,chessType[row][col]))
           {
               Path path;
               path.row1=mouserow;
               path.col1=mousecol;
               path.row2=row;
               path.col2=col;
               path.start=chessType[row][col];
               path.end=chessType[mouserow][mousecol];
               v.push_back(path);

               if(chessType[row][col]==NullChess)
               {
                   keep=chessType[row][col];
                   chessType[row][col]=chessType[mouserow][mousecol];
                   chessType[mouserow][mousecol]=NullChess;
               }

              //If there is a chess piece at the position of mouse click
               else
               {
                   keep=chessType[row][col];
                   chessType[row][col]=NullChess;
                   chessType[row][col]=chessType[mouserow][mousecol];
                   chessType[mouserow][mousecol]=NullChess;
               }
;              repaint();
               clect=-1;

               if(chessType[row][col]==Jiang_R)
               {
                   Shuai_row=row;
                   Shuai_col=col;
               }
               else if(chessType[row][col]==Jiang_B)
               {
                   Jiang_row=row;
                   Jiang_col=col;
               }

               //Judge win or lose
               if(keep==Jiang_R)
               {
                   go=-2;
                   ui->label->setText("Black side wins");
                   ui->label_2->setText("game over");
               }
               else if(keep==Jiang_B)
               {
                   go=-2;
                   ui->label->setText("Red side wins");
                   ui->label_2->setText("game over");
               }
               else if(Shuai_col==Jiang_col)
               {
                   int b=0;
                   for(int i=Shuai_row+1;i<Jiang_row;i++)
                           if(chessType[i][Shuai_col]!=NullChess)
                               b++;
                   if(b==0)
                   {
                       if(go==0)
                       {
                           go=-2;
                           ui->label->setText("Black side wins");
                           ui->label_2->setText("game over");
                       }
                       else if(go==1)
                       {
                           go=-2;
                           ui->label->setText("Red side wins");
                           ui->label_2->setText("game over");
                       }
                   }
               }

               //Change party holder
               if(go==0)
               {
                   go=1;
                   ui->label->setText("Black side, please go");
               }
               else if(go==1)
               {
                   go=0;
                   ui->label->setText("Red Square, please go");
               }
               jilu=go;
               time=dll;
           }
       }

   }

}

(6) Painting countdown

  • If one side is input, the timer will return to zero
  • After the countdown is 0, you can continue to play chess
void Widget::showtime()
{
   texttime=QString::number(time,10);
   ui->lcdNumber->display(texttime);
}

//Set countdown
void Widget::timerEvent(QTimerEvent *event)
{
   if((go==0||go==1)&&time>=1)
   {
       time--;
       showtime();
   }
   else if(go==-2)
   {
       time=0;
       showtime();
   }
}
  • Use LCD number in ui interface (the following are some relevant parameters)

Posted by redking on Mon, 08 Jun 2020 21:32:39 -0700