場の空気を読むAI作成

場の空気を読むAI作成


<発端>
ネットで見かけたある漫画の一シーンを読んで疑問に感じた為

<基本アイデアのメモ>
  • 表裏のトスコインを10万回行う
  • 導関数を利用して、観測者(博打AI)に指定された観測範囲から時間軸前方の状況を分析。表裏どちらかにBETさせる
  • その賭けの正解率を計算してみる。勝率が高ければ「場の流れ」は存在すると判断できる?
  • うまく行けばゲームのAIに利用できる?
  • diffの利用?

最初に頭に浮かんだのはこの式でおっさんが言ってる事なんだけど、主人公は反論している。実際に色々やってみて確認してみる事にする

Formulamath4.jpg

なんとなく「場から降りて様子を見る」「勝てそうな場の流れならBETする」みたいな仕組みが必要な気がする
かならず、どちらかに賭けて強制的に勝負させられる形式では無理かも
思うに「瞬間」と「平均」は性質が違うから、何か面白い現象が見えてくるかもしれないし、何も見えてこないかもしれない

とにかく1/2×xの部分を壊して別の傾きに変えてしまわないと必ず0.5に収束されてしまうのでこの関数の部分を考える
ほにゃららの2乗の形にすれば良いような気もする・・・



<第一段階>

まず「賭場」であるCoinクラスを作ります
これはintの0~1の乱数の10万個の配列を作成します。0が表、1が裏です
これに正規分布的に正しいか1/2チェックする為、レートを表示できるようにしています

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class DerivatiiveQuest1 : MonoBehaviour {

  void Start () {
       Coin coin = new Coin(100000);
       print("コイン表の配分率" + coin.Rate);
       print(coin.ConvertIdeom(0,10));
   }


  class Coin
   {
       int total;
       int[] face;
       float frontCount,backCount,rate;

      public Coin (int num)
       {
           total = num;
           face= new int[total];
           for (int i = 0; i < total-1; i++) {
               face[i]=Random.Range(0,2);
               if(face[i]==0){frontCount++;}else{backCount++;}
           }
       }

      public float Rate {
           get{return frontCount/total;}
       }

      public int Total {
           get{return total;}
       }
       
       public IEnumerable<int> GetCoin(int startNum,int endNum){
           for (int i = startNum; i < endNum; i++) {
               yield return face[i];
           }
       }

      public string ConvertIdeom(int startNum,int endNum){
           string str;
           string ide="";
           for (int i = startNum; i < endNum; i++) {
               if(face[i]==0){str = "表";}else{str = "裏";}
               ide += str;
           }
           return ide;
       }
   }
}

レートに関して何度か実行してみた結果、小数点3桁目からゆらぎがあるようですが
これなら誤差の範囲内でしょう。次に博打AIを作成します

ver0.4


「未来予測するようなAI」をどのように組めばよいか
これに関して微分を使ってグラフの傾き(速度)を観測しそれに対して適切に行動するプログラムを組むことを考える

基本的な動作

  • 凸方向の傾きを検出したら場に参加しBET
  • 凹方向の傾きを検出したら場から離れ勝機を待つ

?傾きのベクトルの長さが大きくなるほどリターンが大きくなる(勝機と考え)掛け金を大きくする
?一定のベクトルの長さになるまで待つ(細かい勝ちを重ねるでなく大きな勝ちを狙う)

?のアルゴリズムはそのAIの性格(そのゲームのキャラクターの性格)になり、これをすれば必ず勝てるといった
アルゴリズムでないがあると面白い要素だと思う


Ver0.3


そもそも我々人間は、この表裏の博打をする前に何故確率は1/2だと思ったのだろう?

その考察を以下にまとめる



Ver0.2


<第三段階>

コインの表裏が3連続以上出た場合、場に参加して逆の側に賭けると勝率はどうなるかを次にやってみようと思います
……
もう少し考え方を変えてみる。導関数で重要なことは傾きであるので、この傾きをうまく導く必要がある
つまり表の出た回数を縦軸、裏の出た回数を横軸にしてグラフを描いた時、傾きは、その率となる筈だ
これを考えの起点としてみよう。45度以上の傾きであれば表に勢いがあり、45度以下であれば裏に勢いがある...
そして角度差の値が強度のスカラーとなる...

まず「表裏の場の流れ」をベクトルと考え、その距離と角度を求めるコードを書くと...

public class Vec : MonoBehaviour {

  public Vector2 vector1;
   float distance,degree;

  void Start () {
       vector1 =new Vector2(2,1);
   }

  void Update ()
   {
       distance= vector1.magnitude;
       degree = Mathf.Atan2(vector1.y,vector1.x)*Mathf.Rad2Deg;
   }
}

となる。これより幾つかのケースを考えてみる

derivativeQuest2.jpg

結果は以上のようになる。この図を見れば色々博打の際に使えそうなパラメータが考えられるのではないだろうか
例えば距離。これは場の出目の強度であると考える事ができる。この値が大きければ「賭け値を上げる」や「場に参加してBETする」ようにすれば良い用な気がする

そして、後一歩進めて「未来予測するようなAI」をどのように組めばよいか考える



ver0.1


<第二段階>

「場から降りて様子を見る」「勝てそうな場の流れならBETする」のような仕組みを組まない単純なAI
つまり5ターン過去までの状態を認識して常に場に参加し確率の高い方へBETするコードです

表裏表表裏 この場合、裏にBETします
裏裏裏裏表 この場合、表にBETします

これは自ら1/2、つまり良くて引き分けに誘導するAIです
思ったとおり、勝率がほぼ1/2の位置へ収束します。これでは賭博師AIとはいえません


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class DerivatiiveQuest1 : MonoBehaviour {

  enum COINFACE{
       OMOTE = 0,
       URA =1
   }

  int deltaT;

  void Start () {
       Coin coin = new Coin(100000);
       print("コイン表の配分率" + coin.Rate);
       print(coin.ConvertIdeom(0,10));

      var printCoin = coin.GetCoin(0,10);
       foreach (var item in printCoin) {
           print(item);
       }

      Ai ai = new Ai(coin);

      for (int i = 0; i < 500; i++) {
           ai.progresses();
       }

      print(string.Format("勝利数= {0:f4}",ai.Win));
   }


  class Ai
   {

      Coin _coin;
       int _deltaT;            //時間は観測者側に依存させることにする
       int range;
       int winCount,loseCount;
       float winRate;

      public int Range{
           get{return range; }
           set{value = range;}
       }

      public float Win {
           get{return (float)winCount / (float)_deltaT;}
       }

      public Ai (Coin coin)
       {
           _coin=coin;
           _deltaT = 0;
           range = 5;
       }

      //観測者の時間を進める
       public bool progresses(){
           int answer;

          if(_deltaT == 0){
               answer = Random.Range(0,2);        //過去が無いので予想不可能。あてずっぽう
           }else{
               answer = this.Reasoning(range);
           }

          if(answer == _coin.Face(_deltaT)){ winCount++; } else { loseCount++; }

          _deltaT++;
           if(_deltaT <_coin.Total){return true;}
           return false;
       }

      //まず場を離れて様子見をしない単純なパターン。たぶん良くて引き分け
       //過去の履歴より推理して勝率の高いと思われる回答を返す
       int Reasoning(int fowardRange){
           int Recollections;
           int frontCount=0;
           int backCount=0;
           int answer=0;

          if(fowardRange > _deltaT){Recollections=_deltaT;} else {Recollections=fowardRange;}
           var coinList = _coin.GetCoin(_deltaT-Recollections,_deltaT);
           foreach (var item in coinList) {
               if(item==0){frontCount++;}else{backCount++;}
           }

          if(frontCount / Recollections > 0.5f){answer = 1;}

          return  answer;
       }
   }


  class Coin
   {
       int total;
       int[] face;
       float frontCount,backCount,rate;

      public Coin (int num)
       {
           total = num;
           face= new int[total];
           for (int i = 0; i < total-1; i++) {
               face[i]=Random.Range(0,2);
               if(face[i]==0){frontCount++;}else{backCount++;}
           }
       }

      public float Rate {
           get{return frontCount/total;}
       }

      public int Total {
           get{return total;}
       }

      public int Face(int num){
           return face[num];
       }

      public IEnumerable<int> GetCoin(int startNum,int endNum){
           for (int i = startNum; i < endNum; i++) {
               yield return face[i];
           }
       }

      public string ConvertIdeom(int startNum,int endNum){
           string str;
           string ide="";
           for (int i = startNum; i < endNum; i++) {
               if(face[i]==(int)COINFACE.OMOTE){str = "表";}else{str = "裏";}
               ide += str;
           }
           return ide;
       }
   }

}

これでは、まず勝てないと思います(引き分ける為のやりかたなので)
冒頭の漫画が常に勝負をし続けるゲームだったのかは1ページだけなので判らないのですが、
賭点を変更したり、場から一時的に離れて流れを読んで賭けるパターンを
次に、なんとかもう少し勝ち点を上げられそうなAIを考えて見ます

  • 最終更新:2014-04-29 14:55:40

このWIKIを編集するにはパスワード入力が必要です

認証パスワード