QT:完整的人機五子棋設計(三)運行


QT Creator5.9.9

2.3游戲進行的過程

整個游戲博弈過程用定義的狀態來判定運行流程。enum RunState {NO_RUNNING, START, RUNNING, GAME_OVER,RESTART,EXIT};

2.3.1整體流程

游戲運行整體就是一個互奕的過程,當我們執行開始時,就進入開始狀態中循環互奕,互奕時當重新開始或判定勝負,就會退出互奕過程。當重新開始,又會進入互奕,此時開始按鈕會被屏蔽。需要添加一個短期的延時,不然會一直占用資源,造成窗口崩潰。

image

2.3.2互奕過程

通過一個當前回合變量改變回合,電腦下棋使用AI(初步隨機下棋),下棋前判斷對方是否提出悔棋請求,若有請求,需要對上一回合做回退處理;下完棋后判斷是否五連;如果該回合未結束,則修改回合變量,更換回合;人下棋使用鼠標點擊,回合到來進入等待循環,鼠標按下后,根據自定義條件跳出循環,然后根據當前回合棋子類型判斷你是否五連結束游戲;如果該回合未結束,則修改回合變量,更換回合。

下棋時,棋子的顯示,都是通過修改二維數組中的棋盤信息,然后根據信息刷新來顯示的。

鼠標按下的操作,請看前面的隨筆,棋盤的制作過程,其主要是通過鼠標事件來監控。

要想驗證悔棋過程,還需要在AI下棋前增加一個延時。

image

2.3.3按鍵功能實現

點擊ui文件進入設計模式,在要實現功能的按鍵上右鍵=>轉到槽……,選擇如下圖所示的clicked()點擊事件,確認后在代碼中會自動創建一個槽函數接口,當鼠標點擊按鈕時,就會運行該函數。

  1 void FiveChess::on_startBtn_clicked()
  2 {
  3 
  4 }

image

按鍵的屏蔽設置:

startBtn是自己修改的目標名image

  1  ui->startBtn->setEnabled(false); // 修改為true即可開啟

退出對話框:

為工程添加會話框,新建->

image

image

修改類名

image

默認添加到工程中

image

設計繪畫UI如下:

image

添加功能:

ui中分別添加兩個按鍵,並右鍵=>轉到槽……,添加槽函數

void ExitShow::on_confirmBtn_clicked()
{
    reject(); // 確認
}

void ExitShow::on_cancelBtn_clicked()
{
    accept(); // 取消
}

會話類代碼中,填充背景和提示信息

void ExitShow::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    QFont font;
    QPixmap exitShowPic(QString(":/images/backGround5.png"));
    painter.drawPixmap(0,0, this->width(),this->height(),exitShowPic);
    painter.drawRect(0,0,this->width()-1,this->height()-1);
    font = ui->confirmBtn->font();
    painter.setFont(font);
    QRect textRect(0,0,this->width(), this->height()*2/3);
    painter.drawText(textRect, Qt::AlignCenter, "你確定要退出嗎?");
}

在主窗口退出按鍵中,槽函數實現如下:

定義一個自定義的會話框實例,然后設置為模態模式(模態模式只能處理當前窗口,主窗口無效),進行顯示后進入d.exec()循環當中,當會話框中的按鍵被按下,觸發reject()或accpt()時,退出循環,當是確認時,觸發reject(), exec()退出值是QDialog::Rejected。

void FiveChess::on_exitBtn_clicked()
{
    ExitShow d(this);
    d.setModal(true);
    d.show();
    if (d.exec() == QDialog::Rejected) {
        state = EXIT; // 退出游戲過程,結束循環 
        qApp->quit(); // 退出app,程序中存在死循環,會造成后台無法退出
    }
}

2.3.4AI算法(隨機位置下棋)

主要要獲取一個隨機的空閑的棋盤坐標。

初始化中定義一個隨機計數器qsrand(time(0)); 然后通過多次調用qrand()取得隨機數,最后得到一個符合范圍的空閑棋盤坐標,修改該棋盤坐標信息,即完成了下棋操作。

所需頭文件:#include <QTime>

  1 bool FiveChess::computerDropAI()
  2 {
  3     int i, j;
  4     while(1) {
  5         i = qrand() % chessboard->getchequerNumOfLine();
  6         j = qrand() % chessboard->getchequerNumOfLine();
  7         if (chessboard->getChessInfo(i, j) == ChessBoard::noChess) {
  8             break;
  9         }
 10     }
 11     chessboard->setChessInfo(i, j, computerChess);
 12     lastChessX = i;
 13     lastChessY = j;
 14     chessboard->update();
 15     if (chessboard->isGameOver(i, j)) {
 16         return true;
 17     }
 18     return false;
 19 }

2.3.5五子相連判斷與處理

如下圖所示,每下一個棋子,有四條直線上的結果需要判斷;

已知的信息有下棋點的棋盤坐標、當前棋子類型。

image

以落棋點為原點,建立坐標軸,就有8個方向上的結果需要考慮。

先考慮獲取單一方向上的連子數,然后獲取直線上連子數,最后獲取所有的結果,判斷五連。

單一方向上的連子數處理如下:

polarityW、polarityY為坐標軸上的極性,用來遞進棋盤坐標。

x、y:為落棋點棋盤坐標。

返回單一方向上相同類型的棋子數。

  1 int ChessBoard::chessNumcalPart(int polarityW, int polarityY, int x, int y)
  2 {
  3     int val = 0;
  4     int W;
  5     int Y;
  6     dropW = x;
  7     dropH = y;
  8     // 當前坐標 dropW dropH
  9     for (int i = 1; i < 5; i++) {
 10         W = dropW + i * polarityW;
 11         Y = dropH + i * polarityY;
 12         if (W < 0 || Y < 0 || W >= 15 || Y >= 15) {
 13             break;
 14         }
 15         if(chessInfo[W][Y] == currentChess) {
 16              val++;
 17         } else {
 18             break;
 19         }
 20     }
 21     return val;
 22 }

直線上的棋子數處理

分別取直線上的兩個方向的結果,最終判斷是否五連。注意兩組極性的入參需要是在一條直線上的變化。

  1 bool ChessBoard::chessNumcalAll(int polarityW1, int polarityY1, int polarityW2, int polarityY2, int x, int y)
  2 {
  3     int val = 1;
  4     val += chessNumcalPart(polarityW1, polarityY1, x, y);
  5     val += chessNumcalPart(polarityW2, polarityY2, x, y);
  6     if(val >= 5) {
  7         return true;
  8     }
  9     return false;
 10 }

結果判定:

  1 bool ChessBoard::isGameOver(int x, int y)
  2 {
  3     bool result = false;
  4     // 判斷行, 參數為判斷方向上變化的極性
  5     result |= chessNumcalAll(-1, 0, 1, 0, x, y);
  6     // 判斷列
  7     result |= chessNumcalAll(0, -1, 0, 1, x, y);
  8     // 判斷反斜線
  9     result |= chessNumcalAll(-1, 1, 1, -1, x, y);
 10     // 判斷正斜線
 11     result |= chessNumcalAll(-1, -1, 1, 1, x, y);
 12     return result;
 13 }

ChessBoard::isGameOver(iint, int)是棋盤類的方法,由於電腦和人下棋時刻不是在一塊,電腦在互奕回合中判斷結果,人在棋盤落子時判斷結果,但是最終游戲結束時的處理是由主窗口統一處理的,所以把游戲結束的處理函數定義主窗口類中的槽函數,而棋盤類中自定義結束信號;電腦獲勝時可以直接調用槽函數,人獲勝時,發射結束信號;如此電腦和人獲勝都是同一函數處理。

槽函數實現:

  1 // 定義
  2 class FiveChess : public QWidget
  3 {
  4 	……
  5 private slots:
  6 	 void GameOver();
  7 	……
  8  9 // 實現
 10 void FiveChess::GameOver()
 11 {
 12     gameOverFlag = true;
 13     qDebug() << "game is over!!!";
 14     resultLabel->setVisible(true);
 15     timeLine->stop();
 16 
 17     if (currentRound == computer) {
 18         GameOverPrompt(computer);
 19         ui->msgLabel->setText("電腦獲勝!");
 20         if (computerChess == ChessBoard::blackChess) {
 21            qDebug() << "win is computer by black chess!!";
 22         } else {
 23            qDebug() << "win is computer by white chess!!";
 24         }
 25     } else {
 26         GameOverPrompt(player);
 27         ui->msgLabel->setText("獲得勝利!");
 28         if (playerChess == ChessBoard::blackChess) {
 29            qDebug() << "win is player by black chess!!";
 30         } else {
 31            qDebug() << "win is player by white chess!!";
 32         }
 33     }
 34 
 35     state = GAME_OVER;
 36     soundList->setCurrentIndex(3);
 37     music->play();
 38 }

棋盤類信號定義:

  1 class ChessBoard : public QWidget
  2 {
  3     ……
  4 signals:
  5     void boardGameOver();
  6     ……
  7  8 

信號不需要實現具體過程,只需在合適的地方發射信號即可。

信號發射:emit 信號名(); // 如果有參數,需要帶入實參

  1 if(isGameOver(dropW, dropH))
  2 {
  3     emit boardGameOver();
  4 }

2.3.6延時函數

  1 void FiveChess::myMSleep(unsigned int msec)
  2 {
  3     QTime _Timer = QTime::currentTime().addMSecs(msec);
  4     while( QTime::currentTime() < _Timer )
  5         QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
  6 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM