Comment detail

マルバツゲーム (Nested Flatten)

お昼休みにさくっとやってみました。

Playerクラスを継承したクラスをつくることでPlayerを差し替えることができます。

10000回を3回ほどやってみて

player1 won:    5190
player2 won:    2410
draw :  2400

player1 won:    5190
player2 won:    2411
draw :  2399

player1 won:    5152
player2 won:    2383
draw :  2465

というかんじでした。

 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
import java.util.Random

class TicTacToe(val size:int, val players:List[Player]) {
  protected val field = new Array[Array[char]](size,size)
  val len = size*size
  val lines = _stline((v,v2) => (v,v2))++_stline((v,v2) => (v2,v))++
              List(((size-1).until(-1,-1)).map(v=>(v,v))) ++
              List((0 until size).map(v=>(v,v)))

  def _stline(f:(int,int) => Pair[int,int]) = 
    (0 until size).map(v => (0 until size).map(v2 => f(v,v2)))

  override def toString = {
    val sep = List.make(size*4+1, "-").mkString("\n", "", "\n")
    field.map{_.mkString("| ", " | ", " |")}.mkString(sep,sep,sep)
  }

  def start:Option[Player] = {
    for(val i<-(0 until size); val j<-(0 until size)) { field(i)(j) = ' '}
    Stream.const(players).flatMap(v=>v).take(len).find {player => 
      val p = player.put(this)
      if(!available_?(p)) error("Oops!")
      field(p._1)(p._2) = player.mark
      judge
    }
  }

  def judge = lines.exists(l => l.forall(!available_?(_)) && 
                    l.forall(v => field(l(0)._1)(l(0)._2) == field(v._1)(v._2)))

  def available_?(pos:Pair[int,int]) = field(pos._1)(pos._2) equals ' '
}

abstract class Player{
  val mark:char
  val name:String
  def put(ttt:TicTacToe):Pair[int,int]
}

class RandomPlayer(override val mark:char, override val name:String) extends Player{
  val rnd = new Random
  override def put(ttt:TicTacToe) = {
    def _n = (rnd.nextInt(ttt.size), rnd.nextInt(ttt.size))
    def next(v:Pair[int,int]):Stream[Pair[int,int]] = Stream.cons(v, next(_n))
    next(_n).find(ttt.available_?).get
  }
}

object Main extends Application{
  val ttt = new TicTacToe(3, List(new RandomPlayer('○', "1"), new RandomPlayer('×', "2")))
  val result = Array.make(3,0)
  (1 to 10000) foreach { i => 
    ttt.start match {
      case Some(p) if p.name equals "1" => result(0) += 1
      case Some(p) if p.name equals "2" => result(1) += 1
      case _ => result(2) += 1
    }
  }
  println("player1 won:\t"+result(0)+"\n"+"player2 won:\t"+result(1)+"\n"+"draw :\t"+result(2))
}

1行ミスってました。コピペはいけませんね・・・。あらためて

player1 won:    5826
player2 won:    2857
draw :  1317

という感じです。

1
2
3
4
5
6
7
8
9
@@ -4,7 +4,7 @@
   protected val field = new Array[Array[char]](size,size)
   val len = size*size
   val lines = _stline((v,v2) => (v,v2))++_stline((v,v2) => (v2,v))++
-              List(((size-1).until(-1,-1)).map(v=>(v,v))) ++
+              List(((size-1).until(-1,-1)).map(v=> (v,(size-1-v)))) ++
               List((0 until size).map(v=>(v,v)))

   def _stline(f:(int,int) => Pair[int,int]) =

出題者です。統計的には試行回数が多いほど誤差が小さくなるので、試しに100万×5回ほどやってみました。

その結果、

先攻勝ち:先攻負け:引き分け ≒ 58.5:28.8:12.7

と出ました。 そこらへんの数字の人は正解と思われます。

乱数は素直につかえば問題ないのですが、変なロジックをとおした結果、かたよりが出たりすると怖いです。 処理系によって乱数の癖が見れたりしたら面白かったかも??

全局面を探索したところ、
先攻:51.4%、後攻:30.4%、引き分け18.1%
が正解のようですね。
syatさんの結果でいいのではないでしょうか。

全局面の意味がよくわかりませんが、
途中で終わったりもするので、
すべての局面が等確率で出現するわけではありません。
結果の検証用に打ち筋をすべてたどってみました。
先攻勝ち:131184(51.4%)
後攻勝ち:77904(30.5%)
引き分け:46080(18.1%)
の255168通りでした。

乱数で勝負させたものは確かにsyatさんの数字に近くなったんですが・・・。
 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
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int data;
    int turn;
} CELL;
/*
  [0][1][2]
  [3][4][5]
  [6][7][8]

cell
 data cellの内容
  0:空白
  1:o
  2:x
 turn 打たれたターン数
*/


//勝敗チェック
// 0    :決着まだ
// not 0:勝負あり
int check(CELL a[],int w){
    int i;
    //縦横
    for(i=0;i<3;i++){
        if((a[i*3].data==w)&&(a[i*3+1].data==w)&&(a[i*3+2].data==w)||
           (a[i].data==w)&&(a[i+3].data==w)&&(a[i+6].data==w)) return -1;
    }
    //斜め
    if((a[0].data==w)&&(a[4].data==w)&&(a[8].data==w)||
       (a[2].data==w)&&(a[4].data==w)&&(a[6].data==w)) return -1;
    return 0;
}

int result[3];

int analyze(CELL a[],int t,int turn){
    int i;

    if(turn==10){
    //引き分け
        result[0]++;
        return -1;
    }
    
    //進行
    for(i=0;i<9;i++){
        if(a[i].data==0){
            //置けるなら置く
            a[i].data=t;
            a[i].turn=turn;
            //勝負判定
            if(check(a,t)){
                //決着
                result[t]++;
            }else{
                //続行
                analyze(a,3-t,turn+1);
            }
            //一手戻す
            a[i].data=0;
            a[i].turn=0;
        }
    }
    return 0;
}

int main(){
    int i;
    CELL a[9];

    //盤面初期化
    for(i=0;i<9;i++){
        a[i].data=0;
        a[i].turn=0;
    }
    for(i=0;i<3;i++){
        result[i]=0;
    }
    
    analyze(a,1,1);
    i=result[0]+result[1]+result[2];
    printf("o win:%d(%2.1f%%) x win:%d(%2.1f%%) draw:%d(%2.1f%%)\n",
        result[1],100.0*result[1]/i,
        result[2],100.0*result[2]/i,
        result[0],100.0*result[0]/i);

    return 0;
}
ちと違うみたいです。

58行目、 
result[t]+=weight(9-turn);
に直して下記コードを追加
で、
o win:212256(58.5%) x win:104544(28.8%) draw:46080(12.7%)

だと思います。

1
2
3
4
int weight(int i) {
  if (i<=1) return 1;
  return i*weight(i-1);
}
なるほど・・・
ようやく理解しました。

Index

Feed

Other

Link

Pathtraq

loading...