基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx

上传人:b****4 文档编号:20273265 上传时间:2023-01-21 格式:DOCX 页数:45 大小:2.30MB
下载 相关 举报
基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx_第1页
第1页 / 共45页
基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx_第2页
第2页 / 共45页
基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx_第3页
第3页 / 共45页
基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx_第4页
第4页 / 共45页
基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx_第5页
第5页 / 共45页
点击查看更多>>
下载资源
资源描述

基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx

《基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx(45页珍藏版)》请在冰豆网上搜索。

基於UCT的围棋引擎的研究与实现Word文档下载推荐.docx

2.4棋步產生(UCT)模組8

2.5棋步合法性判斷模組10

2.6算氣模組10

2.7更新棋盤(提子)模組11

2.8勝負計算模組11

3基於UCT的圍棋引擎的設計12

3.1圍棋引擎總體流程設計12

3.2UCT演算法具體流程設計13

3.3棋步合法性判斷模組設計16

3.4算氣模組設計18

3.5更新棋盤(提子)模組設計19

3.6勝負計算模組設計20

4基於UCT的圍棋引擎的實現22

4.1軟硬體開發環境22

4.2圍棋引擎的資料結構22

4.2.1棋局數據22

4.2.2UCTTree數據23

4.3圍棋引擎的UCT演算法實現23

4.3.1UCT演算法的核心實現23

4.3.2候選步的產生方式及管理機制26

4.3.3選擇節點(UCT選擇)28

4.3.4展開節點31

4.3.5棋局模擬33

4.4圍棋引擎運行效果41

4.4.1圍棋協議對弈測試41

4.4.2調試模式的SGF檔43

4.4.3UCT類比棋局資料統計44

5工作總結及未來展望45

5.1工作總結45

5.2未來展望45

致謝46

參考文獻47

英文摘要49

1緒論

1.1研究背景及意義

博弈是人工智慧的重要研究主題,人工智慧的發展在很大程度上得益於博弈研究的發展。

1997年著名的深藍電腦戰勝國際象棋世界冠軍卡斯帕羅夫成為轟動一時的新聞事件[l]。

可以說,作為博弈研究的主要內容之一,棋類博弈得到了滿意的解決,唯一的例外的是圍棋,目前最優秀的圍棋程式還處於業餘低段水準。

圍棋是博弈的一種,屬雙人零和博弈[2]。

它起源於3000多年前的中國,充分體現了東方人的智慧,盛行于中日韓,逐漸在歐美流行。

它比國際象棋複雜得多,正因為如此,很多人工智慧學家、心理學家和數學家也投入到了電腦圍棋研究領域。

電腦圍棋這個名稱來自於ComputerGo的直譯,略顯生硬。

簡單地說,電腦圍棋就是結合人工智慧技術教電腦下圍棋,以達到與人類棋手相抗衡的目的。

由於圍棋的搜索空間太大、電腦難於處理模糊概念且難於設計學習演算法,造成了電腦圍棋程式的棋力難於提高。

同時,開發出與人類棋手水準相當的圍棋程式也有助於對人類認知能力的理解。

1.2研究狀況

電腦圍棋自Zobrist在1970年設計出第一個可與人對弈的程式以來[3],至今已有約四十年的歷史。

由於圍棋本身的特質,使得電腦圍棋在繼西洋棋、象棋之後,成為人工智慧中一個相當引人注目的新挑戰。

然而電腦圍棋的難點之一,便在於缺乏良好的局面評估函數[4],使其不能跟國際象棋一樣,運用設計良好的局面評估函數、搜尋樹以及優秀的剪枝法,即可獲得不錯的棋力;

電腦圍棋大多借鑒一些經驗法則,以靜態的評估為主,而動態的搜尋則僅用於局部的、目標明確的棋串攻殺,較少的全域搜尋。

因此,人類的經驗如何應運用於電腦圍棋,就成了設計的重點。

自2003年起,Bouzy試圖打破這種情況[5]。

他運用蒙特卡羅(MonteCarlo)方法作為評估函數,並且試圖運用這一評估函數,作全域性的搜索,然而在棋力上始終沒有大的突破。

直到2006年,同樣使用蒙特卡羅方法的程式CrazyStone[6~7],才在杜林舉行的第11屆電腦奧林匹克的九路圍棋項目中奪得金牌。

雖然如此,CrazyStone僅在19路圍棋項目中奪得第五名,仍未撼動以人類思維為主的圍棋程式GNUGo在19路圍棋的地位。

然而,隨著基於蒙特卡羅的UCT(UpperConfidenceboundsappliedtoTrees)搜索方法的出現,以UCT為基礎的圍棋程式MoGo[8~10]也逐漸在一些較非正式的比賽中嶄露頭角。

2007年6月,第12屆電腦奧林匹克於阿姆斯特丹舉行,上屆冠軍GNUGo、亞軍GOIntellect以及前文介紹過的CrazyStone等程式均有參賽,MoGo在強敵環伺之下,以全勝戰績奪得了19路圍棋項目的金牌,CrazyStone也拿到了第二名,GNUGo退居第三。

這象徵UCT的成功,也代表一個嶄新的局面的到來。

1.3關鍵技術

1.3.1MonteCarlo方法

MonteCarlo[11~12]方法主要理論基礎是依據大數定理,在隨機取樣的情況下,可以獲得有誤差的評估值,取樣的數量越多,誤差將越小,評估值將越準確。

將MonteCarlo方法應用於圍棋[13],其核心的思想是,在於通過統計許多模擬棋局的結果,進行局面的優劣判斷。

亦即將蒙特卡羅方法作為局面評估函數,以決定著手的好壞。

其中,所謂的模擬棋局,指的是對某一目標盤面,由電腦隨機落子,直到終盤而可以判定勝負為止。

在隨機落子時,除了基本的圍棋規則外,只有一個限制:

不得自填眼位,這個限制是為了防止棋局無法結束而設定的。

模擬棋局的結果,與目前常見的只判斷黑勝或白勝不同,而是會判斷輸贏的目數,在決定著手優劣時,則是統計此著手下所有模擬棋局平均的輸贏目數來決定的。

1.3.2UCT演算法

UCT又名UCBforTreeSearch,是UCB(UpperConfidenceBound)在TreeSearch上的應用。

而UCB本來是為了解決吃角子老虎機問題(BanditProblem)而產生的。

所謂的吃角子老虎機問題,簡述如下:

目前有若干台吃角子老虎機,每台機器可以投錢並拉動操縱杆,此時會得到收益(reward),投錢、拉杆、得到收益的過程,稱之為一個Play。

每台吃角子老虎機有不同的收益率,倘若玩家想要在若干次的Play裡獲得最大總收益,那麼該怎麼做?

一般來說,玩家會開始動手玩,並且依照目前積累的經驗來決定下一次的Play要選擇哪一台機器,這稱之為開發(exploitation)。

相對地,如果玩家不斷地依照目前所獲得的經驗來決定,而不試圖嘗試其它的其他的機器,則可能會忽略收益率更高的機器,因此適度地嘗試其他機器是必須的,這稱之為探險(exploration)。

如何在開發與探險之間保持平衡,就是UCB試圖解決的ExE(exploitationvs.exploration)問題。

UCB根據目前獲得的資訊,配合上一個調整值,試圖在開發與探險間保持平衡。

大致上來說,每一次Play時,UCB會根據每一台機器目前的平均收益值(亦即其到目前為止的表現),加上一個額外的參數,得出本次Play此台機器的UCB值,然後根據此值,挑選出擁有最大UCB值的機器,作為本次Play所要選擇的機器。

其中,所謂額外參數,會隨每一台機器被選擇的次數增加而相對減少,其目的在於讓選擇機器時,不過分拘泥於舊有的表現,而可以適度地探索其他機器。

UCB公式表示如下(也稱為UCB1)[14]:

(3)

是第j台機器到目前為止的平均收益,

是第j台機器被測試的次數,n是所有機器目前被測試的總次數。

讓公式(3)的值最大的機器將是下一個被選擇的機器。

前項即為此台機器的過去表現,後項則是調整參數。

而UCB1-TUNED是相對於UCB1實驗較佳的配置策略[14]。

UCB1-TUNED的公式如下:

(4)

(5)

讓公式(5)的值最大的機器將是下一個被告選擇來測試的機器。

UCT(UCBforTreeSearch)其實就是把UCB1或UCB1-TUNED(統稱為UCB)等公式運用於TreeSearch上的一個方法[14]。

以概念而言,UCT把每一個節點都當作是一個吃角子老虎機問題,而此節點的每一個分支,都是一台吃角子老虎的機器。

選擇分支,就會獲得相應的收益。

TreeSearch開始時,UCT會建立一棵Tree,然後:

(1)從根節點開始

(2)利用UCB公式計算每個子節點的UCB值,選擇UCB值最高的子節點

(3)若此子節點並非葉節點(從未拜訪過的節點),則由此節點開始,重複

(2)

(4)直到遇到葉節點,則計算葉節點的收益值,並依此更新根節點到此一節點路徑上所有節點的收益值

(5)由

(1)開始重複,直到時間結束,或達到某一預設次數

(6)由根節點的所有子節點中,選擇平均收益值最高者,作為最佳節點

此一節點,就是UCT的結果。

2基於UCT的圍棋引擎的概述

2.1圍棋引擎的總體概述

電腦圍棋程式通常也可稱為電腦圍棋引擎,具體表現為引擎本身沒有也不需要實現圖形圍棋介面(這通常是交由圍棋用戶端來做的),而是通過特定的圍棋協定與引擎外部進行通信,如圖2.1所示。

這樣的好處是邏輯簡單,功能明確,只需要關注圍棋引擎的核心部分,即圍棋引擎是如何去思考的,並嘗試產生最優棋步。

如果不去考慮圍棋引擎具體的思考過程,它表現出來的僅僅是它思考的最終結果,一個棋步而已。

除此之外,圍棋引擎所做的工作就是實現對弈雙方交替落子的過程,同時根據棋規進行相應的更新棋盤處理,並在棋局結束時計算勝負。

事實上,圍棋引擎與圍棋用戶端的關係可以拿IDE和編譯器的關係來類比。

在編譯原始檔案的時候,首先需要設置使用哪個編譯器和相應的編譯參數,然後IDE會調用編譯器去編譯原始檔案,接著編譯器執行編譯過程,最後編譯器返回編譯結果給IDE,IDE則顯示編譯結果給使用者查看。

而在圍棋對弈的時候,首先需要設置使用哪個圍棋引擎和圍棋協定參數,然後圍棋用戶端會通過圍棋協定告訴圍棋引擎為黑棋(或白棋)產生一個棋步,接著圍棋引擎進行思考,最後圍棋引擎返回思考得到的棋步給圍棋用戶端,圍棋用戶端則在棋盤上顯示該棋步給圍棋用戶查看。

圖2.1圍棋引擎與圍棋用戶端的通信

2.2圍棋引擎的總體功能模組

圍棋引擎TaoGo的總體功能模組圖如圖2.2所示。

其中交替下子流程功能模組是圍棋引擎的總體流程。

如何基於UCT產生棋步是圍棋引擎的核心功能模組,在棋步產生(UCT)功能模組下面的五個子功能模組中除了候選步產生及管理子模組是起輔助作用以外,其它四個子功能模組實際上是一個有機聯繫的整體,構成圍棋引擎的UCT演算法流程。

棋步合法性判斷模組是根據圍棋規則來判斷一個棋步是否合法。

更新棋盤(提子)模組是檢查是否有吃子,有則提掉,並記錄可能的打劫位置。

勝負計算模組是在棋局結束時根據圍棋規則來計算勝負。

算氣模組是計算一個棋串的氣數。

圖2.2圍棋引擎總體功能模組

如圖2.3,是圍棋引擎各個模組之間的關係。

可以看到,圍棋引擎交替下子流程模組對外負責與圍棋用戶端進行通信,然後調用引擎內部其它模組進行處理。

例如圍棋用戶端輸入了對手棋步,圍棋引擎交替下子流程模組會調用棋步合法性判斷模組對棋步進行判斷,如果合法則更新棋盤,然後調用棋步產生(UCT)模組產生棋步,最後輸出棋步返回給圍棋用戶端。

圍棋引擎交替下子流程模組在棋局開始時,還會進行一些初始化工作,主要是商定圍棋規則,在棋局結束時則計算對弈雙方的勝負。

棋步產生(UCT)模組在產生棋步過程中會執行多次模擬棋局,正式對局中所需要用到的功能模組同樣會被調用,包括棋步合法性判斷模組、更新棋盤(提子)模組和勝負計算模組。

算氣模組作為最基礎的模組,為各個模組提供依據,服務的上層的模組包括棋步產生(UCT)模組、棋步合法性判斷模組和更新棋盤(提子)模組。

圖2.3圍棋引擎模組間的關係

2.3交替下子流程模組

交替下子流程模組展示了圍棋引擎的基本流程活動,如圖2.4是交替下子流程模組的活動圖。

活動一開始是進行初始化引擎,主要是商定圍棋規則和初始化圍棋引擎內部的一些參數和資料。

然後進入對弈雙方交替下子的過程,直到棋局結束。

交替下子時,如果是對手下子,則從圍棋引擎外部得到對手棋步,否則輪到引擎下子,此時引擎的棋步產生模組會產生合法棋步,然後輸出產生的棋步到圍棋引擎外部。

每當有下子的動作發生,則進行更新棋盤,對棋局改變的狀態進行相應的處理。

當棋局結束時,根據圍棋規則計算雙方的勝負,如果對弈雙方對計算勝負的結果沒有爭議,棋局正式結束,否則繼續對弈。

有一點需要指出的是,在這裡沒有明顯指出得到對手棋步是否要考慮該棋步的合法性。

棋步的合法性通常圍棋引擎外部的圍棋用戶端也會判斷保證,不過在設計時應該考慮加入對對手棋步的合法性判斷和相應的錯誤處理,以保證邏輯的完整性,使得棋局能夠順利進行。

圖2.4交替下子流程的活動圖

2.4棋步產生(UCT)模組

棋步產生(UCT)模組使用UCT演算法來產生棋步,是整個圍棋引擎的核心功能模組。

前面1.3.2小節已經介紹了UCT演算法的理論,下面來說明UCT演算法究竟是如何應用在圍棋上的。

UCT使用在圍棋上,主要的概念,就是每個節點代表一個盤面,此節點的分支代表此盤面下的合法著手,每個分支聯結到的子節點,就是原盤面加上分支代表的著手後,所產生的新盤面。

一般而言,目前盤面為根節點,根節點的分支代表目前盤面下的合法著手,根節點的子節點代表根節點的盤面加上分支代表的著手後所形成的新盤面,如圖2.5所示。

圖2.5UCTTree的概念表示,其中每個節點均記錄訪問次數,勝利次數,形成此節點的著手,著手顏色等資訊

此外,UCB公式的收益值,如前1.3.1小節所述,就是依照MonteCarlo方法的概念,用模擬棋局的結果來決定。

上面1.3.2小節中UCT演算法第4個步驟中,電腦收益值,在此應該改為執行模擬棋局。

所謂的模擬棋局,就是給定一個盤面(在此給的是葉節點所代表的盤面),由電腦接手落子,直到終局,然後判定並回傳勝負結果(黑勝或白勝)。

在此要注意的是,UCT會據此結果,更新葉節點到根節點上所有節點的收益值,也就是說,一個節點會概括承受所有子節點模擬棋局的結果。

對於任一節點,其收益值為:

此一節點的模擬棋局勝利數/此節點被訪問的次數,亦即其勝率。

上式中所謂的勝利數,指的是指向此節點的分支所代表的顏色(黑棋或白棋)在模擬時的勝利次數。

UCTTree搜索流程總結如下:

UCT演算法使用極小極大遊戲樹(minimaxgametree),搭配節點選擇公式(UCB公式),選擇樹節點,展開要測試的節點,然後使用MonteCarlo方法執行模擬棋局,最後將模擬棋局的結果回饋更新。

流程示意圖如圖2.6所示。

圖2.6UCTTree搜索流程示意圖

2.5棋步合法性判斷模組

一個棋步是否合法,是根據圍棋規則來確定的,規則如下:

(1)pass(也稱虛著)是合法的棋步,允許任何一方放棄下子權而使用虛著。

黑白雙方輪流在空點上落子,即不能在非空棋點上落子。

(2)禁著點。

棋盤上的任何一點,如某方下子後,該子立即呈無氣狀態,同時又不能提取對方的棋子。

這個點叫做禁著點。

(3)禁止全域同形。

著子後不得使對方重複面臨曾出現過的局面。

其中打劫是全域同形最基本的情況。

2.6算氣模組

當任何一方落子後,除了虛著以外,都有可能把對方的棋子提掉,這個時候就要計算與所落子相鄰的所有對方棋串的氣數。

事實上,在判斷棋步的合法性時也往往需要計算雙方棋串的氣數。

計算棋串的氣數主要採用標記法,從棋串的某一個棋子開始,先標記該棋子已訪問過,然後對於該棋子四個相鄰的棋點,如果相鄰棋點是空點,則棋串氣數加1;

如果相鄰棋點上是對方的棋子,則無需對該方向上的相鄰棋點繼續進行探索;

如果相鄰棋點上是己方棋子,則對該相鄰棋點上的己方棋子進行同樣的操作,直至計算完整個棋串的氣數。

2.7更新棋盤(提子)模組

更新棋盤時,主要是利用計算棋串氣數的功能檢查是否有提子,有提子時將對方的棋串提掉,即清空對應棋串位置,並記錄可能導致劫爭的位置。

2.8勝負計算模組

著子完畢的棋局,採用數子法計算勝負。

將雙方死子清理出盤外後,對任意一方的活棋和活棋圍住的點以子為單位進行計數。

雙方活棋之間的空點各得一半。

棋盤總點數的一半點為歸本數。

一方總得點數超過此數為勝,等於此數為和,小於此數為負。

如果有貼子,則要按照相關的規定進行計算。

 

3基於UCT的圍棋引擎的設計

3.1圍棋引擎總體流程設計

圖3.1圍棋引擎總體流程

如圖3.1所示,圍棋引擎的總體流程實際就是類比對弈雙方交替落子的過程。

當輪到對手落子時,圍棋引擎通過圍棋協定從引擎外部得到對手的棋步。

否則圍棋引擎使用UCT演算法產生棋步,並通過圍棋協議輸出產生的棋步到引擎外部。

不管是得到對手棋步之後或者是輸出產生棋步之前,都必須檢查棋步的合法性,如不合法則進行相應的出錯處理。

如果檢查的棋步是合法的,接下來就根據圍棋規則做出正確的處理。

如果合法棋步是pass時,則將pass數加1;

否則將pass置為0。

更新棋盤時,對於非pass合法棋步,則提取可能的死子並記錄可能的打劫位置,對於pass棋步則不做任何改變。

最後,棋步數加1,然後回去判斷pass是否大於等於2,如果是說明雙方都pass,棋局結束,否則雙方繼續落子。

3.2UCT演算法具體流程設計

UCT演算法的流程大致分為四個部分:

第一部分是選擇節點,在遊戲樹中選擇子節點。

第二部分是展開節點,產生新的子節點。

第三部分是棋局類比,執行模擬的棋局。

第四部分是回饋更新,將模擬棋局的結果以回溯方式更新遊戲樹節點的資訊。

在本論文中,將針對該流程前三部分進行詳細說明。

圍棋引擎TaoGo使用的UCT流程如下,其示意圖如圖3.2所示。

(1)選擇節點

若有未被訪問過的子節點,則優先以隨機方式選擇其中一個子節點落子然後執行模擬棋局(轉到(3)),否則繼續使用節點選擇公式UCB1或UCB1-TUNED選擇子節點。

當被選中的子節點為葉節點並且該子節點被訪問的次數未達到指定的次數時,則選擇該子節點落子然後執行模擬棋局(轉到(3))。

當被選中的子節點為葉節點並且該子節點被訪問的次數達到指定的次數,則需先展開該子節點(轉到

(2))。

否則重複此步驟直到找到被訪問的次數未達到指定的次數或未被訪問過的葉節點為止。

(2)展開

當節點為葉節點並且該節點被訪問的次數達到指定的次數時,進行展開子結點。

展開時對候選步作篩選,去除不適合的候選步,再將篩選後的候選步展開成子節點並隨機選擇其中一個節點(轉到

(1))。

(3)棋局模擬

當被選擇的葉節點落子後開始執行模擬棋局,在模擬棋局中檢查是否有棋串少於四氣,若有則嘗試逃跑;

如果有符合簡單的模式庫的棋形,執行棋形庫模式匹配;

如果沒有符合棋形的空點,則嘗試攻擊對方少於四氣的棋串;

如果都沒有合適的棋步,最後使用隨機方式選擇合法棋步。

(4)回饋更新

將模擬棋局的結果回溯更新遊戲樹節點的資訊。

圖3.2TaoGo的UCT具體流程示意圖

在實際上執行UCTSearch時,其流程可以用圖3.3表示。

在圖3.3中,所謂GetBestSequence,指的是從根節點開始,根據UCB公式向下搜索,直到找到被訪問次數不超過指定次數的葉節點為止的過程。

當搜索時,若被訪問的節點達到指定的次數,則會產生子節點,子節點的多寡,直接影響到搜索的深度與棋力的高低,因此對於產生出來的子節點,必須加以裁減。

第三個步驟是類比棋局。

所謂類比棋局,就是由上一個步驟所找到的葉節點開始,由電腦接手下完,並回傳勝負結果,以更新UCTTree。

模擬棋局的核心就是其決定著手的方法。

最簡單的方法,就是純粹隨機落子(除了非法著手或自填眼位以外),這種方法的好處就是快速、簡單,且符合MonteCarlo的精神。

但缺點則是用這種方法做出來的圍棋程式,棋力低落。

要使棋力進步的重點,在於棋局的手順要有意義,而不是隨機落子,天南地北地亂下。

TaoGo建立了一個著手庫對常見棋形進行模式匹配,如果沒有棋形可以匹配則對少於四氣的己方棋串進行長氣或叫吃對方少於四氣的棋串,如果以上都不成功,則隨機落子[15-24]。

有了模擬棋局的結果,就要依此更新UCTTree。

所謂更新,指的是把從根節點到第二步驟中所找到的葉節點所形成的路徑上的所有點,依照模擬棋局的結果來更新勝場數和訪問次數,亦即若此點為黑,且模擬棋局之結果為黑勝,則此節點的勝利次數加一,反之亦然。

訪問次數則是此路徑上的所有節點都加一。

若總模擬次數達到所設定的次數,或限制時間已用完,則結束UCTSearch,並且挑出根節點下被訪問次數最多的子節點,作為此次搜索的最佳著手。

圖3.3UCTSearch的流程圖

3.3棋步合法性判斷模組設計

棋步的合法性是根據圍棋規則來判斷,如圖3.4所示。

分別檢查是否是虛著,是否在棋盤內,是否是打劫以及是否是自殺(禁著點)。

虛著,通常採用特殊的棋盤座標來表示,正常棋步跟特殊棋盤座標比較即可知道知道是虛著。

有時候產生的棋步的座標值是不合法的,此時棋步不在棋盤內,跟正常棋盤座標範圍比較即可。

而打劫可借助記錄可能造成打劫的位置,只要比較棋步的座標和造成打劫的位置即可。

至於自殺棋步的判斷,如圖3.5所示。

棋子自殺顧名思義是使得下子後棋子所在的棋串沒有氣數,但同時必須沒有殺死對方的任何棋串。

先計算下子後棋子所在的棋串的氣數,如果棋串氣數不為0則說明肯定不是自殺棋步。

如果棋串氣數為0,則需要考慮有沒有可能殺死對方棋子。

可通過計算與該棋串相鄰的對方棋串的氣數來判斷,如果沒有相鄰對方棋串氣數為0,即提取死子數為0,屬於自殺,是不合法棋步。

如果有提取對方死子,此時也有可能是打劫,不過打劫的情況已經在前面被排除了,說明儘管有提取死子,但肯定不是由於打劫造成的。

這也是打劫要先進行判斷的原因。

圖3.4棋步合法性判斷流程圖

圖3.5自殺棋步判斷

3.4算氣模組設計

算氣模組用來計算一個棋串的氣數。

通常是已知棋串的某一個棋子,計算該棋串的氣數。

使用如下步驟進行計算:

(1)將當前棋子標記為已

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 计算机软件及应用

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1