Comment detail

マルバツゲーム:賢いプレイヤー (Nested Flatten)
 Wikipediaの完勝手順を参考に,以下の形で実装してみました。

1. 3マス揃う場合には揃える
2. 敵方にリーチが掛かっている場合には阻止する
3. ダブルリーチが掛けられる場合には掛ける
4. 敵方がダブルリーチを掛けられる場合には,以下の方法で阻止する
4-1. 敵方のダブルリーチを阻止できる形でリーチを掛けられる場合にはリーチを掛ける
4-2. 4-1以外の場合は,直接的にダブルリーチを阻止する
5. 中央が開いていれば中央に置く
6. 敵方の角の反対が空いていれば,その角に置く
7. 角が空いていれば角に置く
8. 1~7のいずれにも該当しない場合には,空いているマスに置く

 以下実行結果です。

1> c(tic_tac_toe).
{ok,tic_tac_toe}
2> tic_tac_toe:start().
first:0,second:8762,draw:1238
ok
3> tic_tac_toe:start({fun tic_tac_toe:clever_choice/2,fun tic_tac_toe:random_choice/2}).
first:9573,second:0,draw:427
ok
4> tic_tac_toe:start({fun tic_tac_toe:clever_choice/2,fun tic_tac_toe:clever_choice/2}).
first:0,second:0,draw:10000
ok
  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
-module(tic_tac_toe).
-import(io).
-import(lists).
-import(random).
-export([start/0,start/1,random_choice/2,clever_choice/2]).

create_board() -> lists:map(fun(_) -> 0 end,lists:seq(1,9,1)).

enemy(P) -> P rem 2 + 1.

move(B,1,{C,_}) -> move(B,1,C);
move(B,2,{_,C}) -> move(B,2,C);
move(B,P,C) ->
    PS = C(B,P),
    lists:sublist(B,PS - 1) ++ [P|lists:sublist(B,PS + 1,length(B) - PS)].

check_pattern() ->
    lists:map(fun(X) -> lists:seq(X,X+6,3) end, lists:seq(1,3,1)) ++
    lists:map(fun(X) -> lists:seq(X,X+2,1) end, lists:seq(1,7,3)) ++
    [lists:seq(1,9,4),lists:seq(3,7,2)].

check(B,P) ->
    lists:any(
        fun(PT) ->
            lists:all(
                fun(ST) -> ST == P end,
                lists:map(fun(X) -> lists:nth(X,B) end, PT)
            )
        end,
        check_pattern()
    ).

process(B,P,C) ->
    BN = move(B,P,C),
    R = check(BN,P),
    D = lists:all(fun(ST) -> ST /= 0 end, BN),
    if
        R -> P;
        D -> 0;
        true -> process(BN, enemy(P), C)
    end.

process(C) -> process(create_board(),1,C).

random_choice(B,_) ->
    S = lists:map(fun({PS,_}) -> PS end, lists:filter(fun({_,X}) -> X == 0 end, lists:zip(lists:seq(1,length(B),1),B))),
    lists:nth(random:uniform(length(S)),S).

will_complete(B,P,PL) ->
    lists:filter(
        fun(SL) ->
            (length(lists:filter(fun({_,S}) -> S == P end, SL)) == 2) and
            (length(lists:filter(fun({_,S}) -> S == 0 end, SL)) == 1)
        end,
        lists:map(
            fun(PP) -> lists:map(fun(PS) -> {PS,lists:nth(PS,B)} end, PP) end,
            PL
        )
    ).

proposed_complete(B,P,PL) ->
    lists:map(
        fun(SL) ->
            {PS,_} = lists:nth(1, lists:filter(fun({_,S}) -> S == 0 end, SL)),
            PS
        end,
        will_complete(B,P,PL)
    ).

check_fork(B,P) -> length(will_complete(B,P,check_pattern())) >= 2.

lookahead(F,B,P) ->
    lists:filter(
        F,
        lists:map(
            fun({PT,_}) ->{PT,lists:sublist(B,PT - 1)++[P|lists:sublist(B,PT + 1,length(B) - PT)]} end,
            lists:filter(fun({_,S}) -> S == 0 end, lists:zip(lists:seq(1,9,1),B))
        )
    ).

proposed_fork(B,P,_) ->
    lists:map(
        fun({PS,_}) -> PS end,
        lookahead(fun({_,BP}) -> check_fork(BP,P) end,B,P)
    ).

proposed_block_fork(B,P,PL) ->
    FL = proposed_fork(B,enemy(P),PL),
    F = length(FL),
    if
        F == 0 -> [];
        true ->
            lists:map(
                fun({PS,_}) -> PS end,
                lookahead(
                    fun({_,BP}) ->
                        (length(lists:subtract(proposed_complete(BP,P,check_pattern()),FL)) > 0) or
                        (length(proposed_fork(BP,enemy(P),[])) == 0)
                    end,
                    B,
                    P
                )
            )
    end.

proposed_opposite(B,P,PL) ->
    lists:filter(
        fun(PS) -> lists:nth(PS,B) == 0 end,
        lists:map(
            fun({EP,_}) ->
                {_,OP} =
                    lists:nth(1,
                        lists:filter(
                            fun({CP,_}) -> CP == EP end,
                            lists:zip(PL,lists:map(fun(X) -> 10 -X end, PL))
                        )
                    ),
                OP
            end,
            lists:filter(
                fun({_,S}) -> S == enemy(P) end,
                lists:zip(PL,lists:map(fun(PT) -> lists:nth(PT,B) end, PL))
            )
        )
    ).

proposed_space(B,_,PL) -> lists:filter(fun(PS) -> lists:nth(PS,B) == 0 end, PL).

clever_choice(B,P) ->
    lists:nth(
        1,
        lists:append(
            lists:map(
                fun({F,PP,PL}) -> F(B,PP,PL) end,
                [    {fun proposed_complete/3,P,check_pattern()},
                    {fun proposed_complete/3,enemy(P),check_pattern()},
                    {fun proposed_fork/3,P,[]},
                    {fun proposed_block_fork/3,P,[]},
                    {fun proposed_space/3,0,[5]},
                    {fun proposed_opposite/3,P,[1,3,7,9]},
                    {fun proposed_space/3,0,[1,3,7,9]},
                    {fun proposed_space/3,0,[2,4,6,8]}
                ]
            )
        )
    ).

start(C) ->
    R = lists:map(fun(_) -> process(C) end,lists:seq(1,10000,1)),
    io:format("first:~w,second:~w,draw:~w~n",lists:map(fun(P) -> length(lists:filter(fun(X) -> X == P end, R)) end,[1,2,0])).

start() -> start({fun random_choice/2,fun clever_choice/2}).
 間違えてFORTRANを選択してしまいました...。

Index

Feed

Other

Link

Pathtraq

loading...