プラットホーム

オセロゲームのプラットホームです。

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

プログラムの説明

  1. コンピューターは乱数で手を選ぶだけですが、プレイヤーの先手でゲームが出来ます。
    CSForm.cs のソースコードです。
    // Osero Platform
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Collections;
    using System.Diagnostics;
    
    public class MyForm : Form
    {
        int[,] m_t = new int[8, 8]
        {{  0,  0,  0,  0,  0,  0,  0,  0 },
         {  0,  0,  0,  0,  0,  0,  0,  0 },
         {  0,  0,  0,  0,  0,  0,  0,  0 },
         {  0,  0,  0,  1, -1,  0,  0,  0 },
         {  0,  0,  0, -1,  1,  0,  0,  0 },
         {  0,  0,  0,  0,  0,  0,  0,  0 },
         {  0,  0,  0,  0,  0,  0,  0,  0 },
         {  0,  0,  0,  0,  0,  0,  0,  0 }};
    
        int     m_teban= 0;
        int     m_pos;
    
        // Constructor
        public MyForm()
        {
            BackColor = SystemColors.AppWorkspace;
            Width  = 560;
            Height = 600;
            Paint += new PaintEventHandler(MyHandler);
            MouseDown += new MouseEventHandler(OnMyMouseDown);
        }
    
        // オセロ盤の描画
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawImage(new Bitmap("c:\\data\\test\\ban.gif"), new PointF(10F, 10F));
            for (int y = 0; y < 8; y++)
            {
                for (int x = 0; x < 8; x++)
                {
                    if (m_t[y, x] == 1) g.DrawImage(new Bitmap("c:\\data\\test\\koma_b.gif"), new PointF(x * 61 + 28, y * 61 + 24));
                    if (m_t[y, x] == -1) g.DrawImage(new Bitmap("c:\\data\\test\\koma_w.gif"), new PointF(x * 61 + 28, y * 61 + 24));
                }
            }
            m_teban= Pass(m_teban);
        }
    
        // マウスのクリック
        private void OnMyMouseDown(object sender, MouseEventArgs e)
        {
            int     x,y;
            if (m_teban==9)
            {   Pass(9);
                return;
            }
            x = (e.X - 28) / 61;
            y = (e.Y - 24) / 61;
            if (m_teban==-1)
            {   m_pos= Think(m_teban, m_t);
                y= m_pos/10;
                x= m_pos%10;
            }
            if (Reverse(m_teban, x, y, m_t)==true)
            {   m_teban = 0-m_teban;
                Invalidate();
                return;
            }
            string str = "Error Play  C:" + m_teban + "[" + y + "," + x + "]";
            MessageBox.Show(str);
        }
    
        // パス, 終局を調べる
        int Pass(int c)
        {
            int         w;
            ArrayList   array;
            if (c==0)   return 1;   // 黒番で Start
            if (c!=9)               // パスを調べる
            {   array = Search(c, m_t);
                if (array.Count>0)  return c;
            }
            w= 0-c;                 // 反手番で調べる
            array = Search(w, m_t);
            if (array.Count>0)
            {   if (c==1)
                {   MessageBox.Show("黒はパスです");  }
                else
                {   MessageBox.Show("白はパスです");  }
                return w;           // 手番を反転
            }
            string str= "★Game Over  黒:" + Count(1, m_t) + "  白:" + Count(-1, m_t);
            MessageBox.Show(str);
            return 9;       // 終局
        }
    
        // t[yp,xp] に打てるか調べる
        bool  Check(int c, int xp, int yp, int[,] t)
        {
            int     i,j,x,y;
            int nc = 0-c;
            if (yp>7 || xp>7 || yp<0 || xp<0 || c==0 || t[yp,xp]!=0)   return false;
            for(i=-1; i<2; i++)
                for(j=-1; j<2; j++)
                {   y = yp + i;     // 上下左右を調べる
                    x = xp + j;
                    if (y<8 && x<8 && y>=0 && x>=0 && t[y,x]==nc)
                    {   for(; y<8 && x<8 && y>=0 && x>=0 && t[y,x]==nc; y+=i,x+=j);
                        if (y<8 && x<8 && y>=0 && x>=0 && t[y,x]==c)    return true;
                    }
                }
            return false;   //置くことができない
        }
        bool  Check(int c, int pos, int[,] t)
        {
            int x,y;
            y= pos/10;
            x= pos%10;
            return Check(c,x,y,t);
        }
    
        // t[y,x] に駒を置いて、挟んだ駒を裏返す
        bool Reverse(int c, int xp, int yp, int[,] t)
        {
            int i, j, x, y;
            bool sw = false;
            int  nc = 0-c;
            if (yp > 7 || xp > 7 || yp < 0 || xp < 0 || c == 0 || t[yp, xp] != 0) return false;
            for (i = -1; i < 2; i++)
                for (j = -1; j < 2; j++)
                {
                    y = yp + i;
                    x = xp + j;
                    if (y < 8 && x < 8 && y >= 0 && x >= 0 && t[y, x] == nc)
                    {
                        for (; y < 8 && x < 8 && y >= 0 && x >= 0 && t[y, x] == nc; y += i, x += j) ;
                        if (y < 8 && x < 8 && y >= 0 && x >= 0 && t[y, x] == c)
                        {
                            for (; y != yp || x != xp; y -= i, x -= j) t[y, x] = c;
                            sw = true;
                        }
                    }
                }
            if (sw == false) return false;
            t[yp, xp] = c;
            return true;
        }
        bool Reverse(int c, int pos, int[,] t)
        {
            int x,y;
            y= pos/10;
            x= pos%10;
            return Reverse(c,x,y,t);
        }
    
        // 打てる手をサーチ
        ArrayList Search(int c, int[,] t)
        {
            int     x,y,pos;
            ArrayList   array= new ArrayList();
            for(y=0; y<8; y++)
                for(x=0; x<8; x++)
                {   if (Check(c,x,y,t))
                    {   pos= y*10 + x;
                        array.Add(pos);
                    }
                }
            return array;
        }
    
        // 駒のカウント
        int Count(int c, int[,] t)
        {
            int cnt;
            cnt = 0;
            for (int y = 0; y < 8; y++)
            {
                for (int x = 0; x < 8; x++)
                    if (t[y, x] == c) cnt++;
            }
            return cnt;
        }
    
        void t_Log(int sz, int[,] t)
        {
            string str = "";
            for (int y = 0; y < sz; y++)
            {
                for (int x = 0; x < sz; 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";
            }
            Debug.Write(str);
        }
    
        // 乱数でプレイ
        int Think(int c, int[,] t)
        {
            int     num;
            Random  rand = new Random();
            ArrayList   array= Search(c, t);
            if (array.Count==0) return 99;
            num = rand.Next(array.Count);
            return((int)array[num]);
        }
    }
    
    class osero
    {
        public static void Main()
        {
            MyForm mf = new MyForm();
            Application.Run(mf);
        }
    }
    
  2. 駒を裏返す で説明した項目は省略します。
    m_teban でゲームの進行を管理します。
    m_teban 説明
    0 初期値(Pass 関数で 1 に設定)
    1 黒(プレイヤー)の手番
    -1 白(コンピュータ)の手番
    9 終局(盤上の駒を数えて表示)
  3. MyHandler() 関数で盤と駒を描画します。
    描画が終わると Pass() 関数で終局と手番のパスを調べます。
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawImage(new Bitmap("c:\\data\\test\\ban.gif"), new PointF(10F, 10F));
            for (int y = 0; y < 8; y++)
            {
                for (int x = 0; x < 8; x++)
                {
                    if (m_t[y, x] == 1) g.DrawImage(new Bitmap("c:\\data\\test\\koma_b.gif"), new PointF(x * 61 + 28, y * 61 + 24));
                    if (m_t[y, x] == -1) g.DrawImage(new Bitmap("c:\\data\\test\\koma_w.gif"), new PointF(x * 61 + 28, y * 61 + 24));
                }
            }
            m_teban= Pass(m_teban);
        }
    
  4. パスと終局を調べる Pass() 関数です。
    c==0 の時は、黒先(1) でゲームを開始します。
    c!=9 の時は、手番で打つ手があるかを調べます。
    手番がパスのときは、手番を変えて打つ手を調べます。
    手番を変えて打つ手があるときは手番を切り替えます。
    黒,白共に打つ箇所が無ければ終局で、盤上の駒を数えて表示します。
        int Pass(int c)
        {
            int         w;
            ArrayList   array;
            if (c==0)   return 1;   // 黒番で Start
            if (c!=9)               // パスを調べる
            {   array = Search(c, m_t);
                if (array.Count>0)  return c;
            }
            w= 0-c;                 // 反手番で調べる
            array = Search(w, m_t);
            if (array.Count>0)
            {   if (c==1)
                {   MessageBox.Show("黒はパスです");  }
                else
                {   MessageBox.Show("白はパスです");  }
                return w;           // 手番を反転
            }
            string str= "★Game Over  黒:" + Count(1, m_t) + "  白:" + Count(-1, m_t);
            MessageBox.Show(str);
            return 9;       // 終局
        }
    
  5. マウスのクリックでゲームを進行します。
    コンピュータの手番のときは Think() 関数で打つ手を決めます。
    Reverse() 関数で挟んだ駒を裏返して手番を切り替えます。
    Invalidate() でオセロ盤を再描画します。
    Reverse() 関数の戻り値が false のときは置くことが出来ません。
        private void OnMyMouseDown(object sender, MouseEventArgs e)
        {
            int     x,y;
            if (m_teban==9)
            {   Pass(9);
                return;
            }
            x = (e.X - 28) / 61;
            y = (e.Y - 24) / 61;
            if (m_teban==-1)
            {   m_pos= Think(m_teban, m_t);     // Think() 関数で打つ手を決める
                y= m_pos/10;
                x= m_pos%10;
            }
            if (Reverse(m_teban, x, y, m_t)==true)
            {   m_teban = 0-m_teban;
                Invalidate();
                return;
            }
            string str = "Error Play  C:" + m_teban + "[" + y + "," + x + "]";
            MessageBox.Show(str);
        }
    
  6. コンピュータが打つ手を決める Think() 関数です。
    Search() 関数で打てる手を列挙して、乱数で手を決めます。
    パスのときは 9,9 で盤の外を設定します。
        int Think(int c, int[,] t)
        {
            int     num;
            Random  rand = new Random();
            ArrayList   array= Search(c, t);
            if (array.Count==0) return 99;
            num = rand.Next(array.Count);
            return((int)array[num]);
        }
    
  7. Search() 関数は c の手番で打てる手を列挙する関数です。
    Check() 関数で打てるか否かを調べて ArrayList に登録します。
        ArrayList Search(int c, int[,] t)
        {
            int     x,y,pos;
            ArrayList   array= new ArrayList();
            for(y=0; y<8; y++)
                for(x=0; x<8; x++)
                {   if (Check(c,x,y,t))
                    {   pos= y*10 + x;
                        array.Add(pos);
                    }
                }
            return array;
        }
    
  8. Check() 関数で xp, yp の座標に駒を置けるかを調べます。
    相手の駒を挟むことが出来れば置くことが出来ます。
        bool  Check(int c, int xp, int yp, int[,] t)
        {
            int     i,j,x,y;
            int     nc = 0-c;
            if (yp>7 || xp>7 || yp<0 || xp<0 || c==0 || t[yp,xp]!=0)   return false;
            for(i=-1; i<2; i++)
                for(j=-1; j<2; j++)
                {   y = yp + i;     // 上下左右を調べる
                    x = xp + j;
                    if (y<8 && x<8 && y>=0 && x>=0 && t[y,x]==nc)
                    {   for(; y<8 && x<8 && y>=0 && x>=0 && t[y,x]==nc; y+=i,x+=j);
                        if (y<8 && x<8 && y>=0 && x>=0 && t[y,x]==c)    return true;
                    }
                }
            return false;   //置くことができない
        }
        bool  Check(int c, int pos, int[,] t)
        {
            int x,y;
            y= pos/10;
            x= pos%10;
            return Check(c,x,y,t);
        }
    
  9. t_Log() 関数は、デバッグの為に出力ウインドウに局面を表示する関数で、無くても構いません。
        void t_Log(int sz, int[,] t)
        {
            string str = "";
            for (int y = 0; y < sz; y++)
            {
                for (int x = 0; x < sz; 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";
            }
            Debug.Write(str);
        }
    

[Next Chapter ↓] Final 関数
[Previous Chapter ↑] 駒を裏返す

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