我最近偶然發現一款叫2048的遊戲。你需要通過上下左右方向鍵來移動合併值相同的方塊(Title)。每一次移動之後,一個值為2或者4的新方塊會隨機出現在某個空位置。如果所有位置都塞滿方塊,並且沒有值相同的方塊可以合併的時候,遊戲結束。遊戲的目標是合併出一個值為2048的方塊。
我需要遵循一套定義良好的策略來實現這個目標。所以我想到寫個程式來實現。我當前的演算法如下:
1 2 3 4 5 6 |
while(!game_over) { for each possible move: count_no_of_merges_for_2-tiles and 4-tiles choose the move with large number of merges } |
我所做的是,在任何時刻,我都嘗試合併值為2或者4的方塊,也就是我會嘗試讓值為2和4的方塊越少越好。如果我嘗試那麼做,其它的方塊會自動的合併,看起來像是個好策略。
但是當我真正使用這套演算法的時候,我大概只能得到4000分,遊戲就結束了。遊戲的最高分應該是20000多點,遠超我當前的分數。有比上面策略更好的演算法嗎?
最佳回答 by ovolve
我是AI程式的作者,前面也有人提到AI程式。你可以看AI版遊戲,或者直接閱讀原始碼。
當前,這套執行在我筆記本瀏覽器的javascript程式能夠達到90%左右的勝率,每次移動的思考時間是100毫秒。儘管不是最完美,但做得還不賴。
既然這個遊戲是一個離散狀態空間,資訊完備的回合制遊戲,類似於象棋和國際跳棋,那麼我就使用了針對這些遊戲的證明過的行之有效的方法。一套叫minimax search的演算法,結合了alpha-beta pruning。既然已經有很多資訊解釋了這套演算法,那麼我就僅談談我在static evaluation function中使用到的兩個重要概念。這將會把一些人在這裡表達的直覺形式化。
單調性(Monotonicity)
這個概念保證方塊的值沿著上下左右方向的,要麼增加,要麼減少。這個概念單獨地解釋了一個大家提到的直覺,值較大的方塊應該聚集到某一個角落。這將有助於阻止值小的方塊被孤立起來,也將讓皮膚保持良好的組織結構,使得值小的方塊漸進層疊式的並逐步合併為值大的方塊。
下圖是一個有完美單調性格子的截圖。我通過執行eval函式被設定為忽略其它概念的演算法獲得,僅僅考慮單調性。
平滑性(Smoothness)
上面的概念傾向於構造值遞減的結構,但如要合併,相鄰的方格值必須相同。因此,平滑性衡量相鄰方格值的差,並嘗試減少差。
Hacker News上的一個評論者用圖論給出了一個平滑性的有趣解釋。來源於2048的一個優秀分支。
下圖是個有完美單平滑性的截圖。
空閒方塊(Free Tiles)
最後,有一個針對空閒格子過少的懲罰。畢竟皮膚過於擁擠的時候,選擇受限且很快會被用完。
就是這樣。掃描遊戲格子,同時優化以上標準,這會產生相當好的表現。與明確硬編碼的移動策略相比,這種使用通用性的方法有一個優點,這種演算法可以找到有趣且難以預料的解決方案。如果你觀察它執行,它經常會做出一些驚奇但有效的移動,比如突然轉向一個相反的牆或者角落。
修改
這是該方法強大能力的一個展示。我拿掉了方格值大小的限制(到2048之後還可以繼續執行,下圖是8次嘗試中最好一次的截圖,是的,那可是一個4096外加一個2048),那意味著在同一個皮膚上它完成了3次困難的2048方塊。