三目並べの自動学習

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 座標
        public int v=0;     // 点数
    }
    class Pos
    {   public int y;       // Y 座標
        public int x;       // X 座標
    }
    
    class Prog
    {   static int[,] m_t = new int[3, 3];  // 盤
        static int m_num;                   // プレイのIDX
    
        public static void Main()
        {   int[,] dat = new int[9, 2];     // 手の記録
            Pos ps;
            Cell top = new Cell();
            Cell pt;
            int i, j, k, ret;
    
            for (j = 0; j < 10000; j++)     // 10000 回繰り返す
            {   ret = 0;
                Init();
                for (m_num=0; m_num<9; m_num++)
                {   ps = Play(m_num, m_t);
                    if (m_num % 2 == 0) m_t[ps.y, ps.x] = 1;
                    else m_t[ps.y, ps.x] = -1;
                    dat[m_num, 0] = ps.y;
                    dat[m_num, 1] = ps.x;
                    ret = Fine(m_t);
                    if (ret != 0) break;
                }
                pt = top;
                for (i=0; i<m_num; i++)
                {   k = AddCell(pt, i, dat[i, 0], dat[i, 1]);
                    pt = (Cell)pt.AL[k];
                    pt.v += ret;
                }
                Console.WriteLine("Number:" + j);
                //Print(top);
                //Console.ReadLine();
            }
        }
        // 乱数で選択
        public static Pos Play(int num, int[,] t)
        {   Random rand = new Random(); //シード値で初期化
            Pos p = new Pos();
            int r;
            r= rand.Next(9-num);
            for(p.y=0; p.y<3; p.y++)
                for(p.x=0; p.x<3; p.x++)
                {   if (t[p.y, p.x] == 0)
                    {   if (r<1)    return p;
                        r--;
                    }
                }
            return p;
        }
        // 終了判定
        public static int Fine(int[,] t)
        {   int[] tw = new int[8];
            int     i;
            for(i=0; i<8; i++)  tw[i] = 0;
            for(i=0; i<3; i++)
            {   tw[0] += t[0,i];
                tw[1] += t[1,i];
                tw[2] += t[2,i];
                tw[3] += t[i,0];
                tw[4] += t[i,1];
                tw[5] += t[i,2];
                tw[6] += t[i,i];
                tw[7] += t[i,2-i];
            }
            for(i=0; i<8; i++)
            {   if (tw[i]==3)   return 1;
                if (tw[i]==-3)  return -1;
            }
            return 0;
        }
        // ゲームの初期化
        public static void Init()
        {   int  x,y;
            for(y=0; y<3; y++)
                for(x=0; x<3; x++)
                    m_t[y,x] = 0;
            m_num= 0;
        }
        // pt の ArrayList に i, y, x のセルを追加する
        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;
        }
        // 局面の印字
        public static void Disp(int[,] t)
        {   int     x,y;
            string  str= "";
            for(y=0; y<3; y++)
            {   for(x=0; x<3; x++)
                {   if (t[y,x]==0)  str= str + ".";
                    if (t[y,x]==1)  str= str + "X";
                    if (t[y,x]==-1) str= str + "O";
                }
                str= str + "\n";
            }
            Console.Write(str);
        }
        // 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 + " V:" + wk.v);
                if (wk.AL != null)  Print(wk);  // AL[i] から Cell にリンク
            }
        }
    }
    
  2. 少数の対局データでは全く役に立ちませんが、多くの対局データを学習します。
    多くの対局データを集めるのは大変かも知れませんが、コンピュータ同士て自動対局させれば簡単でしょう。
    Cell 構造体(Class)に加えて、座標を表す Pos 構造体を定義します。
    class Cell
    {   public ArrayList AL = null;
        public int n;       // N 手目
        public int y;       // Y 座標
        public int x;       // X 座標
        public int v=0;     // 点数
    }
    class Pos
    {   public int y;       // Y 座標
        public int x;       // X 座標
    }
    
  3. m_t[3,3] は三目並べゲームの盤です。
    m_num は何手目かを示す dat(ゲームの記録)の Index です。
    Cell top は何も記録されていない状態から始めます。
    class Prog
    {   static int[,] m_t = new int[3, 3];  // 盤
        static int m_num;                   // プレイのIDX
    
        public static void Main()
        {
            int[,] dat = new int[9, 2];     // 手の記録
            Pos ps;
            Cell top = new Cell();
    
  4. 今回は1万回ゲームをしてその結果を Cell top から連鎖します。
    1万局も登録すれば何とかなると思っていたのですが、考えが甘かったようです。 (^_^;)
    Init() はゲームを初期化する関数です。
            for (j = 0; j < 10000; j++)
            {   ret = 0;
                Init();
    
  5. m_num を0~8まで(1ゲーム)繰り返します。
    Play() 関数がコンピュータが自動的に手を決める関数で、偶数,奇数で黒と白の駒を置きます。
    dat[9,2] に打った手を記録します。
    Fine() 関数はゲームの終了(3目並んだ)を判定する関数です。
                for(m_num=0; m_num<9; m_num++)
                {   ps = Play(m_num, m_t);
                    if (m_num % 2 == 0) m_t[ps.y, ps.x] = 1;
                    else m_t[ps.y, ps.x] = -1;
                    dat[m_num, 0] = ps.y;
                    dat[m_num, 1] = ps.x;
                    ret = Fine(m_t);
                    if (ret != 0) break;
                }
    
  6. 1ゲームが終わるごとに Cell top から連鎖した構造体に結果を登録します。
    AddCell() 関数で一手登録します。
    通過した Cell の v に点数(ret)を加算します。
    ret の値は「1:黒の勝ち, 2:白の勝ち, 0:引き分け」になっています。
    Console.WriteLine(), Print(), Console.ReadLine() はデバッグの為のソースコードです。
                pt = top;
                for (i=0; i<m_num; i++)
                {   k = AddCell(pt, i, dat[i, 0], dat[i, 1]);
                    pt = (Cell)pt.AL[k];
                    pt.v += ret;
                }
                Console.WriteLine("Number:" + j);
                //Print(top);
                //Console.ReadLine();
            }
    
  7. コンピュータが自動的に打つ手を決める関数です。
    乱数で空いている箇所を選択して Pos でリターンするだけです。
    num は現在の手数です。
        public static Pos Play(int num, int[,] t)
        {   Random rand = new Random(); //シード値で初期化
            Pos p = new Pos();
            int r;
            r= rand.Next(9-num);
            for(p.y=0; p.y<3; p.y++)
                for(p.x=0; p.x<3; p.x++)
                {   if (t[p.y, p.x] == 0)
                    {   if (r<1)    return p;
                        r--;
                    }
                }
            return p;
        }
    
  8. ゲームの終了(3目並んでいる)を判定する Fine() 関数です。
    縦,横,斜めに3目並んだかを調べて「1:黒の勝ち, -1:白の勝ち, 0:ゲーム中」をリターンします。
        public static int Fine(int[,] t)
        {   int[] tw = new int[8];
            int     i;
            for(i=0; i<8; i++)  tw[i] = 0;
            for(i=0; i<3; i++)
            {   tw[0] += t[0,i];
                tw[1] += t[1,i];
                tw[2] += t[2,i];
                tw[3] += t[i,0];
                tw[4] += t[i,1];
                tw[5] += t[i,2];
                tw[6] += t[i,i];
                tw[7] += t[i,2-i];
            }
            for(i=0; i<8; i++)
            {   if (tw[i]==3)   return 1;
                if (tw[i]==-3)  return -1;
            }
            return 0;
        }
    
  9. ゲームを初期化する Init() 関数です。
        // ゲームの初期化
        public static void Init()
        {   int  x,y;
            for(y=0; y<3; y++)
                for(x=0; x<3; x++)
                    m_t[y,x] = 0;
            m_num= 0;
        }
    
  10. pt の ArrayList にセルを追加する AddCell() 関数です。
    ArrayList を連鎖して、ゲームの手を学習します。
        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;
        }
    
  11. Disp() は3目並べの盤を印字する関数ですが、直接ゲームには関係しません。
    Print(Cell top) は Cell に記録されたデータを印字する関数ですが、直接ゲームには関係しません。
  12. 登録時間が気になったのですが、1万件の登録は瞬間に完了しました。
    これでデータを学習する準備が整ったので、後は三目並べゲームに応用するだけです。
    今までは Console Mode のプログラムですが、ゲームは Windows Mode でプログラムします。

[Next Chapter ↓] 三目並べゲーム
[Previous Chapter ↑] 三目並べの学習

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