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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...