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)