QT5寫Tetris之AI機器人玩遊戲

背景

使用Qt5.12.9的QGraphicsItem來實現俄羅斯方塊,使用簡單的評估函數,實現AI機器人玩俄羅斯方塊遊戲。這是AI機器人的第一步,這個算法很簡單,但頗有效,大多數狀況能消5百層以上,最近的爲數很少的測試中,最高紀錄已經消了超過2500層。在這個基礎上,能夠方便的積累原始數據,我但願能抽取模式,進行模式識別及至機器學習。python

思路

在手動遊戲基礎上進行改造,借鑑回放的經驗,只須要加入一個評估算法,爲每個新方塊找出一個放置的姿態(旋轉次數)和最終位置座標就能夠了。個人算法設計也很簡單,就是爲每個方塊窮舉其放置方法,使用一個緊密程度的評估算法進行評分,取出最高分的操做,如有相同得分的操做,用隨機數二一添作五。linux

效果圖

關鍵代碼分析

流程控制

界面操做控制變量,作到隨時能夠在手動與自動兩種模式之間進行切換。git

if (isAutoRunning) {                //自動模式
        autoProcessCurBlock();            //處理當前方塊,使用評估函數肯定方塊的最終姿態與位置
        block->relocate(curPos);            //放置
        block->setBlockNotActive();        //固定方塊
        generateNextBlock();            //取下一個方塊,遊戲繼續
    }else                        //手動模式
        this->moveBlockDown();
    ...

方塊放置評分函數

個人設計思想很直觀,俄羅斯方塊就是要儘可能緊密的堆積在一塊兒,因此對每個組成方塊的block都檢測一個它周圍的四個位置,看是否有block(包括邊界)存在,如有就加1分,沒有不加分。這塊的加分,並無區別組成方塊自身的block和外界的block,由於每一個方塊都是與本身進行比較,因此區分與不區分效果是同樣的。起始分由深度肯定,越深我認爲效果越好。另個,block的垂直下方最好不要有空洞,如有會減分。github

int Game::evaluate(Tetris* t)
{
    QPoint pos = t->getPos();
    int ct = pos.y();                //深度爲基礎分
    int cct = t->cleanCount();
    if (cct > 1)                //能消層,加分
        ct += 10 * (cct - 1);
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (t->data[i][j]) {
                ct += t->hasTetrisBlock(pos.x() + j + 1, pos.y() + i) ? 1 : 0;            //檢測block右邊的位置
                ct += t->hasTetrisBlock(pos.x() + j - 1, pos.y() + i) ? 1 : 0;            //檢測block左邊的位置
                ct += t->hasTetrisBlock(pos.x() + j, pos.y() + i + 1) ? 1 : 0;            //檢測block下方的位置
                ct += t->hasTetrisBlock(pos.x() + j, pos.y() + i - 1) ? 1 : 0;            //檢測block上方的位置

                if (i == 3 || t->data[i + 1][j] == 0) {                    
                    if (!t->hasTetrisBlock(pos.x() + j, pos.y() + i + 1)) {            //block下方的緊臨空洞
                        ct -= 4;
                    }
                    else {
                        int k = 2;
                        while (pos.y() + i + k <= 19) {
                            if (!t->hasTetrisBlock(pos.x(), pos.y() + i + k)) {    //block下方的非緊臨空洞
                                ct -= 1;
                                break;
                            }
                            k++;
                        }
                    }
                }
            }
        }
    }
    return ct;
}

窮舉方塊的全部放置方式

一個方塊最多隻有四種姿態,把方塊的每一種姿態都從左到右moveDownEnd一次,進行評分,取得分最高的方案。算法

void Game::autoProcessCurBlock()
{
    int max = 0;
    QPoint initPos = block->getPos();
    Tetris* tmp = new Tetris(initPos, block->getShape(), -1);        //構造當前方塊的替身,blockType爲-1,這種方塊不會顯示
    int rotateCt = block->getRotateNum();                //同步替身初始姿態
    for (int k = 0; k < rotateCt; k++)
        tmp->rotate();
    rotateCt = 0;                            //用於保存方塊的最終姿態

    for (int r = 0; r < 4; r++) {                    //四種姿態遍歷,其實能夠優化,有的方塊不須要四次
        if (r > 0) {
            tmp->relocate(initPos);                //注意,旋轉要在方塊進入遊戲界面的地方旋轉,否則可能旋轉不成功
            tmp->rotate();
        }
        while (tmp->moveLeft());                //從最左邊開始
        do {
            tmp->moveDownEnd();
            tmp->setBlockNotActive();            //固定方塊,以便進行評分
            int score = evaluate(tmp);            //評分
            if (score > max) {                //找到當前最優方案
                max = score;
                curPos = tmp->getPos();
                rotateCt = r;
            }
            else if (score == max) {                //出現相等評分,隨機取
                if (qrand() % 2 == 1) {
                    curPos = tmp->getPos();
                    rotateCt = r;
                }
            }
            //initPos.setX(tmp->getPos().x());
            tmp->relocate(QPoint(tmp->getPos().x(), initPos.y()));    //返回到遊戲空間上方
            tmp->setBlockTest();                //方塊恢復到測試狀態 
        } while (tmp->moveRight());                //方塊右移,直到不能移動 
    }
    delete tmp;                            //銷燬測試方塊,忽然想到這塊能夠優化,只須要建七個方塊就好,這樣就不用不斷的建立和銷燬了
    for (int k = 0; k < rotateCt; k++)
        block->rotate();
}

下一步的設想

使用python從新實現全部功能,也再也不用Qt,就用python自帶的tkinter就好。把重點放在模式提取,讓AI自動玩遊戲,寫個算法,提取優秀的操做模式。而後使用模式匹配或機器學習算法來優化AI。如今尚未具體的想法,只有這麼個大概的設想。windows

源代碼及運行方法

項目採用cmake組織,請安裝cmake3.10以上版本。下面腳本是windows下基於MSVC的,其它操做系統上基本相似,或者使用qtcreator打開進行操做。機器學習

cmake -A win32 -Bbuild .
cd build
cmake --build . --config Release

注:本項目採用方案能跨平臺運行,已經適配過windows,linux,mac。函數

源代碼:學習

https://gitee.com/zhoutk/qtetris.git

測試

https://gitee.com/zhoutk/qtdemo/tree/master/tetrisGraphicsItem

https://github.com/zhoutk/qtDemo/tree/master/tetrisGraphicsItem
相關文章
相關標籤/搜索