2048游戲-AI程序算法分析

針對(duì)曾經(jīng)火爆的2048游戲,有人實(shí)現(xiàn)了一個(gè)AI程序,可以以較大概率(高于90%)贏得游戲,并且作者在stackoverflow上簡要介紹了AI的算法框架和實(shí)現(xiàn)思路。但是這個(gè)回答主要集中在啟發(fā)函數(shù)的選取上,對(duì)AI用到的核心算法并沒有仔細(xì)說明。這篇文章將主要分為兩個(gè)部分,第一部分介紹其中用到的基礎(chǔ)算法,即Minimax和Alpha-beta剪枝;第二部分分析作者具體的實(shí)現(xiàn)。


基礎(chǔ)算法
2048本質(zhì)上可以抽象成信息對(duì)稱雙人對(duì)弈模型(玩家向四個(gè)方向中的一個(gè)移動(dòng),然后計(jì)算機(jī)在某個(gè)空格中填入2或4)。這里“信息對(duì)稱”是指在任一時(shí)刻對(duì)弈雙方對(duì)格局的信息完全一致,移動(dòng)策略僅依賴對(duì)接下來格局的推理。作者使用的核心算法為對(duì)弈模型中常用的帶Alpha-beta剪枝的Minimax。這個(gè)算法也常被用于如國際象棋等信息對(duì)稱對(duì)弈AI中。
Minimax
下面先介紹不帶剪枝的Minimax。首先本文將通過一個(gè)簡單的例子說明Minimax算法的思路和決策方式。
問題
現(xiàn)在考慮這樣一個(gè)游戲:有三個(gè)盤子A、B和C,每個(gè)盤子分別放有三張紙幣。A放的是1、20、50;B放的是5、10、100;C放的是1、5、20。單位均為“元”。有甲、乙兩人,兩人均對(duì)三個(gè)盤子和上面放置的紙幣有可以任意查看。游戲分三步:
甲從三個(gè)盤子中選取一個(gè)。
乙從甲選取的盤子中拿出兩張紙幣交給甲。
甲從乙所給的兩張紙幣中選取一張,拿走。

其中甲的目標(biāo)是最后拿到的紙幣面值盡量大,乙的目標(biāo)是讓甲最后拿到的紙幣面值盡量小。
下面用Minimax算法解決這個(gè)問題。
基本思路
一般解決博弈類問題的自然想法是將格局組織成一棵樹,樹的每一個(gè)節(jié)點(diǎn)表示一種格局,而父子關(guān)系表示由父格局經(jīng)過一步可以到達(dá)子格局。Minimax也不例外,它通過對(duì)以當(dāng)前格局為根的格局樹搜索來確定下一步的選擇。而一切格局樹搜索算法的核心都是對(duì)每個(gè)格局價(jià)值的評(píng)價(jià)。Minimax算法基于以下樸素思想確定格局價(jià)值:
Minimax是一種悲觀算法,即假設(shè)對(duì)手每一步都會(huì)將我方引入從當(dāng)前看理論上價(jià)值最小的格局方向,即對(duì)手具有完美決策能力。因此我方的策略應(yīng)該是選擇那些對(duì)方所能達(dá)到的讓我方最差情況中最好的,也就是讓對(duì)方在完美決策下所對(duì)我造成的損失最小。
Minimax不找理論最優(yōu)解,因?yàn)槔碚撟顑?yōu)解往往依賴于對(duì)手是否足夠愚蠢,Minimax中我方完全掌握主動(dòng),如果對(duì)方每一步?jīng)Q策都是完美的,則我方可以達(dá)到預(yù)計(jì)的最小損失格局,如果對(duì)方?jīng)]有走出完美決策,則我方可能達(dá)到比預(yù)計(jì)的最悲觀情況更好的結(jié)局。總之我方就是要在最壞情況中選擇最好的。

上面的表述有些抽象,下面看具體示例。
解題
下圖是上述示例問題的格局樹:



注意,由于示例問題格局?jǐn)?shù)非常少,我們可以給出完整的格局樹。這種情況下我可以找到Minimax算法的全局最優(yōu)解。而真實(shí)情況中,格局樹非常龐大,即使是計(jì)算機(jī)也不可能給出完整的樹,因此我們往往只搜索一定深度,這時(shí)只能找到局部最優(yōu)解。
我們從甲的角度考慮。其中正方形節(jié)點(diǎn)表示輪到我方(甲),而三角形表示輪到對(duì)方(乙)。經(jīng)過三輪對(duì)弈后(我方-對(duì)方-我方),將進(jìn)入終局。黃色葉結(jié)點(diǎn)表示所有可能的結(jié)局。從甲方看,由于最終的收益可以通過紙幣的面值評(píng)價(jià),我們自然可以用結(jié)局中甲方拿到的紙幣面值表示終格局的價(jià)值。
下面考慮倒數(shù)第二層節(jié)點(diǎn),在這些節(jié)點(diǎn)上,輪到我方選擇,所以我們應(yīng)該引入可選擇的最大價(jià)值格局,因此每個(gè)節(jié)點(diǎn)的價(jià)值為其子節(jié)點(diǎn)的最大值:



這些輪到我方的節(jié)點(diǎn)叫做max節(jié)點(diǎn),max節(jié)點(diǎn)的值是其子節(jié)點(diǎn)最大值。
倒數(shù)第三層輪到對(duì)方選擇,假設(shè)對(duì)方會(huì)盡力將局勢(shì)引入讓我方價(jià)值最小的格局,因此這些節(jié)點(diǎn)的價(jià)值取決于子節(jié)點(diǎn)的最小值。這些輪到對(duì)方的節(jié)點(diǎn)叫做min節(jié)點(diǎn)。
最后,根節(jié)點(diǎn)是max節(jié)點(diǎn),因此價(jià)值取決于葉子節(jié)點(diǎn)的最大值。最終完整賦值的格局樹如下:

總結(jié)一下Minimax算法的步驟:
首先確定最大搜索深度D,D可能達(dá)到終局,也可能是一個(gè)中間格局。
在最大深度為D的格局樹葉子節(jié)點(diǎn)上,使用預(yù)定義的價(jià)值評(píng)價(jià)函數(shù)對(duì)葉子節(jié)點(diǎn)價(jià)值進(jìn)行評(píng)價(jià)。
自底向上為非葉子節(jié)點(diǎn)賦值。其中max節(jié)點(diǎn)取子節(jié)點(diǎn)最大值,min節(jié)點(diǎn)取子節(jié)點(diǎn)最小值。
每次輪到我方時(shí)(此時(shí)必處在格局樹的某個(gè)max節(jié)點(diǎn)),選擇價(jià)值等于此max節(jié)點(diǎn)價(jià)值的那個(gè)子節(jié)點(diǎn)路徑。

在上面的例子中,根節(jié)點(diǎn)的價(jià)值為20,表示如果對(duì)方每一步都完美決策,則我方按照上述算法可最終拿到20元,這是我方在Minimax算法下最好的決策。格局轉(zhuǎn)換路徑如下圖紅色路徑所示:



對(duì)于真實(shí)問題中的Minimax,再次強(qiáng)調(diào)幾點(diǎn):
真實(shí)問題一般無法構(gòu)造出完整的格局樹,所以需要確定一個(gè)最大深度D,每次最多從當(dāng)前格局向下計(jì)算D層。
因?yàn)樯鲜鲈颍琈inimax一般是尋找一個(gè)局部最優(yōu)解而不是全局最優(yōu)解,搜索深度越大越可能找到更好的解,但計(jì)算耗時(shí)會(huì)呈指數(shù)級(jí)膨脹。
也是因?yàn)闊o法一次構(gòu)造出完整的格局樹,所以真實(shí)問題中Minimax一般是邊對(duì)弈邊計(jì)算局部格局樹,而不是只計(jì)算一次,但已計(jì)算的中間結(jié)果可以緩存。

Alpha-beta剪枝
簡單的Minimax算法有一個(gè)很大的問題就是計(jì)算復(fù)雜性。由于所需搜索的節(jié)點(diǎn)數(shù)隨最大深度呈指數(shù)膨脹,而算法的效果往往和深度相關(guān),因此這極大限制了算法的效果。
Alpha-beta剪枝是對(duì)Minimax的補(bǔ)充和改進(jìn)。采用Alpha-beta剪枝后,我們可不必構(gòu)造和搜索最大深度D內(nèi)的所有節(jié)點(diǎn),在構(gòu)造過程中,如果發(fā)現(xiàn)當(dāng)前格局再往下不能找到更好的解,我們就停止在這個(gè)格局及以下的搜索,也就是剪枝。
Alpha-beta基于這樣一種樸素的思想:時(shí)時(shí)刻刻記得當(dāng)前已經(jīng)知道的最好選擇,如果從當(dāng)前格局搜索下去,不可能找到比已知最優(yōu)解更好的解,則停止這個(gè)格局分支的搜索(剪枝),回溯到父節(jié)點(diǎn)繼續(xù)搜索。
Alpha-beta算法可以看成變種的Minimax,基本方法是從根節(jié)點(diǎn)開始采用深度優(yōu)先的方式構(gòu)造格局樹,在構(gòu)造每個(gè)節(jié)點(diǎn)時(shí),都會(huì)讀取此節(jié)點(diǎn)的alpha和beta兩個(gè)值,其中alpha表示搜索到當(dāng)前節(jié)點(diǎn)時(shí)已知的最好選擇的下界,而beta表示從這個(gè)節(jié)點(diǎn)往下搜索最壞結(jié)局的上界。由于我們假設(shè)對(duì)手會(huì)將局勢(shì)引入最壞結(jié)局之一,因此當(dāng)beta小于alpha時(shí),表示從此處開始不論最終結(jié)局是哪一個(gè),其上限價(jià)值也要低于已知的最優(yōu)解,也就是說已經(jīng)不可能此處向下找到更好的解,所以就會(huì)剪枝。
下面同樣以上述示例介紹Alpha-beta剪枝算法的工作原理。我們從根節(jié)點(diǎn)開始,詳述使用Alpha-beta的每一個(gè)步驟:
根節(jié)點(diǎn)的alpha和beta分別被初始化為?∞
?∞,和+∞
+∞。
深度優(yōu)先搜索第一個(gè)孩子,不是葉子節(jié)點(diǎn),所以alpha和beta繼承自父節(jié)點(diǎn),分別為?∞
?∞,和+∞
+∞
搜索第三層的第一個(gè)孩子,同上。
搜索第四層,到達(dá)葉子節(jié)點(diǎn),采用評(píng)價(jià)函數(shù)得到此節(jié)點(diǎn)的評(píng)價(jià)值為1。


此葉節(jié)點(diǎn)的父節(jié)點(diǎn)為max節(jié)點(diǎn),因此更新其alpha值為1,表示此節(jié)點(diǎn)取值的下界為1。

再看另外一個(gè)子節(jié)點(diǎn),值為20,大于當(dāng)前alpha值,因此將alpha值更新為20。
此時(shí)第三層最左節(jié)點(diǎn)所有子樹搜索完畢,作為max節(jié)點(diǎn),更新其真實(shí)值為當(dāng)前alpha值:20。
由于其父節(jié)點(diǎn)(第二層最左節(jié)點(diǎn))為min節(jié)點(diǎn),因此更新其父節(jié)點(diǎn)beta值為20,表示這個(gè)節(jié)點(diǎn)取值最多為20。


搜索第二層最左節(jié)點(diǎn)的第二個(gè)孩子及其子樹,按上述邏輯,得到值為50(注意第二層最左節(jié)點(diǎn)的beta值要傳遞給孩子)。由于50大于20,不更新min節(jié)點(diǎn)的beta值。


搜索第二層最左節(jié)點(diǎn)的第三個(gè)孩子。當(dāng)看完第一個(gè)葉子節(jié)點(diǎn)后,發(fā)現(xiàn)第三個(gè)孩子的alpha=beta,此時(shí)表示這個(gè)節(jié)點(diǎn)下不會(huì)再有更好解,于是剪枝。


繼續(xù)搜索B分支,當(dāng)搜索完B分支的第一個(gè)孩子后,發(fā)現(xiàn)此時(shí)B分支的alpha為20,beta為10。這表示B分支節(jié)點(diǎn)的最大取值不會(huì)超過10,而我們已經(jīng)在A分支取到20,此時(shí)滿足alpha大于等于beta的剪枝條件,因此將B剪枝。并將B分支的節(jié)點(diǎn)值設(shè)為10,注意,這個(gè)10不一定是這個(gè)節(jié)點(diǎn)的真實(shí)值,而只是上線,B節(jié)點(diǎn)的真實(shí)值可能是5,可能是1,可能是任何小于10的值。但是已經(jīng)無所謂了,反正我們知道這個(gè)分支不會(huì)好過A分支,因此可以放棄了。


在C分支搜索時(shí)遇到了與B分支相同的情況。因此講C分支剪枝。


此時(shí)搜索全部完畢,而我們也得到了這一步的策略:應(yīng)該走A分支。
可以看到相比普通Minimax要搜索18個(gè)葉子節(jié)點(diǎn)相比,這里只搜索了9個(gè)。采用Alpha-beta剪枝,可以在相同時(shí)間內(nèi)加大Minimax的搜索深度,因此可以獲得更好的效果。并且Alpha-beta的解和普通Minimax的解是一致的。
針對(duì)2048游戲的實(shí)現(xiàn)
下面看一下ov3y同學(xué)針對(duì)2048實(shí)現(xiàn)的AI。程序的github在這里,主要程序都在ai.js中。
建模
上面說過Minimax和Alpha-beta都是針對(duì)信息對(duì)稱的輪流對(duì)弈問題,這里作者是這樣抽象游戲的:
我方:游戲玩家。每次可以選擇上、下、左、右四個(gè)行棋策略中的一種(某些格局會(huì)少于四種,因?yàn)橛行┓较虿豢勺撸P衅搴蠓綁K按照既定邏輯移動(dòng)及合并,格局轉(zhuǎn)換完成。
對(duì)方:計(jì)算機(jī)。在當(dāng)前任意空格子里放置一個(gè)方塊,方塊的數(shù)值可以是2或4。放置新方塊后,格局轉(zhuǎn)換完成。
勝利條件:出現(xiàn)某個(gè)方塊的數(shù)值為“2048”。
失敗條件:格子全滿,且無法向四個(gè)方向中任何一個(gè)方向移動(dòng)(均不能觸發(fā)合并)。

如此2048游戲就被建模成一個(gè)信息對(duì)稱的雙人對(duì)弈問題。
格局評(píng)價(jià)
作為算法的核心,如何評(píng)價(jià)當(dāng)前格局的價(jià)值是重中之重。在2048中,除了終局外,中間格局并無非常明顯的價(jià)值評(píng)價(jià)指標(biāo),因此需要用一些啟發(fā)式的指標(biāo)來評(píng)價(jià)格局。那些分?jǐn)?shù)高的“好”格局是容易引向勝利的格局,而分低的“壞”格局是容易引向失敗的格局。
作者采用了如下幾個(gè)啟發(fā)式指標(biāo)。
單調(diào)性
單調(diào)性指方塊從左到右、從上到下均遵從遞增或遞減。一般來說,越單調(diào)的格局越好。下面是一個(gè)具有良好單調(diào)格局的例子:



平滑性
平滑性是指每個(gè)方塊與其直接相鄰方塊數(shù)值的差,其中差越小越平滑。例如2旁邊是4就比2旁邊是128平滑。一般認(rèn)為越平滑的格局越好。下面是一個(gè)具有極端平滑性的例子:



空格數(shù)
這個(gè)很好理解,因?yàn)橐话銇碚f,空格子越少對(duì)玩家越不利。所以我們認(rèn)為空格越多的格局越好。
孤立空格數(shù)
這個(gè)指標(biāo)評(píng)價(jià)空格被分開的程度,空格越分散則格局越差。
具體來說,2048-AI在評(píng)價(jià)格局時(shí),對(duì)這些啟發(fā)指標(biāo)采用了加權(quán)策略。具體代碼如下:
// static evaluation function
AI.prototype.eval = function() {
 var emptyCells = this.grid.availableCells().length;
 
 var smoothWeight = 0.1,
 //monoWeight = 0.0,
 //islandWeight = 0.0,
 mono2Weight = 1.0,
 emptyWeight = 2.7,
 maxWeight = 1.0;
 
 return this.grid.smoothness() * smoothWeight
 //+ this.grid.monotonicity() * monoWeight
 //- this.grid.islands() * islandWeight
 + this.grid.monotonicity2() * mono2Weight
 + Math.log(emptyCells) * emptyWeight
 + this.grid.maxValue() * maxWeight;
};

有興趣的同學(xué)可以調(diào)整一下權(quán)重看看有什么效果。
對(duì)對(duì)方選擇的剪枝
在這個(gè)程序中,除了采用Alpha-beta剪枝外,在min節(jié)點(diǎn)還采用了另一種剪枝,即只考慮對(duì)方走出讓格局最差的那一步(而實(shí)際2048中計(jì)算機(jī)的選擇是隨機(jī)的),而不是搜索全部對(duì)方可能的走法。這是因?yàn)閷?duì)方所有可能的選擇為“空格數(shù)×2”,如果全部搜索的話會(huì)嚴(yán)重限制搜索深度。
相關(guān)剪枝代碼如下:

// try a 2 and 4 in each cell and measure how annoying it is
// with metrics from eval
var candidates = [];
var cells = this.grid.availableCells();
var scores = { 2: [], 4: [] };
for (var value in scores) {
for (var i in cells) {
scores[value].push(null);
var cell = cells[i];
var tile = new Tile(cell, parseInt(value, 10));
this.grid.insertTile(tile);
scores[value][i] = -this.grid.smoothness() + this.grid.islands();
this.grid.removeTile(cell);
}
}

// now just pick out the most annoying moves
var maxScore = Math.max(Math.max.apply(null, scores[2]), Math.max.apply(null, scores[4]));
for (var value in scores) { // 2 and 4
for (var i=0; i<scores[value].length; i++) {
if (scores[value][i] == maxScore) {
candidates.push( { position: cells[i], value: parseInt(value, 10) } );
}
}
}

搜索深度
在2048-AI的實(shí)現(xiàn)中,并沒有限制搜索的最大深度,而是限制每次“思考”的時(shí)間。這里設(shè)定了一個(gè)超時(shí)時(shí)間,默認(rèn)為100ms,在這個(gè)時(shí)間內(nèi),會(huì)從1開始,搜索到所能達(dá)到的深度。相關(guān)代碼:

// performs iterative deepening over the alpha-beta search
AI.prototype.iterativeDeep = function() {
 var start = (new Date()).getTime();
 var depth = 0;
 var best;
 do {
 var newBest = this.search(depth, -10000, 10000, 0 ,0);
 if (newBest.move == -1) {
 //console.log('BREAKING EARLY');
 break;
 } else {
 best = newBest;
 }
 depth++;
 } while ( (new Date()).getTime() - start < minSearchTime);
 //console.log('depth', --depth);
 //console.log(this.translate(best.move));
 //console.log(best);
 return best
}

因此這個(gè)算法實(shí)現(xiàn)的效果實(shí)際上依賴于執(zhí)行javascript引擎機(jī)器的性能。當(dāng)然可以通過增加超時(shí)時(shí)間來達(dá)到更好的效果,但此時(shí)每一步行走速度會(huì)相應(yīng)變慢。
算法的改進(jìn)
目前這個(gè)實(shí)現(xiàn)作者聲稱成功合成2048的概率超過90%,但是合成4096甚至8192的概率并不高。作者在github項(xiàng)目的REAMDE中同時(shí)給出了一些優(yōu)化建議,這些建議包括:
緩存結(jié)果。目前這個(gè)實(shí)現(xiàn)并沒有對(duì)已搜索的樹做緩存,每一步都要重新開始搜索。
多線程搜索。由于javascript引擎的單線程特性,這一點(diǎn)很難做到,但如果在其它平臺(tái)上也許也可考慮并行技術(shù)。
更好的啟發(fā)函數(shù)。也許可以總結(jié)出一些更好的啟發(fā)函數(shù)來評(píng)價(jià)格局價(jià)值。

參考文獻(xiàn)
2048 Game
2048-AI github
An Exhaustive Explanation of Minimax, a Staple AI Algorithm
Tic Tac Toe: Understanding the Minimax Algorithm
CS 161 Recitation Notes - Minimax with Alpha Beta Pruning

分享自CodingLabs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 五子棋 五子棋五子棋是比較流行的棋類游戲了,玩法簡單,基本上人人會(huì)玩,在此就不介紹游戲規(guī)則了。下面使用 swift...
    天機(jī)否閱讀 20,554評(píng)論 3 29
  • 棋類游戲?qū)?zhàn)的實(shí)現(xiàn) 六洲棋 五子棋 AI對(duì)戰(zhàn) 藍(lán)牙對(duì)戰(zhàn) 在線對(duì)戰(zhàn) 六洲棋 六洲棋,又稱:泥棋、插方、來馬、五福棋,...
    天機(jī)否閱讀 1,236評(píng)論 0 9
  • 決策樹理論在決策樹理論中,有這樣一句話,“用較少的東西,照樣可以做很好的事情。越是小的決策樹,越優(yōu)于大的決策樹”。...
    制杖灶灶閱讀 5,883評(píng)論 0 25
  • 每每總是有點(diǎn)不忍心回過頭去看的走過的路,每次回頭,都會(huì)有點(diǎn)失望。不得不承認(rèn),走過的那些時(shí)光,不是不美好,只是仍不是...
    野獸派蘑菇閱讀 352評(píng)論 0 1
  • 采首陽之銅兮,諸侯是將, 見牛斗之分兮,耀耀其光。 鑒其質(zhì)而顯名兮,天地不藏, 相交兵乎諸原兮,累累多骨, 以惡金...
    在山水清閱讀 411評(píng)論 0 0