10.03 BOIDSv2.00 ~知識(シ)ル~

以前NHKの番組で名人を破った「AI将棋」がどのように作られているかの説明を聞いたことがあります。直ぐに思いつくのは「コンピュータは速いから一手打つと次どうするかを瞬時に可能性から導き出すのでは」という考えです。ここまで一緒に作って試した方にはお分かりかと思いますが、「対象の数が多くなると」sin/cosですら実際に計算すると処理時間に影響が出てしまうほどです。将棋も同じように可能性からの選択(2分木方式)すると、前半選択肢は膨大な数になってしまいますのでスーパーコンピュータでもかなりの時間がかかってしまいます。では、どうしているか?

AI将棋は分記法式は用いず、勝った場合の主要駒の配置を図形として学習し、日に数百万回と将棋を打って「学習」します。その学習した過去の知識から同じような図形になるように打つ手を選び出しているのだそうです。最近の若い棋士たちはパソコン相手に練習するため旧態定石とは違った新しい手を次々打ってくるので昔の手しか知らない世代は戸惑っているとか・・・。

実はプログラムの世界にもオブジェクト指向とともに旧タイプの考え方や手法というものがあります。まさにここでいう「旧態の定石」です。
要するに
    if  (~だったら) (XXXへ移動する)  else  (***へ移動する)

といったように、予め決められた(プログラムで作成された)方法でしか移動できないのが、それにあたります。
オブジェクト指向のようにプログラムの作り方そのものも対象ですが、ロジックも同じように新旧があるわけです。今回は、この作る時の論理も新しい考えを元に作ってみましょう。

今回のVer2.00では、折角ポッドに「」が与えられたのですから、将棋AIのように「与えられた情報=目から見たもの」→「知識との照合」といったイメージで作成します。



★何を知りたいのか

はじめに「知識」は「何に使用」されるか?

これは単純に「泳ぐため」であり「身を守る為」であり「食べ物をとる為」であります。
このために「泳ぐ方向を条件によって変更したい」ことが究極の目的です。

今回のシミュレーションでは「泳ぐ方向を変更するための条件」は全部で3つあります。それは、

① 壁の接近

② 他のポッドの接近

③ サメの接近

しかし「たった3つ」なのですが、これが結構難しい!!
例えば一番左上の方の青い魚で考えてみますと、左右に壁、前方に他のポッドが存在します。

眼で見た瞬間的判断として、どちらへ移動するか?

将棋のお話と同じことが起こってきました。 瞬時に判断するにはどうしたらよいのか?という問題です。



結果的にみると上のように、距離を保ってぐるぐる回るようになればよいわけです。

人間だったらどうするでしょうか?
人間だったら危険に優先順位を付けて回避するでしょう。
危険に対する優先順位とは

① 厳守すべき壁情報
 壁は通り抜けできないので左右どっちにあるのかの区分情報。
 この場合以前のような内積・外積は必要なくドット検知が中央の右か左かで容易に決まります。

②他のポッド情報
 他のポッドは移動しているので自ポッドが移動後、隣接する情報ではなく現時点での位置情報で
 非常にあいまいな情報といえます。

③サメ情報
 現時点ではややこしいので考えないでおきます。

どう回避していくかは次回詳しくやるとして、今回はその前に判別すべき情報をどのように蓄積するかを決めなくてはいけません。



要注意事項】
私も作成するに当たりはまってしまったことがあります。
今までの延長線上で作成していくと、実はこの情報を全く得ることができないのです。 よぉーく考えていただくとわかるのですが、このプログラムは、

①ビットマップイメージを一旦クリアする。

②新しいイメージを書く

③ピクチャーボックス内のビットマップイメージへ実際に書く

3つが循環しています。今までのように座標計算していた個所ではイメージがクリアされていて前の状態が残っていません。
このためピクチャーボックスを描画した後に領域を実施し各々のポッドから見た状況を知識として残す必要があるのです。この知識は次の周回で文字通り「知識」として動作選択する際に生かされていきます。

従ってイワシの知識として必要なデータ構造を1個体あたり下記のように設定しました。

【 AIいわし知識ベース 】
Pos x 現在のピクチャーボックス上X座標
y 現在のピクチャーボックス上Y座標
Vec x 次移動するX速度成分
y 次移動するY速度成分
CollisionStatus 0 進行方向右に壁あり(近接)
1 進行方向左に壁あり(近接)
2 進行方向中央に壁あり(近接)
3 進行方向右に壁あり
4 進行方向左に壁あり
5 進行方向中央に壁あり
6 進行方向右にポッドあり(近接)
7 進行方向左にポッドあり(近接)
8 進行方向中央にポッドあり(近接)
9 進行方向右にポッドあり
10 進行方向左にポッドあり
11 進行方向中央にポッドあり


「Pos」ならびに「Vec」は移動に必要な自分の位置と速度を表しております。
「CollisionStatus」は配列形式で、有りの状態では「1」、無しの状態は「0」が代入され、これを「知識」としてストックします。


この時の プログラムは下記のようになります。


【データ宣言部】

typedef struct { /* 座標系標準形 */
  double     x;
  double     y;
} Vector_str;


typedef struct Iwashi_tab {   /* iwashiの本体 */
  Vector_str   Pos;    /* iwashiの座標 */
  Vector_str   Vec;    /* 速度の成分 */
  unsigned char  CollisionStatus[20]; /* 衝突用ステータス */
} Iwashi_str;




【処理部】

/************************************************************************
*  衝突検知                              *
************************************************************************/
    void  collisionCheck(int index,Iwashi_str  *IwashiThis)
    {
      int       AreaPx,AreaPy;
      Vector_str   AreaTempPos;
      Vector_str   absPos,TopPos;
      double     RotAng;
      Color      pixcelColor;
      int       c_judge;
      double     a_sin,a_cos;
      double     diff;

      Vector_str   walPos_near;  /* 距離が近い方             */
      double     walDst_near;
      int       walLR_near;
      Vector_str   walPos_far;   /* 距離が遠い方             */
      double     walDst_far;
      int       walLR_far;

      Vector_str   podPos_near;  /* 距離が近い方             */
      double     podDst_near;
      int       podLR_near;
      Vector_str   podPos_far;   /* 距離が遠い方             */
      double     podDst_far;
      int       podLR_far;
      int       j;

      for(j=0; j<20; j++)
      {
        iwashi[index].CollisionStatus[j] = 0;
      }

      RotAng = atan2(iwashi[index].Vec.y, iwashi[index].Vec.x);
                      /* 半径線を算出するための角度算出    */

      a_sin = sin(RotAng);
      a_cos = cos(RotAng);

                      /* 接触面回避のための+3       ↓*/
      TopPos.x = (PodHalfSize + 3) * a_cos + iwashi[index].Pos.x;
      TopPos.y = (PodHalfSize + 3) * a_sin + iwashi[index].Pos.y;

      podPos_near.x  = TopPos.x;   /* 初期値がないと異常線発生       */
      podPos_near.y  = TopPos.y;
      podPos_far.x  = TopPos.x;
      podPos_far.y  = TopPos.y;

      RotAng += _COLLISION_VIEW_ANGLE;
                      /* 半径線を算出するための角度算出    */
      a_sin = sin(RotAng);
      a_cos = cos(RotAng);

      walDst_near = judge_distNear;
      walLR_near = 0;
      walDst_far = judge_distFar;
      walLR_far  = 0;

      podDst_near = judge_distNear;
      podLR_near = 0;
      podDst_far = judge_distFar;
      podLR_far  = 0;

      for (AreaPx=(-1) * (int)judge_width; AreaPx <= (int)judge_width; AreaPx++)
      {
        for (AreaPy=(-1) * (int)judge_height; AreaPy <= 0; AreaPy++)
        {
    /************************************************************************
    *  進行方向を基準とした検索マトリックス座標算出            *
    ************************************************************************/
          AreaTempPos.x = AreaPx * a_cos - AreaPy * a_sin;
          AreaTempPos.y = AreaPx * a_sin + AreaPy * a_cos;
                        /* 進行方向へマトリックス回転         */

          absPos.x = (AreaTempPos.x + TopPos.x);
          absPos.y = (AreaTempPos.y + TopPos.y);
    /************************************************************************
    *  左右の壁の反射                           *
    ************************************************************************/
          if (absPos.x < field.xMin)
          {              /* 範囲はポッド半径の符号に注意     */
            continue;
          }
          else if (absPos.x > field.xMax)
          {
            continue;
          }

    /************************************************************************
    *  上下の壁の反射                           *
    ************************************************************************/
          if (absPos.y < field.yMin)
          {
            continue;
          }
          else if (absPos.y > field.yMax)
          {
            continue;
          }

    /************************************************************************
    *  検索対象ピクセルの色をみる                     *
    ************************************************************************/
          try{
            pixcelColor = canvas->GetPixel((int)absPos.x , (int)absPos.y);
            c_judge = pixcelColor.R + pixcelColor.G + pixcelColor.B;
          }
          catch (Exception^)
          {
            c_judge = 0;
          }

    /************************************************************************
    *  検索対象ピクセルが白以外を探す                   *
    ************************************************************************/
          if (c_judge != 765)
          {

    /************************************************************************
    *  検索対象全表示                           *
    ************************************************************************/
            if (checkBox17->Checked == false)
            {
              gf->DrawLine(YellowPen, (int)TopPos.x, (int)TopPos.y, (int)absPos.x, (int)absPos.y);
            }

    /************************************************************************
    *  検索対象距離測定                          *
    ************************************************************************/
            diff = fabs(distanceTo((Vector_str *)&TopPos.x , (Vector_str *)&absPos.x));

    /************************************************************************
    *  検索対象が壁(緑色)の検索                     *
    ************************************************************************/
            if ((pixcelColor.R == 0)&&(pixcelColor.G != 0)&&(pixcelColor.B == 0))
            {        /* 壁を判定する       */
              if (judge_distFar >= diff)
              {
                if (judge_distNear >= diff)
                {    /* 距離が近い方(judge_distNear)の選択    */
                  if (walDst_near > diff)
                  {
                    walDst_near = diff;
                    walPos_near.x = absPos.x;
                    walPos_near.y = absPos.y;
                    if (AreaPx > 0)
                    {
                      walLR_near |= 1;
                    }
                    else  if (AreaPx < 0)
                    {
                      walLR_near |= 2;
                    }
                    else
                    {
                      walLR_near |= 4;
                    }
                  }
                }
                else
                {
                  if (walDst_far > diff)
                  {
                    walDst_far = diff;
                    walPos_far.x = absPos.x;
                    walPos_far.y = absPos.y;
                    if (AreaPx > 0)
                    {
                      walLR_far |= 1;
                    }
                    else  if (AreaPx < 0)
                    {
                      walLR_far |= 2;
                    }
                    else
                    {
                      walLR_far |= 4;
                    }
                  }
                }
              }
            }

    /************************************************************************
    *  検索対象が物体(青色)の検索                    *
    ************************************************************************/
            else if ((pixcelColor.R == 0)&&(pixcelColor.G == 0)&&(pixcelColor.B != 0))
            {
              if (judge_distFar >= diff)
              {
                if (judge_distNear >= diff)
                {    /* 距離が近い方(judge_distNear)の選択    */
                  if (podDst_near > diff)
                  {
                    podDst_near = diff;
                    podPos_near.x = absPos.x;
                    podPos_near.y = absPos.y;
                    if (AreaPx > 0)
                    {
                      podLR_near |= 1;
                    }
                    else  if (AreaPx < 0)
                    {
                      podLR_near |= 2;
                    }
                    else
                    {
                      podLR_near |= 4;
                    }
                  }
                }
                else
                {
                  if (podDst_far > diff)
                  {
                    podDst_far = diff;
                    podPos_far.x = absPos.x;
                    podPos_far.y = absPos.y;
                    if (AreaPx > 0)
                    {
                      podLR_far |= 1;
                    }
                    else  if (AreaPx < 0)
                    {
                      podLR_far |= 2;
                    }
                    else
                    {
                      podLR_far |= 4;
                    }
                  }
                }

              }
            }
          }
/************************************************************************
*  検索対象ピクセルを点で表示                     *
************************************************************************/
          if (checkBox10->Checked == false)
          {
             canvas->SetPixel((int)absPos.x , (int)absPos.y, Color::Black);
                /* 検索領域ピクセルを描画する    */
          }
        }
      }


      if (walLR_near != 0)
      {
    /************************************************************************
    *  他の壁を進行方向右に検出                      *
    ************************************************************************/
        if ((walLR_near & 0x01) != 0)
        {
    /************************************************************************
    *  進行方向右に壁                           *
    ************************************************************************/
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)walPos_near.x, (int)walPos_near.y);
          }
          iwashi[index].CollisionStatus[0] |= 0x01;

        }

    /************************************************************************
    *  進行方向左に壁                           *
    ************************************************************************/
        if ((walLR_near & 0x02) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)walPos_near.x, (int)walPos_near.y);
          }
          iwashi[index].CollisionStatus[1] |= 0x01;
        }

    /************************************************************************
    *  進行方向前に壁                           *
    ************************************************************************/
        if ((walLR_near & 0x04) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)walPos_near.x, (int)walPos_near.y);
          }
          iwashi[index].CollisionStatus[2] |= 0x01;
        }
      }


      if (walLR_far != 0)
      {
    /************************************************************************
    *  他の壁を進行方向右に検出                      *
    ************************************************************************/
        if ((walLR_far & 0x01) != 0)
        {
    /************************************************************************
    *  進行方向右に壁                           *
    ************************************************************************/
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)walPos_far.x, (int)walPos_far.y);
          }
          iwashi[index].CollisionStatus[3] |= 0x01;

        }

    /************************************************************************
    *  進行方向左に壁                           *
    ************************************************************************/
        if ((walLR_far & 0x02) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)walPos_far.x, (int)walPos_far.y);
          }
          iwashi[index].CollisionStatus[4] |= 0x01;
        }

    /************************************************************************
    *  進行方向前に壁                           *
    ************************************************************************/
        if ((walLR_far & 0x04) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)walPos_far.x, (int)walPos_far.y);
          }
          iwashi[index].CollisionStatus[5] |= 0x01;
        }
      }


      if (podLR_near != 0)
      {
    /************************************************************************
    *  他のポッドを進行方向右に検出                    *
    ************************************************************************/
        if ((podLR_near & 0x01) != 0)
        {
    /************************************************************************
    *  進行方向右にポッド                         *
    ************************************************************************/
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)podPos_near.x, (int)podPos_near.y);
          }
          iwashi[index].CollisionStatus[6] |= 0x01;

        }

    /************************************************************************
    *  進行方向左にポッド                         *
    ************************************************************************/
        if ((podLR_near & 0x02) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)podPos_near.x, (int)podPos_near.y);
          }
          iwashi[index].CollisionStatus[7] |= 0x01;
        }

    /************************************************************************
    *  進行方向前にポッド                         *
    ************************************************************************/
        if ((podLR_near & 0x04) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)podPos_near.x, (int)podPos_near.y);
          }
          iwashi[index].CollisionStatus[8] |= 0x01;
        }
      }


      if (podLR_far != 0)
      {
    /************************************************************************
    *  他のポッドを進行方向右に検出                    *
    ************************************************************************/
        if ((podLR_far & 0x01) != 0)
        {
    /************************************************************************
    *  進行方向右にポッド                         *
    ************************************************************************/
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)podPos_far.x, (int)podPos_far.y);
          }
          iwashi[index].CollisionStatus[9] |= 0x01;

        }

    /************************************************************************
    *  進行方向左にポッド                         *
    ************************************************************************/
        if ((podLR_far & 0x02) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)podPos_far.x, (int)podPos_far.y);
          }
          iwashi[index].CollisionStatus[10] |= 0x01;
        }

    /************************************************************************
    *  進行方向前にポッド                         *
    ************************************************************************/
        if ((podLR_far & 0x04) != 0)
        {
          if (checkBox20->Checked == false)
          {
            gf->DrawLine(BlackPen, (int)TopPos.x, (int)TopPos.y, (int)podPos_far.x, (int)podPos_far.y);
          }
          iwashi[index].CollisionStatus[11] |= 0x01;
        }
      }

    /************************************************************************
    *  シナプスに見立てたチェックボックスに表示              *
    ************************************************************************/
      if (index == 0)
      {
        for(j=0; j<20; j++)
        {
          if (iwashi[index].CollisionStatus[j] != 0)
          {
            switch(j)
            {
            case  0:
              checkBox1->Checked = true;
              break;
            case  1:
              checkBox2->Checked = true;
              break;
            case  2:
              checkBox3->Checked = true;
              break;
            case  3:
              checkBox4->Checked = true;
              break;
            case  4:
              checkBox5->Checked = true;
              break;
            case  5:
              checkBox6->Checked = true;
              break;
            case  6:
              checkBox7->Checked = true;
              break;
            case  7:
              checkBox11->Checked = true;
              break;
            case  8:
              checkBox12->Checked = true;
              break;
            case  9:
              checkBox13->Checked = true;
              break;
            case  10:
              checkBox14->Checked = true;
              break;
            case  11:
              checkBox15->Checked = true;
              break;
            }
          }
          else
          {
            switch(j)
            {
            case  0:
              checkBox1->Checked = false;
              break;
            case  1:
              checkBox2->Checked = false;
              break;
            case  2:
              checkBox3->Checked = false;
              break;
            case  3:
              checkBox4->Checked = false;
              break;
            case  4:
              checkBox5->Checked = false;
              break;
            case  5:
              checkBox6->Checked = false;
              break;
            case  6:
              checkBox7->Checked = false;
              break;
            case  7:
              checkBox11->Checked = false;
              break;
            case  8:
              checkBox12->Checked = false;
              break;
            case  9:
              checkBox13->Checked = false;
              break;
            case  10:
              checkBox14->Checked = false;
              break;
            case  11:
              checkBox15->Checked = false;
              break;
            }
          }
        }
      }

    }