React的井字過三關(3)

来源:http://www.cnblogs.com/djtao/archive/2016/12/24/6216597.html
-Advertisement-
Play Games

這是React井字棋項目的最後一篇筆記,記述AI實現。 一. 是開頭都會說的原理 但凡懂一點圍棋的人都知道“大場”這個概念,可以淺顯地把它理解為佈局時棋盤上各處的要點。棋諺“金角銀邊草肚皮”,就很好地說明瞭大場具有的特征:價值高。 比如沒其他子的情況下,先手占星角位,這手棋價值大約是20目。第一手下 ...


這是React井字棋項目的最後一篇筆記,記述AI實現。


一. 是開頭都會說的原理

但凡懂一點圍棋的人都知道“大場”這個概念,可以淺顯地把它理解為佈局時棋盤上各處的要點。棋諺“金角銀邊草肚皮”,就很好地說明瞭大場具有的特征:價值高。

比如沒其他子的情況下,先手占星角位,這手棋價值大約是20目。第一手下在頂角,價值可能就1-2目。那就如果第一手占天元,價值...就不好說了。

一個棋類游戲AI實現的難度在於,每手棋的價值其實都是人類經驗性的總結。演算法無論是窮舉或進化,都是人在教機器下棋。人的水平高,機器就學得快。反之亦然。

回到九宮格的井字棋,由於場地條件限制,極大地簡化了ai的實現過程。窮舉就可是最現實的辦法。人只需要根據經驗定義每種情況下點位的價值。教會它計算每手棋的價值,再根據價值排序,找出價值最高的一個就可以了。

只考慮一條線路上的情況,當場面出現己方的二子連線時,無疑最佳落子點是這條線的第三點(一著取勝),這手棋對於取勝來說,必然是無價的。但機器不懂人類的價值觀,我們就假定這手棋的價值是10000吧。如果這條線路上對方出現二子連線,此時場面最有價值的點位就是封堵對方二子連線的位置,這手棋價值也很重要,但不能超過己方二子連線的價值,可算作1000。同理,當這條線路上,只有你的一個子,你下這手棋可以獲得在這條線上的先手優勢,有價值,但是比前兩種情況都低,算作100。如果你下一手棋,線路上只有對方的一個子。你落子於此,既不能一步取勝,也不能封堵對方的二子連線,且不能在這條線上獲得先手優勢,那就是廢著,昏著(如同圍棋中在自己的地盤裡填子一樣)——儘管我們那麼評價它,但這在實戰中還是可能出現的,所以它的權重就是-10。

其它情況有沒有考慮到呢?有比如線路一個子都沒有的情況,先手肯定有一點點優勢。但是在井字棋的佈局里還不夠不典型。就定義為0吧——這裡又有坑了。

實際上的情況是,一手棋包含了2-4條線路。綜合各條線路的判斷下來。累加的權重就是這手棋的價值。


二. AI結構

先在外部js上定義一個ai函數;再把這個ai.js引入到header中。回到組件的頁面。在Game組件的渲染方法return前寫console.log(ai(lastHistory.squares))

好了,可以想象一下,的井字棋應用之前有了一個history狀態。只要在電腦走棋的時候,把這個history狀態發送給AI,AI遍歷狀態,自動找出最佳的落子點,並返回給狀態。

  • 判斷哪些位置能走
function ai(arr,turnToX){
    var loacation=arr.map(function(item,index){
        if(item===null){
            return index;
        }
    }).filter(function(item){
        return item!==undefined;
    });
    ...  
}

通過這一步,得到一個數組。這個數組代表哪些地方(一維坐標)是可以走的。

  • 確定電腦應該扮演的角色,這樣才能分析——把turnToX狀態傳進來
var player=turnToX?'X':'O';
  • 計算能走的點位價值,sort排序返回點位。

之前所謂的ai演算法實現的大坑,其實也就是這麼回事。


三. 情景分析

到目前為止,拿到了可落子的點位,AI已經實現了一半。

再次請出這張棋盤圖:

AI就根據這張圖照著寫。因為不是二維坐標系,所以判斷時必須細心了。

AI結構後續

    // ai函數
    var oCalc={};
    for(var i=0;i<arr.length;i++){
        for(j=0;j<loacation.length;j++){
            var index=loacation[j];
            switch(index){
                case 0:
                case 2:
                case 6:
                case 8:
                  oCalc['loacation'+index]=judgeConner(index);
                  break;
                case 1:
                case 3:
                case 5:
                case 7:
                  oCalc['loacation'+index.toString()]=judgeSide(index);
                  break;
                case 4:
                  oCalc['loacation'+index.toString()]=judgeCenter(index);
                  break;
            }
        }
    }

角位是2,4,6,8。邊位是1,3,5,7。中心位置為4。根據不同的情況返回不同的位置到對象oCalc中。(作為測試,可以把oCalc作為ai函數的return值)。

  • 角位,需要判定的有3條線路(橫豎斜)
  • 邊位,需要判斷2條線路(橫豎)
  • 中心位置需要判斷4條線路:(橫豎撇捺)

接下來就是在各個函數里var一個value,一個個對arr進行判定。

當然,有稍微簡化一點的辦法:意識到很多判斷是重覆的,比如說邊角判斷,線路判斷2,5,8其實就是棋盤被“推倒之後的”0,1,2。根據這個思想可以定義a,b兩個值,當判斷的邊角是0時,a=1,b=2。當判斷的邊角是2時,a=5,b=8。同理,當判斷的邊角是8時,a=7,b=6...如此往複。

處理相同權重的點位

游戲一開始,就發現所有的位置打出來,權重都是0。這也就是之前把單條線路設置為0的坑。最合理的演算法應該是設置為1或10。也就是說,中心位置按理是最佳落子點,但是由於判斷太繁瑣,就省略了這些判斷,當成是0。

事實上做這些判斷是得不償失的,因為體現“1”這個價值的點位只有在第一步時才用到。

解決方案:就是在judgeCneter函數中首先來一個判斷,如果可落子點為9,直接把value設置為10並馬上返回。

頁面還可能會有很多相同價值的落子點,找出第一個返回就行了。

排序

當前需要根據oCalc的鍵值來獲取該對象的屬性:

var largest={
        index:-1,
        value:-100
    };
    for(var attr in oCalc){
        if(oCalc[attr]>largeast.value){
            largest.index=parseInt(attr.replace('loacation',''));
            largest.value=oCalc[attr];
        }
    }
    
    console.log([player,JSON.stringify(largest)]);
    return largest.index;
    /*******ai函數結束*********/
}

註意largest初始狀態。

這下就拿到點位了!


四. 回歸React

現在可以做一個按鈕,點擊請求之後,直接在棋盤上反映AI所走給出來的最佳位置。

給按鈕綁定一個handleClick事件,之前的handleClick參數傳的是狀態,,現在直接把ai的計算結果傳進去。

之前的組件已經完備,狀態工作正常。以至於不需要再動什麼內容。

效果如下

留意到最後一個狀態index是-1.再點擊就會報錯。所以handleClick開頭再加一個如果參數等於-1的判斷。

筆者之前用過jQuery寫面向過程的井字棋(http://djtao.top/ttt/),二者的代碼量差不多,但是React寫的思路非常明確,錯誤非常好排查。深感覺到工程越大,框架優勢越明顯。

至此,渡劫成功。代碼就不放了。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本人看的深入理解jvm(該版本的java se7) java運行時數據區域 Java虛擬機在執行java程式時,把記憶體劃分為幾個不同的階段,存在不同的存在時間。不同的用途 先上圖 程式計數器:是jvm中一小塊記憶體空間,可以當做當前線程的位元組碼文件的行號,位元組碼解釋器通過改變這個值來獲取下一個指令。J ...
  • 這是俺滴師傅給俺傳授了的知識,特在此分享。 TP框架,做PHP開發的都應該有所耳聞。下麵,我們就來說說入口文件的生成: 創建新項目時,首先,在目錄文件下創建一個新的文件夾。然後將Thinkphp框架文件包拷進去。 (框架包) 框架包裡面的文件主要用到的是上面三個圈起來的,只用拷這幾個就行。 (這時, ...
  • StringBuffer 線程安全的可變字元序列。 StringBuffer源碼分析(JDK1.6): public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, Cha ...
  • ·核心作用: -保證一個類只有一個實例,並且提供一個訪問該實例的全局訪問點。 ·常見應用場景: -Windows的Task Manager(任務管理器)就是很典型的單例模式 -Windows的Recycle Bin(回收站)也是很典型的單例應用。在整個系統運行過程中,回收站一直維護著僅有的一個實例 ...
  • 創建型模式:-單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式結構型模式:-適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式行為型模式:-模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式 ...
  • 關於Prometheus Prometheus是一套開源的監控系統,它將所有信息都存儲為時間序列數據;因此實現一種Profiling監控方式,實時分析系統運行的狀態、執行時間、調用次數等,以找到系統的熱點,為性能優化提供依據。 監控方式 程式代碼收集運行數據寫入到redis,通過API介面開放給Pr ...
  • 一、緣起 分散式環境下,多台機器上多個進程對一個數據進行操作,如果不做互斥,就有可能出現“餘額扣成負數”,或者“商品超賣”的情況,如何實現簡易分散式鎖,對分散式環境下的臨界資源做互斥,是今天將要討論的話題。 二、互斥原理 原理:多個訪問方對同一個資源進行操作,需要進行互斥,通常是利用一個這些訪問方同 ...
  • 前面的話   一個完整的HTML文檔必須包含3個部分:文檔聲明、文檔頭部和文檔主體。而正是它們構成了HTML的骨架結構。前面已經分別介紹過文檔聲明和文檔頭部,本文將詳細介紹構成HTML骨架結構的基礎元素   HTML    與``標簽限定了文檔的開始 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...