2048 a.i. 在 stackoverflow 上有個討論:
得票最高的回答是基於 min-max-tree + alpha beta 剪枝,啟發函式的設計很優秀。
其實也可以不用設計啟發函式就寫出 a.i. 的,我用的方法是圍棋 a.i. 領域的經典演算法——monte carlo 局面評估 + uct 搜尋。
演算法的介紹見我幾年前寫的一篇博文:
簡而言之就兩點:
通過隨機遊戲評估給定局面的得分;
從博弈樹的父節點往下選擇子節點時,綜合考慮子節點的歷史得分與嘗試次數。
針對2048遊戲,我對演算法做了乙個改動——把 minx-max-tree 改為 random-max-tree,因為增加數字是隨機的,而不是理性的博弈方,所以猜想 min-max-tree 容易傾向過分保守的博弈策略,而不敢追求更大的成果。
uct搜尋的**:
orientation uctplayer::nextmove(const fullboard& full_board) constnewprofit函式用於更新該節點到某葉子節點的記錄,是遞迴實現的:return
bestchild(full_board);
}
float uctplayer::newprofit(board::fullboard *node,起初用結局的最大數字作為得分,後來發現當跑到512後,monte carlo棋局的結果並不會出現更大的數字,各個節點變得沒有區別。於是作了改進,把移動次數作為得分,大為改善。int* mc_count) const
else
else
node_record->setvisitedtimes(visited_times + 1
); }
return
result;
}
整個程式的設計分為 board、player、game 三大模組,board 負責棋盤邏輯,player 負責移動或增加數字的邏輯,game把board和player連起來。
game類的宣告如下:
classrun函式的實現:game
void
run();
protected
: game(board::fullboard &&full_board,
addingnumberplayeruniqueptr &&adding_number_player,
movingplayeruniqueptr &&moving_player);
virtual
void beforeaddnumber() const
virtual
void beforemove() const
private
: board::fullboard full_board_;
addingnumberplayeruniqueptr adding_number_player_unique_ptr_;
movingplayeruniqueptr moving_player_unique_ptr_;
disallow_copy_and_assign(game);
};
void這樣就可以通過繼承 game 類,實現不同的建構函式,組合出不同的 game,比如 montecarlogame 的建構函式:game::run()
else
}}
montecarlogame::montecarlogame(fullboard &&full_board) :乙個新的2048棋局,會先放上兩個數字,新棋局應該能方便地build。預設應該隨機地增加兩個數字,builder 類可以這麼寫:game(move(full_board),
std::move(game::addingnumberplayeruniqueptr(
newaddingnumberrandomlyplayer)),
std::move(game::movingplayeruniqueptr(
new movingrandomlyplayer))) {}
template很久以前,高效的 c++ **不提倡在函式中 return 靜態分配記憶體的物件,現在有了右值引用就方便多了。class
newgamebuilder ;
template
newgamebuilder
::newgamebuilder() :
initialization_player_(game::game::addingnumberplayeruniqueptr(
newplayer::addingnumberrandomlyplayer))
template
newgamebuilder
& newgamebuilder::setaddingnumberplayer(
game::game::addingnumberplayeruniqueptr &&initialization_player)
template
g newgamebuilder
::build() const
return
g(std::move(full_board));
}
main 函式:
這個a.i.的移動不像基於人為設定啟發函式的a.i.那麼有規則,不會把最大的數字固定在角落,但最後也能有相對不錯的結果,遊戲過程更具觀賞性~
Monte Carlo方法的基本思路
monte carlo方法的基本思路 1 針對實際問題建立乙個簡單且便於實現的概率統計模型,使所求的解恰好是所建模型的概率分布或其某個數字特徵,比如是某個事件的概率,或者是該模型的期望值。2 對模型中的隨機變數建立抽樣方法,在計算機上進行模擬試驗,抽取足夠的隨機數,並對有關的事件進行統計。3 對模擬...
蒙特卡洛(Monte Carlo)方法的理解
蒙特卡洛方法在計算機 領域應用較多,無論是文獻還是報告均是高頻出現詞彙,為了加深印象將我理解的蒙特卡洛方法整理如下。一 蒙特卡洛方法的簡單理解 蒙特卡洛方法是一種基於隨機數的計算方法,明確這是一種計算方法,其也被稱為隨機模擬方法 計算機隨機模擬方法等。是一種基於隨機取樣 或稱為統計取樣 的數值計算方...
R語言由Monte Carlo方法計算積分
蒙特 卡羅方法 monte carlo method 也稱統計模擬方法,是二十世紀四十年代中期由於科學技術的發展和電子計算機的發明,而被提出的一種以概率統計理論為指導的一類非常重要的數值計算方法。是指使用隨機數 或更常見的偽隨機數 來解決很多計算問題的方法。f function o sum rexp...