challenge マルバツゲーム:賢いプレイヤー

#6190 の続編です。
マルバツゲームで、賢いプレイヤーの思考ルーチンを実装してください。

賢いといってもいろいろありますが、
1.負けない
2.できるだけ勝つ
という条件でいってみたいと思います。

ランダムプレイヤーと1万回バトルした結果(勝ち・負け・分け)を表示してください。
先攻になっても後攻になっても無敗!となれば言うことなしです。

Posted feedbacks - Smalltalk

Squeak Smalltalk で。

勝ちの手か、負け回避の必要がなければ、

  • 中央が空いていれば中央
  • 隅が空いていれば、相手が陣取ったマス目に近いほうの開いている隅。ただし、後手で ○×○ と斜めに列んだときは隅以外。
  • ランダム

の判断順としてみました。

 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
| 全マス目 全ライン 勝敗カウンタ 中央のマス 隅のマス群 縁のマス群 |

中央のマス := #(2 2).
隅のマス群 := #((1 1) (3 3) (1 3) (3 1)).
縁のマス群 := #((1 2) (3 2) (2 1) (2 3)).
全マス目 := (中央のマス, 隅のマス群, 縁のマス群) asOrderedCollection.

全ライン := OrderedCollection new.
(1 to: 3) do: [:座標1 |
    全ライン add: ((1 to: 3) collect: [:座標2 | {座標1. 座標2}]).
    全ライン add: ((1 to: 3) collect: [:座標2 | {座標2. 座標1}])].
全ライン add: #((1 1) (2 2) (3 3)); add: #((1 3) (2 2) (3 1)).

^#(賢い先手 #賢い後手 #賢い両者) collect: [:モード |

    勝敗カウンタ := Bag new.

    1e4 timesRepeat: [
        | 残りマス群 先手 後手 打ち手順 ランダム 手番 結果 |
        残りマス群 := 全マス目 copy.
        先手 := OrderedCollection new. 後手 := 先手 copy.
        打ち手順 := {先手. 後手}.
        ランダム := モード caseOf: {
                [#賢い先手] -> [後手].
                [#賢い後手] -> [先手]}
            otherwise: [nil].
        手番 := 0.
        結果 := nil.

        [残りマス群
            ifEmpty: [結果 := #引き分け]
            ifNotEmpty: [
                | 打ち手 敵の手 必勝手検索 必勝の手 守りの手 詰み回避の手 最善手 打つ手 |
                打ち手 := 打ち手順 atWrap: (手番 := 手番 + 1).
                敵の手 := 打ち手順 atWrap: 手番 + 1.

                必勝手検索 := [:対象マス群 |
                    | 候補群 |
                    全ライン
                        detect: [:構成マス群 |
                            (候補群 := 構成マス群 difference: 対象マス群) size = 1 and: [
                                残りマス群 includesAllOf: 候補群]]
                        ifNone: [候補群 := #()]..
                    候補群 ifNotEmpty: [候補群 first] ifEmpty: [nil]].

                必勝の手 := [必勝手検索 value: 打ち手].
                守りの手 := [必勝手検索 value: 敵の手].

                詰み回避の手 := [
                    (残りマス群 includes: 中央のマス) ifTrue: [中央のマス] ifFalse: [
                        (敵の手 includesAnyOf: {隅のマス群 first: 2. 隅のマス群 last: 2})
                            ifTrue: [縁のマス群 first]
                            ifFalse: [
                                (隅のマス群 intersection: 残りマス群)
                                    ifNotEmptyDo: [:候補群 |
                                        候補群 detectMin: [:候補 |
                                            敵の手 inject: 0 into: [:総和 :敵のマス |
                                                総和 + (敵のマス * 候補) sum]]]
                                    ifEmpty: [nil]]]].

                最善手 := [
                    必勝の手 value ifNil: [
                        守りの手 value ifNil: [
                            詰み回避の手 value ifNil: [残りマス群 atRandom]]]].

                打つ手 := 打ち手 ~~ ランダム ifTrue: [最善手 value] ifFalse: [残りマス群 atRandom].
                打ち手 add: (残りマス群 remove: 打つ手).
                (全ライン anySatisfy: [:必須マス群 | 打ち手 includesAllOf: 必須マス群])
                    ifTrue: [結果 := 打ち手 == 先手 ifTrue: [#先手勝ち] ifFalse: [#後手勝ち]]].
        結果 isNil] whileTrue.

        勝敗カウンタ add: 結果].

    モード -> 勝敗カウンタ sortedCounts asArray]

"=> {#賢い先手->{9774->#先手勝ち. 226->#引き分け}. 
    #賢い後手->{7962->#後手勝ち. 2038->#引き分け}. 
    #賢い両者->{10000->#引き分け}} "

Index

Feed

Other

Link

Pathtraq

loading...