三目並べの学習

C# で ArrayList を使って三目並べゲームの手を学習します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. ArrayList を使って、三目並べゲームの手(勝ち方)を学習します。
    //★ 三目並べの学習機能    前田 稔 ★
    using System;
    using System.Collections;   //ArrayList を使うとき
    class Cell
    {   public ArrayList AL = null;
        public int n;   // N 手目
        public int y;   // Y 座標
        public int x;   // X 座標
    }
    
    class Prog
    {
        static int[,] dat = new int[9, 2]
        {{1,1}, {0,0}, {0,2}, {2,0}, {1,0}, {1,2}, {0,1}, {2,1}, {2,2}};
    
        public static void Main()
        {
            Cell top = new Cell();
            Cell pt;
            int i,j;
    
            pt = top;
            for(i=0; i<dat.GetLength(0); i++)
            {
                j = AddCell(pt,i,dat[i,0],dat[i,1]);
                pt = (Cell)pt.AL[j];
            }
            Print(top);
            Console.ReadLine(); 
        }
    
        // pt の ArrayList に i, y, x のセルを追加する(x,y が存在するときはパス)
        public static int AddCell(Cell pt, int i, int y, int x)
        {   Cell wk,wk2;
            int  j;
            wk = new Cell();
            wk.n = i;
            wk.y = y;
            wk.x = x;
            if (pt.AL==null)    pt.AL= new ArrayList();
            for (j=0; j<pt.AL.Count; j++)
            {   wk2 = (Cell)pt.AL[j];
                if (wk2.y==y && wk2.x==x) break;
            }
            if (j>=pt.AL.Count) pt.AL.Add(wk);
            return j;
        }
    
        // top セルから再帰で印字
        public static void Print(Cell top)
        {   Cell wk;
            int  i;
            for (i=0; i<top.AL.Count; i++)      // ArrayList(手番で列挙)の印字
            {   wk = (Cell)top.AL[i];
                Console.WriteLine("N:" + wk.n + " Y:" + wk.y + " X:" + wk.x);
                if (wk.AL != null)  Print(wk);  // AL[i] から Cell にリンク
            }
        }
    }
    
  2. 将棋や囲碁でコンピュータがプロを負かすほど強くなってきました。
    その元となる考え方がディープラーニングと呼ばれるコンピュータの学習機能です。
    学習機能の本質に少しでも触れられるように、三目並べゲームを材々にプログラミングしてみましょう。
    dat が三目並べゲームの一局のデータで、これを Cell に記憶します。
        static int[,] dat = new int[9, 2]
        {{1,1}, {0,0}, {0,2}, {2,0}, {1,0}, {1,2}, {0,1}, {2,1}, {2,2}};
    
  3. Cell top は何も記録されていない状態から始めます。
    dat のデータを一手ずつ AddCell() 関数で登録します。
    top が指す ArrayList には、三目並べゲームの一手目(最大 3*3=9個)が記憶されます。
    二手目は ArrayList の一手目からリンクされている Cell の ArrayList に最大8個(一手目を除く)が記憶されます。
        public static void Main()
        {   Cell top = new Cell();
            Cell pt;
            int i,j;
            pt = top;
            for(i=0; i<dat.GetLength(0); i++)
            {   j = AddCell(pt,i,dat[i,0],dat[i,1]);
                pt = (Cell)pt.AL[j];
            }
            Print(top);
            Console.ReadLine(); 
        }
    
  4. AddCell() 関数では pt が指し示す ArrayList に i, y, x のセルを追加します。
    既に x,y の手が登録されているときはパスします。
        public static int AddCell(Cell pt, int i, int y, int x)
        {   Cell wk,wk2;
            int  j;
            wk = new Cell();
            wk.n = i;
            wk.y = y;
            wk.x = x;
            if (pt.AL==null)    pt.AL= new ArrayList();
            for (j=0; j<pt.AL.Count; j++)
            {   wk2 = (Cell)pt.AL[j];
                if (wk2.y==y && wk2.x==x) break;
            }
            if (j>=pt.AL.Count) pt.AL.Add(wk);
            return j;
        }
    

複数対局の登録

  1. dat を3次元配列にして複数の対局データを学習させます。
        static int[,,] dat = new int[3, 9, 2]
        {{{1,1}, {0,0}, {0,2}, {2,0}, {1,0}, {1,2}, {0,1}, {2,1}, {2,2}},
         {{1,1}, {0,0}, {0,2}, {2,0}, {1,0}, {1,2}, {2,1}, {0,1}, {2,2}},
         {{1,1}, {2,2}, {0,2}, {2,0}, {2,1}, {0,1}, {1,2}, {1,0}, {0,0}}};
    
  2. 3次元配列で定義した複数の対局データを学習するソースコードです。
        public static void Main()
        {   Cell top = new Cell();
            Cell pt;
            int i,j,k;
            for(i=0; i<dat.GetLength(0); i++)
            {   pt = top;
                for(j=0; j<dat.GetLength(1); j++)
                {   k = AddCell(pt,j,dat[i,j,0],dat[i,j,1]);
                    pt = (Cell)pt.AL[k];
                }
            }
            Print(top);
            Console.ReadLine(); 
        }
    
  3. 少数の対局データでは全く役に立ちませんが、多くの対局データを集めて学習します。
    多くの対局データを集めるのは大変かも知れませんが、コンピュータ同士で自動対局させれば簡単でしょう。

点数を追加

  1. Cell に点数を追加します。
    class Cell
    {   public ArrayList AL = null;
        public int n;       // N 手目
        public int y;       // Y 座標
        public int x;       // X 座標
        public int v=0;     // 点数
    }
    
  2. dat は4局分の対局データで val はその結果(勝敗)です。
    勝敗は「黒勝ち=1,白勝ち=-1,引き分け=0」になっていて、1局目は引き分け、2局目と3局目が黒勝ち、4局目が白勝ちです。
        static int[,,] dat = new int[4, 9, 2]
        {{{1,1}, {0,0}, {0,2}, {2,0}, {1,0}, {1,2}, {0,1}, {2,1}, {2,2}},
         {{1,1}, {0,1}, {2,2}, {0,0}, {0,2}, {2,0}, {1,2}, {1,0}, {2,1}},
         {{1,1}, {2,1}, {2,2}, {0,0}, {0,2}, {2,0}, {1,2}, {1,0}, {0,1}},
         {{0,0}, {2,2}, {2,0}, {1,0}, {0,1}, {0,2}, {1,1}, {1,2}, {2,1}}};
        static int[] val = { 0, 1, 1, -1 };
    
  3. 1手目から9手目までの手順に従って、通過した Cell の v に点数を加算します。
            for(i=0; i<dat.GetLength(0); i++)
            {   pt = top;
                for(j=0; j<dat.GetLength(1); j++)
                {   k = AddCell(pt,j,dat[i,j,0],dat[i,j,1]);
                    pt = (Cell)pt.AL[k];
                    pt.v += val[i];
                }
            }
    
  4. Print() 関数のセルの印字に、点数(v)を追加してます。
        public static void Print(Cell top)
        {   Cell wk;
            int  i;
            for (i=0; i<top.AL.Count; i++)      // ArrayList(手番で列挙)の印字
            {   wk = (Cell)top.AL[i];
                Console.WriteLine("N:" + wk.n + " Y:" + wk.y + " X:" + wk.x + " V:" + wk.v);
                if (wk.AL != null)  Print(wk);  // AL[i] から Cell にリンク
            }
        }
    
  5. 少数の対局データでは全く役に立ちませんが、多くの対局データを学習すれば威力を発揮します。
    当初1万局ぐらいのデータを登録すれば何とかなると思っていたのですが、考えが甘かったようです。 (^_^;)
    対局に利用するためには、1手ごとに「この手で負けた! この手で勝った!」の勝率を記録します。
    そして実際の対局では、最も有利な手をコンピュータが選びます。

[Next Chapter ↓] 三目並べの自動学習
[Previous Chapter ↑] ゲームの手を記憶

超初心者のプログラム入門(C# Frame Work)