challenge マルバツゲーム

マルバツゲームは3×3の格子に交互に○と×を書き込み、先に縦・横・斜めに記号をそろえたほうが勝ちというおなじみのゲームです。

「毎ターン乱数を使って手を決めるランダムプレイヤー同士を対戦させる」というのが今回のお題です。 1万回対戦させ、勝ち・負け・引き分けの数を表示してください。 そして先手が有利であることを確かめてください。

良い手を思考するプレイヤーについては別のお題にしようと思っています。 プレイヤーを簡単に差し換えることができる設計を目指してください。

Posted feedbacks - C#

IPlayerBaseインターフェースを実装していればプレーヤーになれます。
勝敗判定の部分をもっとエレガントに書きたいですね。

結果
○ WIN : 5837
× WIN : 2846
  DRAW : 1317

○ WIN : 5863
× WIN : 2924
  DRAW : 1213

○ WIN : 5811
× WIN : 2895
  DRAW : 1294
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
using System.Collections.Generic;
using System.Linq;

namespace どう書く_org_マルバツゲ {
    class Program {
        static void Main(string[] args) {
            int maruWin = 0;
            int batuWin = 0;
            int draw = 0;
            IPlayerBase maru = new Player() { Name = "○" };
            IPlayerBase batu = new Player() { Name = "×" };

            for(int i = 0; i < 10000; i++) {
                Game game = new Game(maru, batu);
                switch(game.Start()) {
                case "○":
                    maruWin++;
                    break;
                case "×":
                    batuWin++;
                    break;
                case "draw":
                    draw++;
                    break;
                }
            }

            Console.WriteLine("○ WIN : " + maruWin.ToString());
            Console.WriteLine("× WIN : " + batuWin.ToString());
            Console.WriteLine("  DRAW : " + draw.ToString());

            Console.ReadLine();
        }
    }

    class Game {
        private IPlayerBase _maru;
        private IPlayerBase _batu;

        public string[] Board = new string[] {
            "□□□",
            "□□□",
            "□□□" };

        public Game(IPlayerBase maru, IPlayerBase batu) {
            _maru = maru;
            _maru.Game = this;
            _batu = batu;
            _batu.Game = this;
        }

        public string Start() {
            string jadge = "";
            while(true) {
                _maru.Play();
                jadge = Judge(_maru.Name);
                if(jadge != "") {
                    break;
                }
                _batu.Play();
                jadge = Judge(_batu.Name);
                if(jadge != "") {
                    break;
                }
            }

            return jadge;
        }

        //勝者を判定
        private string Judge(string name) {
            string name3 = name + name + name;
            //横
            foreach(string str in Board) {
                if(str == name3) {
                    return name;
                }
            }

            //縦
            for(int x = 0; x < 3; x++) {
                string str = "";
                for(int y = 0; y < 3; y++) {
                    str += Board[y][x];
                }
                if(str == name3) {
                    return name;
                }
            }

            //斜め
            string tmp = Board[0][0].ToString() + Board[1][1].ToString() + Board[2][2].ToString();
            if(tmp == name3) {
                return name;
            }
            tmp = Board[0][2].ToString() + Board[1][1].ToString() + Board[2][0].ToString();
            if(tmp == name3) {
                return name;
            }

            //引き分け
            if(!(Board[0] + Board[1] + Board[2]).Contains('□')) {
                return "draw";
            }
            return "";
        }
    }

    class Player :IPlayerBase {
        #region IPlayerBase メンバ

        public Game Game { get; set; } //参加しているゲーム

        public string Name { get; set; } //○か×か

        private Random Rnd = new Random();
        //一手打つ
        public void Play() {
            List<Location> cells = new List<Location>(); //置けるマス

            //置けるマスを列挙
            for(int y = 0; y < 3; y++) {
                for(int x = 0; x < 3; x++) {
                    if(Game.Board[y][x] == '□') {
                        Location location = new Location();
                        location.x = x;
                        location.y = y;

                        cells.Add(location);
                    }
                }
            }

            //置けるマスが無ければ何もしない
            if(cells.Count == 0) return;

            //乱数で置くマスを決定
            int index = Rnd.Next(cells.Count);
            Location cell = cells[index];

            //石を置く
            Game.Board[cell.y] = Game.Board[cell.y].Remove(cell.x, 1).Insert(cell.x, Name);
        }
        #endregion
    }

    //マスの位置
    struct Location {
        public int x;
        public int y;
    }

    //プレーヤーが実装するインターフェース
    //これを実装したプレーヤーに差し替え可
    interface IPlayerBase {
        Game Game { set; get; }
        string Name { set; get; }
        void Play();
    }
}

PlayerはIPlayerを継承してTacticseを実装すれば置き換え可能です。

LocalRandomPlayerとMemberRandomPlayerといますが、
両方ともランダムで場所を決めていて、
Randomのインスタンス生成をクラス内で保持してる(Member)か、
関数内で保持してる(Local)かの違いです。

/*実行結果
開始時間:2008年4月25日 17:54:06
1回目
---LocalRandomPlayer---
○Win   :5903
×Win   :3103
Draw    :994

2回目
---LocalRandomPlayer---
○Win   :5492
×Win   :3073
Draw    :1435

3回目
---LocalRandomPlayer---
○Win   :5693
×Win   :3351
Draw    :956

4回目
---LocalRandomPlayer---
○Win   :5681
×Win   :3228
Draw    :1091

5回目
---LocalRandomPlayer---
○Win   :6277
×Win   :2812
Draw    :911

終了時間:2008年4月25日 17:54:13
開始時間:2008年4月25日 17:55:21
1回目
---MemberRandomPlayer---
○Win   :5565
×Win   :3202
Draw    :1233

2回目
---MemberRandomPlayer---
○Win   :5490
×Win   :3240
Draw    :1270

3回目
---MemberRandomPlayer---
○Win   :5606
×Win   :3130
Draw    :1264

4回目
---MemberRandomPlayer---
○Win   :5456
×Win   :3207
Draw    :1337

5回目
---MemberRandomPlayer---
○Win   :5482
×Win   :3199
Draw    :1319

終了時間:2008年4月25日 17:55:21
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
using System;
using System.Collections.Generic;

/// <summary>
/// 座標構造体
/// </summary>
struct Point
{
    public int x;
    public int y;
    public Point(int x, int y) { this.x = x; this.y = y; }
}

/// <summary>
/// プレイヤーインターフェース
/// </summary>
interface IPlayer
{
    /// <summary>
    /// fieldにおける座標を返す。
    /// </summary>
    /// <param name="field">現在のフィールド</param>
    /// <param name="pointList">おけることのできる座標</param>
    /// <returns></returns>
    Point Tacticse(ref int[,] field, ref List<Point> pointList);
}

class LocalRandomPlayer
    : IPlayer
{
    public Point Tacticse(ref int[,] field, ref List<Point> pointList)
    {
        Random rnd = new Random();
        int n = rnd.Next(pointList.Count);
        Point tmp = pointList[n];
        pointList.RemoveAt(n);
        return tmp;
    }
}

class MemberRandomPlayer
    : IPlayer
{
    private Random rnd;

    public MemberRandomPlayer()
    {
        rnd = new Random();
    }

    public Point Tacticse(ref int[,] field, ref List<Point> pointList)
    {
        int n = rnd.Next(pointList.Count);
        Point tmp = pointList[n];
        pointList.RemoveAt(n);
        return tmp;
    }
}

class Judge
{
    private int[,] field;
    private IPlayer Maru;
    private IPlayer Batu;
    private List<Point> pointList;
    private const int FIELD_SIZE = 3;

    public Judge(IPlayer m, IPlayer b)
    {
        Maru = m;
        Batu = b;
    }

    /// <summary>
    /// 初期化
    /// </summary>
    private void Init()
    {
        field = new int[FIELD_SIZE, FIELD_SIZE];
        pointList = new List<Point>();

        for (int i = 0; i < FIELD_SIZE; i++)
        {
            for (int j = 0; j < FIELD_SIZE; j++)
            {
                pointList.Add(new Point(i, j));
            }            
        }
    }
    /// <summary>
    /// 試合開始
    /// </summary>
    /// <returns>試合結果(0:引き分け、1:○Win、2:×Win)</returns>
    public int Game()
    {
        const int MARU = 1, BATU = -1;
        int n = 0, winnerFlag;
        IPlayer[] Player = { Maru, Batu };
        int[] Mark = { MARU, BATU };

        Init();

        do
        {
            Point p = Player[n % 2].Tacticse(ref field, ref pointList);
            field[p.x, p.y] = Mark[n % 2];
            if ((winnerFlag = checkJudge()) != 0) break;
        } while (++n < 9);

        //BATUを-1→2に変換
        if (winnerFlag == BATU)
            winnerFlag = 2;
        return winnerFlag;
    }

    /// <summary>
    /// 勝敗判定
    /// </summary>
    /// <returns>勝敗が決まっていれば0以外、決まっていなければ0</returns>
    private int checkJudge()
    {
        for (int i = 0; i < FIELD_SIZE; i++)
        {
            //横
            if (Math.Abs(field[0, i] + field[1, i] + field[2, i]) - 3 == 0)
                return field[0, 1];

            //縦
            if (Math.Abs(field[i, 0] + field[i, 1] + field[i, 2]) - 3 == 0)
                return field[i, 0];
        }

        //斜め
        if (Math.Abs(field[0, 0] + field[1, 1] + field[2, 2]) - 3 == 0 ||
            Math.Abs(field[2, 0] + field[1, 1] + field[0, 2]) - 3 == 0)
            return field[1, 1];
        return 0;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("開始時間:{0}", DateTime.Now.ToString("F"));
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("{0}回目", i + 1);
            //Start(new LocalRandomPlayer(),
            //      new LocalRandomPlayer(),
            //      10000,
            //      "LocalRandomPlayer");
            //Console.WriteLine();
            Start(new MemberRandomPlayer(),
                  new MemberRandomPlayer(),
                  10000,
                  "MemberRandomPlayer");
            Console.WriteLine();
        }
        Console.WriteLine("終了時間:{0}", DateTime.Now.ToString("F"));
    }

    static void Start(IPlayer m, IPlayer b, int num, string typeCode)
    {
        int[] count = new int[3];
        Judge judge = new Judge(m, b);
        for (int i = 0; i < num; i++)
            count[judge.Game()]++;
        Console.WriteLine("---{0}---", typeCode);
        Console.WriteLine("○Win\t:{0}", count[1]);
        Console.WriteLine("×Win\t:{0}", count[2]);
        Console.WriteLine("Draw\t:{0}", count[0]);
    }
}

Index

Feed

Other

Link

Pathtraq

loading...