マルバツゲーム
Posted feedbacks - Flatten
Nested Hiddenお昼休みにさくっとやってみました。
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))
}
|
勝敗判定の部分をもっとエレガントに書きたいですね。
結果
○ 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を実装すれば、実装を置き換えられます。 しかし、Scalaと比べてだいぶ長くなっちゃいました。 とりあえず、3回実行してみた結果です。 Result: Player1 won: 5214 Player2 won: 2355 draw game : 2431 Result: Player1 won: 5211 Player2 won: 2444 draw game : 2345 Result: Player1 won: 5174 Player2 won: 2404 draw game : 2422
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 | import java.util.Random;
public class TicTacToe {
public static final int SIZE = 3;
private final Mark[][] fields_ = new Mark[SIZE][SIZE];
private final Pair[][] lines_ = new Pair[][] {
{new Pair(0,0), new Pair(0,1), new Pair(0,2)},
{new Pair(1,0), new Pair(1,1), new Pair(1,2)},
{new Pair(2,0), new Pair(2,1), new Pair(2,2)},
{new Pair(0,0), new Pair(1,0), new Pair(2,0)},
{new Pair(0,1), new Pair(1,1), new Pair(2,1)},
{new Pair(0,2), new Pair(1,2), new Pair(2,2)},
{new Pair(0,0), new Pair(1,1), new Pair(2,2)},
{new Pair(2,2), new Pair(1,1), new Pair(0,0)},
};
public TicTacToe() {
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
fields_[x][y] = Mark.E;
}
}
}
public Mark getMark(Pair pair) {
return fields_[pair.x][pair.y];
}
public void setMark(Pair pair, Mark mark) {
fields_[pair.x][pair.y] = mark;
}
public boolean nextStep(Pair pair, Mark mark) {
if (getMark(pair) != Mark.E) return false;
setMark(pair, mark);
return true;
}
public Mark isEndGame() {
for (Pair[] row: lines_) {
Mark m = null;
boolean isSame = true;
for (Pair cell: row) {
Mark mark = fields_[cell.x][cell.y];
if (m == null) {
m = mark;
continue;
}
if (m != mark) {
isSame = false;
break;
}
}
if (isSame) return m;
}
return Mark.E;
}
public static void main(String[] args) {
Player player1 = new RandomPlayer(Mark.O, "Player1");
Player player2 = new RandomPlayer(Mark.X, "Player2");
int maxTurn = TicTacToe.SIZE * TicTacToe.SIZE;
int[] result = new int[3];
for (int round = 0; round < 10000; round++) {
TicTacToe game = new TicTacToe();
for (int turn = 0; turn <= maxTurn; turn++) {
if (turn == maxTurn) {
result[2]++;
break;
} else if (turn % 2 == 0) {
player1.put(game);
} else {
player2.put(game);
}
Mark mark = game.isEndGame();
if (mark == Mark.O) {
result[0]++;
break;
} else if (mark == Mark.X) {
result[1]++;
break;
}
}
}
System.out.println("Result:");
System.out.printf("%s won: %d%n", player1.name, result[0]);
System.out.printf("%s won: %d%n", player2.name, result[1]);
System.out.printf("draw game : %d%n", result[2]);
}
}
enum Mark {
E { @Override public Mark getOpponent() { return E; } },
O { @Override public Mark getOpponent() { return X; } },
X { @Override public Mark getOpponent() { return O; } };
public abstract Mark getOpponent();
}
class Pair {
public final int x;
public final int y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
}
abstract class Player {
public final Mark mark;
public final String name;
public Player(Mark mark, String name) {
this.mark = mark;
this.name = name;
}
public abstract void put(TicTacToe game);
}
class RandomPlayer extends Player {
private final Random random_ = new Random();
public RandomPlayer(Mark mark, String name) {
super(mark, name);
}
@Override
public void put(TicTacToe game) {
for (;;) {
Pair pair = getNextPair();
boolean res = game.nextStep(pair, mark);
if (res) break;
}
}
private Pair getNextPair() {
return new Pair(random_.nextInt(TicTacToe.SIZE), random_.nextInt(TicTacToe.SIZE));
}
}
|
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 | | マス目群 勝敗カウンタ 勝ち判定 |
マス目群 := OrderedCollection new.
(1 to: 3) asDigitsToPower: 2 do: [:座標 | マス目群 add: 座標 copy].
勝敗カウンタ := Bag new.
1e4 timesRepeat: [
| 残りのマス目群 先手 後手 打ち手順 手番 結果 |
残りのマス目群 := マス目群 copy shuffled.
先手 := OrderedCollection new. 後手 := 先手 copy.
打ち手順 := {先手. 後手}.
手番 := 0.
結果 := nil.
勝ち判定 := [:取得済み |
(#(first second) anySatisfy: [:セレクタ |
(取得済み
groupBy: [:各々 | 各々 perform: セレクタ]
having: [:括り | 括り size = 3]) notEmpty]) or: [
{[:各々 | 各々 first = 各々 second]. [:各々 | 各々 sum = 4]}
anySatisfy: [:条件 | (取得済み count: 条件) = 3]]].
[残りのマス目群
ifEmpty: [結果 := #引き分け]
ifNotEmpty: [
| 取得マス目群 |
(取得マス目群 := 打ち手順 atWrap: (手番 := 手番 + 1))
add: 残りのマス目群 removeFirst.
(勝ち判定 value: 取得マス目群) ifTrue: [
結果 := 手番 odd
ifTrue: [#先手勝ち]
ifFalse: [#後手勝ち]]].
結果 isNil] whileTrue.
勝敗カウンタ add: 結果].
^勝敗カウンタ sortedCounts asArray
"=> {5895->#先手勝ち. 2796->#後手勝ち. 1309->#引き分け} "
|
勝敗判定が分かりづらかったので変えてみました。
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 | | マス目群 ライン群 勝敗カウンタ |
マス目群 := OrderedCollection new.
(1 to: 3) asDigitsToPower: 2 do: [:座標 | マス目群 add: 座標 copy].
ライン群 := 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)).
勝敗カウンタ := Bag new.
1e4 timesRepeat: [
| 残りのマス目群 先手 後手 打ち手順 手番 結果 |
残りのマス目群 := マス目群 copy shuffled.
先手 := OrderedCollection new. 後手 := 先手 copy.
打ち手順 := {先手. 後手}.
手番 := 0.
結果 := nil.
[残りのマス目群
ifEmpty: [結果 := #引き分け]
ifNotEmpty: [
| 取得マス目群 |
(取得マス目群 := 打ち手順 atWrap: (手番 := 手番 + 1))
add: 残りのマス目群 removeFirst.
(ライン群 anySatisfy: [:必須マス群 |
取得マス目群 includesAllOf: 必須マス群])
ifTrue: [結果 := 手番 odd
ifTrue: [#先手勝ち] ifFalse: [#後手勝ち]]].
結果 isNil] whileTrue.
勝敗カウンタ add: 結果].
^勝敗カウンタ sortedCounts asArray
|
ちょっと力業でしょうか。
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 | (use srfi-1)
(use srfi-27)
(use util.list)
(random-source-randomize! default-random-source)
;; board: (0 1 0 0 0 0 0 0 0)
;; board -> board
(define (player-random board self)
(define (okeru)
(length (filter zero? board)))
(let loop ((board board)
(next (random-integer (okeru))))
(if (null? board)
()
(cons (if (= next 0)
self
(car board))
(loop (cdr board)
(if (= (car board) 0)
(- next 1)
next
))))))
(define (play p1 p2)
(define (finish? board)
(or (> (judge board) 0)
(not (find zero? board))))
(let loop ((board (make-list 9 0))
(phase 1))
(if (finish? board)
board
(if (= phase 1)
(loop (p1 board 1) 2)
(loop (p2 board 2) 1)))))
(define (judge board)
(define line-points-list
'((0 1 2)
(3 4 5)
(6 7 8)
(0 3 6)
(1 4 7)
(2 5 8)
(0 4 8)
(2 4 6)))
(define (judge-4-player n)
(find (lambda (line-points)
(every (lambda (j)
(= (ref board j) n))
line-points))
line-points-list))
(cond ((judge-4-player 1) 1)
((judge-4-player 2) 2)
(else 0)))
(define (marubatsu p1 p2)
(define (one-play)
(judge (play p1 p2)))
(define hash (make-hash-table))
(dotimes (counter 10000)
(hash-table-update! hash (one-play) (cut + 1 <>) 0))
(write (hash-table->alist hash)))
|
ちょっと力業でしょうか。
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 | (use srfi-1)
(use srfi-27)
(use util.list)
(random-source-randomize! default-random-source)
;; board: (0 1 0 0 0 0 0 0 0)
;; board -> board
(define (player-random board self)
(define (okeru)
(length (filter zero? board)))
(let loop ((board board)
(next (random-integer (okeru))))
(if (null? board)
()
(cons (if (= next 0)
self
(car board))
(loop (cdr board)
(if (= (car board) 0)
(- next 1)
next
))))))
(define (play p1 p2)
(define (finish? board)
(or (> (judge board) 0)
(not (find zero? board))))
(let loop ((board (make-list 9 0))
(phase 1))
(if (finish? board)
board
(if (= phase 1)
(loop (p1 board 1) 2)
(loop (p2 board 2) 1)))))
(define (judge board)
(define line-points-list
'((0 1 2)
(3 4 5)
(6 7 8)
(0 3 6)
(1 4 7)
(2 5 8)
(0 4 8)
(2 4 6)))
(define (judge-4-player n)
(find (lambda (line-points)
(every (lambda (j)
(= (ref board j) n))
line-points))
line-points-list))
(cond ((judge-4-player 1) 1)
((judge-4-player 2) 2)
(else 0)))
(define (marubatsu p1 p2)
(define (one-play)
(judge (play p1 p2)))
(define hash (make-hash-table))
(dotimes (counter 10000)
(hash-table-update! hash (one-play) (cut + 1 <>) 0))
(write (hash-table->alist hash)))
|
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
と出ました。 そこらへんの数字の人は正解と思われます。
乱数は素直につかえば問題ないのですが、変なロジックをとおした結果、かたよりが出たりすると怖いです。 処理系によって乱数の癖が見れたりしたら面白かったかも??
終了判定にバグ発見してしまいました。 ここを直して、100万回実行した結果以下のようになりました。 これでほぼ合っていそうです。 Result: Player1 won: 582337 Player2 won: 290884 draw game : 126779
1 2 3 4 | 15c15
< {new Pair(2,2), new Pair(1,1), new Pair(0,0)},
---
> {new Pair(0,2), new Pair(1,1), new Pair(2,0)},
|
プレイヤーを単純な関数として実装しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from random import choice
def p(d, x):
return choice([i for i, j in enumerate(d) if j == 0])
def w(d, i):
f = lambda l: [i] * 3 == l
return (f(d[:3]) or f(d[3:6]) or f(d[6:]) or
f(d[::3]) or f(d[1::3]) or f(d[2::3]) or
f(d[::4]) or f(d[2:7:2]))
def m(*p):
d = [0] * 9
for i in range(9):
x = i % 2 + 1
d[p[i%2](d, x)] = x
if i > 3 and w(d, x):
return x
return 0
r = [0] * 3
for i in range(10000):
r[m(p, p)] += 1
print 'draw: %d\n1st: %d\n2nd: %d' % tuple(r)
|
○×ゲームなのに視覚的なものはほとんど考慮してないので恐縮ですが... ちなみに、100万回勝負させたら player 1 won : 3595448(0.359545) player 2 won : 2882753(0.288275) draw : 3521799(0.35218) だそうです。
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 | #include <algorithm>
#include <iostream>
#include <map>
#include <ctime>
class board
{
int cell[3*3];
public:
board() { reset(); }
void reset() { std::fill_n(cell, 3*3, 0); }
int& operator()(int x, int y)
{ return cell[x+3*y]; }
const int& operator()(int x, int y) const
{ return cell[x+3*y]; }
};
std::ostream& operator<<(std::ostream& os, const board& b)
{
for ( int y = 0; y < 3; ++y ) {
y != 0 && os << "-+-+-\n";
os << b(0,y) << "|" << b(1,y) << "|" << b(2,y) << "\n";
}
return os;
}
class player
{
static int player_id_;
int id_;
protected:
player() : id_(++player_id_) {}
virtual ~player();
public:
int id()const { return id_; }
void operator()(board& b) const
{ next(b); }
private:
virtual void
next(board& b)
const = 0;
};
int player::player_id_ = 0;
player::~player() {}
class rand_player
: public player
{
void next(board& b) const;
};
void
rand_player::next(board& b)
const
{
while (1) {
int x = std::rand() % 3;
int y = std::rand() % 3;
if ( b(x,y) == 0 ) {
b(x,y) = id();
break;
}
}
}
struct board_checker
{
int operator()(board& b) const;
};
int
board_checker::operator()(board& b)
const
{
bool draw=true;
for (int x = 0; draw && x < 3; ++x )
for (int y = 0; draw && y < 3; ++y )
draw = b(x,y) != 0;
if ( draw )
return -1;
if ( int id = b(1,1) )
if ((b(0,0) == id && b(2,2) == id ) ||
(b(0,2) == id && b(2,0) == id) ||
(b(0,1) == id && b(2,1) == id) ||
(b(1,0) == id && b(1,2) == id) )
return id;
if ( int id = b(0,0) )
if ((b(0,1) == id && b(0,2) == id) ||
(b(1,0) == id && b(2,0) == id) )
return id;
if ( int id = b(2,2) )
if ((b(1,2) == id && b(0,2) == id) ||
(b(2,1) == id && b(2,0) == id) )
return id;
// not finish
return 0;
}
int main()
{
std::srand(std::time(0));
rand_player p1, p2;
player *p[] = { &p1, &p2 };
board b;
board_checker bc;
std::map<int, unsigned int> st;
const std::size_t max = 10000
for ( std::size_t i = 0; i < max; ++i ) {
b.reset();
int count = 0;
while ( 1 ) {
(*p[count++ % 2])(b);
// std::cout << " i = " << i << " step = " << count << "\n" << b;
if ( count >= 4 )
if ( int result = bc(b) ) {
st[result]++;
break;
}
}
}
std::cout << "player " << p1.id() << " won : " << st[p1.id()]
<< "(" << st[p1.id()]/static_cast<double>(max) << ")\n";
std::cout << "player " << p2.id() << " won : " << st[p2.id()]
<< "(" << st[p2.id()]/static_cast<double>(max) << ")\n";
std::cout << "draw : " << st[-1]
<< "(" << st[-1]/static_cast<double>(max) << ")\n";
return 0;
}
|
こんにちは。 お手軽実装のはずが、コンパイラにおこられて、ソース分割する羽目になりました。 無駄に拡張性を重視したもので、長くなり、冗長になりました。orz でもその代わりに、長さ3以外もいけます。 あぁ、バグ取りはしましたけど、保障はしません。
result: Player1[5853] Player2[2865] Draw[1282]
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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | //////////////////////Main.cpp////////////////////////////
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include "MBGame.h"
#include "Player.h"
void ShowVisual(MBGame& in){
Mark V;
for(unsigned int i=0;i<in.Length();i++){
for(unsigned int j=0;j<in.Length();j++){
V=in.At(j,i);
if(V == None) printf("+");
if(V == Maru) printf("O");
if(V == Batu) printf("X");
}
printf("\n");
}
}
int main(){
unsigned int Len = 3;
MBGame mbg(Len);
Player A,B;
Mark F;
int P1=0,P2=0,D=0;
//srand(time(NULL));
mbg.SetPlayer(A,B);
for(int c=0;c<100;c++){
mbg.Init(Len);
F = mbg.Play();
if(F == None) {printf("[%d] Draw!!\n",c);D++;}
if(F == Maru) {printf("[%d] O Win!\n",c);P1++;}
if(F == Batu) {printf("[%d] X Win!\n",c);P2++;}
ShowVisual(mbg);
printf("\n");
}
printf("Player1[%d] Player2[%d] Draw[%d]",P1,P2,D);
return 0;
}
///////////////////////////MBGame.h////////////////////
#pragma once
#include <vector>
#include "Mark.h"
#include "Player.h"
class Player;
class MBGame{
Player *A_,*B_;
unsigned int Length_;
std::vector<Mark> Board_;
bool IsLock_;
public:
MBGame(unsigned int Len=3);
void Init(unsigned int Len=3);
unsigned int Length();
Mark Play();
Mark IsEnd();
bool IsEmpty(unsigned int x,unsigned int y);
bool HasEmpty();
bool SetStone(unsigned int x,unsigned int y,Mark M);
bool IsLocked();
Mark At(unsigned int x,unsigned int y);
void SetPlayer(Player& A,Player& B);
protected:
void Lock();
void UnLock();
};
///////////////////////////////MBGame.cpp/////////////
#include <algorithm>
#include "MBGame.h"
MBGame::MBGame(unsigned int Len){
A_=NULL;
B_=NULL;
Init(Len);
}
void MBGame::Init(unsigned int Len){
if(Len == 0) Len = 3;
Length_ = Len;
Board_.resize(Len*Len);
std::fill(Board_.begin(),Board_.end(),None);
}
bool MBGame::SetStone(unsigned int x,unsigned int y,Mark M){
if(IsLocked()) return false;
if(!IsEmpty(x,y)) return false;
Board_[x+ (y*Length())] = M;
Lock();
return true;
}
void MBGame::SetPlayer(Player& A,Player& B){
A_ = &A;
B_ = &B;
A_->SetMark(Maru);
B_->SetMark(Batu);
}
/////////////////////////////////////////////////////////////////
Mark MBGame::Play(){
Mark f;
unsigned int c=0;
while(HasEmpty()){
if((c%2) == 0 ){
UnLock();
A_->Play(*this);
}else{
UnLock();
B_->Play(*this);
}
f=IsEnd();
if(f!=None) return f;
c++;
}
return None;
}
Mark MBGame::IsEnd(){
Mark F=None;
unsigned int i=0,j=0;
for(i=0;i<Length();i++){//横の判定
F = At(0,i);
if(F == None) continue;
for(j=0;j<Length();j++){
if(F != At(j,i)) break;
}
if(j==Length()) return F;
}
for(j=0;j<Length();j++){//縦の判定
F = At(j,0);
if(F == None) continue;
for(i=0;i<Length();i++){
if(F != At(j,i)) break;
}
if(i==Length()) return F;
}
F = At(0,0);
for(i=0;i<Length();i++){//斜めの判定
if(F == None) break;
if(F != At(i,i)) break;
}
if(i == Length()) return F;
F = At((Length()-1),0);
for(i=0;i<Length() ;i++){//斜めの判定2
if(F == None) break;
if(F != At((Length()-1) - i,i)) break;
}
if(i == Length()) return F;
return None;
}
/////////////////////////////////////////////////////////////////
bool MBGame::IsEmpty(unsigned int x,unsigned int y){
return At(x,y) == None;
}
bool MBGame::HasEmpty(){
for(unsigned int i=0;i<Length();i++){
for(unsigned int j=0;j<Length();j++){
if(IsEmpty(j,i)) return true;
}
}
return false;
}
/////////////////////////////////////////////////////////////////
Mark MBGame::At(unsigned int x,unsigned int y){
return Board_[y*Length() + x];
}
unsigned int MBGame::Length(){
return Length_;
}
/////////////////////////////////////////////////////////////////
bool MBGame::IsLocked(){
return IsLock_;
}
void MBGame::Lock(){
IsLock_ = true;
}
void MBGame::UnLock(){
IsLock_ = false;
}
////////////////////////////////////////////////////////////////
/////////////////////////Player.h//////////////////////////
#pragma once;
#include "MBGame.h"
class MBGame;
class Player{
Mark Mark_;
public:
void SetMark(Mark in);
virtual void Play(MBGame& in);
};
////////////////////////Player.cpp////////////////////////
#include "Player.h"
void Player::SetMark(Mark in){
Mark_ = in;
}
void Player::Play(MBGame &in){
unsigned int x,y;
do{
x=(rand()%in.Length());
y=(rand()%in.Length());
//if(!in.IsEmpty(x,y)) continue;
}while(!in.SetStone(x,y,Mark_));
}
////////////////////////////Mark.h//////////////////////////
#pragma once;
enum Mark{
Batu =2,
None =0,
Maru =1,
};
//////////////////////////////////////////////////////////////
|
drawを先に判定しちゃダメじゃん...
というわけで、100万回試行の結果がちゃんと
player 1 won : 585255(0.585255) player 2 won : 287878(0.287878) draw : 126867(0.126867)
になりました。
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 | int
board_checker::operator()(board& b)
const
{
if ( int id = b(1,1) )
if ((b(0,0) == id && b(2,2) == id ) ||
(b(0,2) == id && b(2,0) == id) ||
(b(0,1) == id && b(2,1) == id) ||
(b(1,0) == id && b(1,2) == id) )
return id;
if ( int id = b(0,0) )
if ((b(0,1) == id && b(0,2) == id) ||
(b(1,0) == id && b(2,0) == id) )
return id;
if ( int id = b(2,2) )
if ((b(1,2) == id && b(0,2) == id) ||
(b(2,1) == id && b(2,0) == id) )
return id;
bool draw=true;
for (int x = 0; draw && x < 3; ++x )
for (int y = 0; draw && y < 3; ++y )
draw = b(x,y) != 0;
if ( draw )
return -1;
// not finish
return 0;
}
|
MARU: 5765 BATU: 2707 DRAW: 1528 となりました。
RandPlayer.prototype.think の部分を修正することで Player を差し替えることができます。
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 | var Board = function(){ this.init(); };
Board.prototype = {
cell : [],
size : 3,
init : function(){
for( var i = 0; i < this.size; i++ ){
this.cell[i] = [];
}
},
isVacant : function( x, y ){ return this.cell[x][y] == undefined; },
put : function( x, y, id ){ this.cell[x][y] = id; },
judge : function( x, y ){
var i;
var size = this.size;
var cell = this.cell;
for( i = 1; i < size; i++ ){
if( cell[i-1][y] == undefined || cell[i-1][y] != cell[i][y] )
break;
}
if( i == size ) return true;
for( i = 1; i < size; i++ ){
if( cell[x][i-1] == undefined || cell[x][i-1] != cell[x][i] )
break;
}
if( i == size ) return true;
if( x == y ){
for( i = 1; i < size; i++ ){
if( cell[i-1][i-1] == undefined || cell[i-1][i-1] != cell[i][i] )
break;
}
if( i == size ) return true;
}
if( x + y == size - 1 ){
for( i = 1; i < size; i++ ){
if( cell[i-1][size-1] == undefined || cell[i-1][size-i] != cell[i][size-i-1] )
break;
}
if( i == size ) return true;
}
return false;
}
};
var Player = function(){};
Player.prototype = {
put : function( b, x, y ){ b.put( x, y ); },
think: undefined,
win : 0
};
var RandPlayer = function(){};
RandPlayer.prototype = new Player();
RandPlayer.prototype.think = function( b ){
var x,y,tmp;
var size = b.size
var max = size * size;
while(1){
tmp = Math.floor(Math.random() * max );
x = Math.floor( tmp / size );
y = tmp % size;
if( b.isVacant( x, y ) ) break;
}
return {x:x, y:y};
};
var b = new Board();
var max = b.size * b.size;
var target;
var p = [ new RandPlayer(), new RandPlayer() ];
for( var i = 0; i < 10000; i++ ){
b.init();
for( j = 0; j < max; j++ ){
target = p[j%2].think( b );
b.put(target.x, target.y, j%2 );
if( b.judge( target.x, target.y ) )
break;
}
if( j != max ){
p[j%2].win++;
}
}
document.write("MARU: " + p[0].win + "<br/>" );
document.write("BATU: " + p[1].win + "<br/>" );
document.write("DRAW: " + (10000 - p[0].win - p[1].win) + "<br/>" );
|
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 | import Data.Array
import Data.Maybe
import Control.Monad
import System.Random
type Pos = (Int, Int)
type OX = Bool
type Board = Array Pos (Maybe OX)
type View = OX -> Bool
type Strategy m = Board -> View -> m Pos
type Player m = (Strategy m, View)
emptyBoard :: Board
emptyBoard = array ((0,0), (2,2)) [(p, Nothing) | p<-ps]
where ps = [(x,y) | x<-[0..2], y<-[0..2]]
availablePos :: Board -> [Pos]
availablePos b = [p | (p, Nothing) <- assocs b]
win :: Board -> View -> Bool
win b v = any (all (fromMaybe False . fmap v . (b!))) xss
where
xss = [ [(x,y) | y<-[0..2]] | x<-[0..2] ] ++
[ [(x,y) | x<-[0..2]] | y<-[0..2] ] ++
[ [(0,0), (1,1), (2,2)], [(0,2), (1,1), (2,0)] ]
play :: Monad m => Strategy m -> Strategy m -> m (Maybe Bool)
play s1 s2 = go emptyBoard (s1, id) (s2, not)
where
go b p1@(s,v) p2
| null ps = return Nothing
| otherwise = do
pos <- s b v
let m = Just (v True)
b' = b // [(pos, m)]
if win b' v
then return m
else go b' p2 p1
where ps = availablePos b
randStrategy :: Strategy IO
randStrategy b v = do
let ps = availablePos b
i <- getStdRandom (randomR (0, length ps - 1))
when (i<0) $ putStrLn $ show ps
return (ps!!i)
main :: IO ()
main = do
let n = 10000
xs <- replicateM n (play randStrategy randStrategy)
let a = sum [1 | Just True <- xs]
b = sum [1 | Just False <- xs]
c = n - (a + b)
putStrLn $ "player1 won: " ++ show a
putStrLn $ "player2 won: " ++ show b
putStrLn $ "draw: " ++ show c
putStrLn $ "total: " ++ show n
|
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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | #include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.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;
}
//ゲーム状態表示
void putgame(CELL a[],int mode){
int i,j,turn_max;
char c[3]={' ','o','x'};
switch(mode){
case 0: //盤面
for(i=0;i<9;i++){
printf("%c",c[a[i].data]);
if(i%3!=2)
printf("|");
else{
printf("\n");
if(i!=8) printf("-+-+-");
printf("\n");
}
}
break;
case 1://盤面+手順
for(i=0;i<9;i++){
printf("%c%d",c[a[i].data],a[i].turn);
if(i%3!=2)
printf("|");
else{
printf("\n");
if(i!=8) printf("--+--+--");
printf("\n");
}
}
break;
case 2://手順
turn_max=0;
for(i=0;i<9;i++){
if(turn_max<a[i].turn) turn_max=a[i].turn;
}
for(i=1;i<=turn_max;i++){
for(j=0;j<9;j++){
if(a[j].turn==i){
printf("%c(%d,%d)",c[a[j].data],j%3,j/3);
if(i!=turn_max) printf(",");
else printf("\n");
}
}
}
break;
}
}
//乱数プレイヤー
int randplayer(CELL a[],int turn){
int p;
int i;
static int seed=0;
//初期化ルーチン
if(turn==0){
seed+=rand();
return 0;
}
//乱数再現
srand(seed);
for(i=2-turn&2;i<turn;i+=2) rand();
//思考ルーチン
//残ってるマスからランダムに選択
p=(rand()>>3)%(9-(turn-1));
//埋まってるマスのスキップ
i=0;
while(1){
if(a[i].data)
p++;
else{
if(i==p) break;
}
i++;
}
return p;
}
//乱数プレイヤー2
int randplayer2(CELL a[],int turn){
int p;
int i;
static int seed=0;;
//初期化ルーチン
if(turn==0){
seed+=time(NULL);
return 0;
}
//乱数再現
srand(seed);
for(i=2-turn&2;i<turn;i+=2) rand();
//思考ルーチン
//残ってるマスからランダムに選択
p=(rand()>>3)%(9-(turn-1));
//埋まってるマスのスキップ
i=0;
while(1){
if(a[i].data)
p++;
else{
if(i==p) break;
}
i++;
}
return p;
}
//ゲーム進行
int game(int (*p1)(CELL [],int ),int (*p2)(CELL [],int)){
CELL a[9];
int i;
int t=1;
int p;
int ret=0;
int (*player[2])(CELL [],int);
//プレイヤー登録
player[0]=p1;
player[1]=p2;
//盤面初期化
for(i=0;i<9;i++){
a[i].data=0;
a[i].turn=0;
}
//プレイヤー初期化
player[0](a,0);
player[1](a,0);
for(i=1;i<=9;i++){
p=player[t-1](a,i);
a[p].data=t;
a[p].turn=i;
// putgame(a,0);
if (check(a,t)){
ret=t;
break;
}else
t=3-t;
}
// putgame(a,1);
// putgame(a,2);
return ret;
}
int main(){
int i;
int result[3]={0,0,0};
//乱数の初期化
srand(time(NULL));
for(i=1;i<=10000;i++){
result[game(randplayer,randplayer2)]++;
if(i%100==0){
printf("~test%d:o win:%d(%2.1f%%) x win:%d(%2.1f%%) draw:%d(%2.1f%%)\n",
i,result[1],100.0*result[1]/i,result[2],100.0*result[2]/i,result[0],100.0*result[0]/i);
}
}
return 0;
}
|
全局面を探索したところ、 先攻: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;
}
|
・状態遷移を相互再帰で書く ・Gaucheで最近になって文書化されたshuffleを使う ・盤面のベクタを副作用で上書きするのが普通だけど、副作用が嫌なので棋譜のリストを持ってみました。 結果 player1 won: 5787 player2 won: 2909 draw: 1304
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 | (use srfi-1)
(use gauche.sequence)
(define (win? l)
(any (cut lset<= = <> l)
'((0 1 2) (3 4 5) (6 7 8) (0 3 6) (1 4 7) (2 5 8) (0 4 8) (2 4 6))))
(let new-game ((win 0)
(lose 0)
(draw 0))
(if (<= 10000 (+ win lose draw))
(format #t " player1 won: ~a\n player2 won: ~a\n draw: ~a\n" win lose draw)
(letrec ((p1-turn (lambda (p1 p2)
(if (win? p2)
(new-game win (+ 1 lose) draw)
(let ((vacant (lset-difference = '(0 1 2 3 4 5 6 7 8) (append p1 p2))))
(if (null? vacant)
(new-game win lose (+ 1 draw))
(p2-turn (cons (car (shuffle vacant)) p1) p2))))))
(p2-turn (lambda (p1 p2)
(if (win? p1)
(new-game (+ 1 win) lose draw)
(let ((vacant (lset-difference = '(0 1 2 3 4 5 6 7 8) (append p1 p2))))
(if (null? vacant)
(new-game win lose (+ 1 draw))
(p1-turn p1 (cons (car (shuffle vacant)) p2))))))))
(p1-turn '() '()))))
|
結果
player1 won: 5907
player2 won: 2872
draw: 1221
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | (use srfi-1)
(use gauche.sequence)
(define (win? l)
(any (cut lset<= = <> l)
'((0 1 2) (3 4 5) (6 7 8) (0 3 6) (1 4 7) (2 5 8) (0 4 8) (2 4 6))))
(let omake ((win 0)
(lose 0)
(draw 0))
(if (<= 10000 (+ win lose draw))
(format #t " player1 won: ~a\n player2 won: ~a\n draw: ~a\n" win lose draw)
(receive (p1 p2) (split-at (shuffle '(0 1 2 3 4 5 6 7 8)) 5)
(or (and-let* ((i (list-index win? (list (cddr p1) (cdr p2) (cdr p1) p2 p1))))
(if (even? i)
(omake (+ 1 win) lose draw)
(omake win (+ 1 lose) draw)))
(omake win lose (+ 1 draw))))))
|
ちと違うみたいです。 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);
}
|
あんまり綺麗なコードじゃないですが、 PostScript で。 O win : 5892 draw : 1247 X win : 2861
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 | %!PS
/ShowMap { % [Map Vector] ShowMap -
aload length sqrt cvi
dup { % v0 v1 ... vn N
dup { % v0 v1 ... vn N
exch dup
0 gt { pop (O) } { 0 lt { (X) } { ( ) } ifelse } ifelse
print
} repeat
() =
} repeat
pop
() =
} bind def
/DotProduct { % [Vector1] [Vector2] DotProduct scaler
[ 3 1 roll
dup length 1 sub 0 1 3 -1 roll {
3 copy get 3 1 roll exch get mul 3 1 roll
} for
pop pop
]
0 exch
{ add } forall
} bind def
/CheckerVector [
[ 1 1 1 0 0 0 0 0 0 ]
[ 0 0 0 1 1 1 0 0 0 ]
[ 0 0 0 0 0 0 1 1 1 ]
[ 1 0 0 1 0 0 1 0 0 ]
[ 0 1 0 0 1 0 0 1 0 ]
[ 0 0 1 0 0 1 0 0 1 ]
[ 1 0 0 0 1 0 0 0 1 ]
[ 0 0 1 0 1 0 1 0 0 ]
] def
/KachiMake { % [Map Vector] KachiMake integer
dup length sqrt cvi 0 exch
CheckerVector
{
% [Map] 0 N [V]
3 index DotProduct
dup
% [Map] 0 N V V
2 index eq {
pop pop 1 exch exit
} {
% [Map] 0 N V
1 index neg eq {
pop -1 exch exit
} if
} ifelse
} forall
pop exch pop
} bind def
/Okeru { % [Map] Okeru [Okeru]
[ exch dup length 1 sub
0 1 3 -1 roll
{
2 copy get 0 eq {
exch
} {
pop
} ifelse
} for
pop
]
} bind def
/RandomPlayer { % [Map] player RandomPlayer [Map'] bool
% player: -1 or 1
exch dup dup 4 2 roll Okeru
dup length dup
0 gt {
rand 1024 idiv exch mod
get
exch put true
} {
pop pop pop pop false
} ifelse
} bind def
/Marubatsu { % [Map] int /Proc1 /Proc2 Marubatsu int
% Result: 0 : draw, 1: O win, -1: X win
% Initial Table
4 copy 2 index 1 lt {
exch
} if
pop
cvx exec
{
KachiMake dup 0 eq
{
pop
3 -1 roll neg 3 1 roll
Marubatsu
} if
} {
pop 0
} ifelse
} bind def
% Randmize
realtime srand
% Counter
[0 0 0]
10000 {
[0 0 0 0 0 0 0 0 0] 1 /RandomPlayer /RandomPlayer Marubatsu
5 1 roll pop pop pop
% ShowMap
pop
1 add 2 copy 2 copy get 1 add put pop
} repeat
aload pop
(O win : ) print =
(draw : ) print =
(X win : ) print =
|
なるほど・・・ ようやく理解しました。
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]);
}
}
|
あぅ・・・匿名で投稿してしまいました。 ↑はうちの投稿したものです。
勝利条件の判定をきれいに書けないかなってことばかり考えてました。で揃ってる並びって等差数列だなとか思って、真ん中の値と可能な公差の連想リストで表現してみました。
他はわりと適当です。
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 | (defconstant +diff-alist+ '((1 1) (3 3) (4 1 2 3 4) (5 3) (7 1)))
(defun wonp (ps)
(loop for (m . ds) in +diff-alist+
if (member m ps)
do (loop for d in ds
if (and (member (- m d) ps) (member (+ m d) ps))
do (return-from wonp t))))
(defun execute-game (player-O player-X)
(do ((vacant (list 0 1 2 3 4 5 6 7 8))
(turn :O (if (eq turn :O) :X :O))
(pl1 player-O pl2)
(pl2 player-X pl1)
(ps1 () ps2)
(ps2 () ps1))
((null vacant) :draw)
(multiple-value-setq (ps1 vacant) (funcall pl1 ps1 ps2 vacant))
(if (wonp ps1) (return turn))))
(let ((state (make-random-state t)))
(defun random-player (ps-player ps-opponent vacant)
(declare (ignore ps-opponent))
(let ((r (random (length vacant) state)))
(if (zerop r)
(values (cons (car vacant) ps-player) (cdr vacant))
(let ((p (pop (cdr (nthcdr (1- r) vacant)))))
(values (cons p ps-player) vacant))))))
;;; test
(let ((a 0) (b 0) (c 0))
(dotimes (i 10000)
(case (execute-game #'random-player #'random-player)
(:O (incf a))
(:X (incf b))
(:draw (incf c))))
(format t "O win: ~A~%X win: ~A~%draw: ~A" a b c))
|
ランダムプレーヤーではなくて、最初に手順を決定してしまうので、条件を満たして無いかも…。
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 | #!c:\perl\bin\perl.exe
use strict;
my $player1 = 0;
my $player2 = 0;
my $draw = 0;
my $result;
for(1..10000){
$result = &doMarubatsu();
if($result == 1){
$player1++;
}elsif($result == 2){
$player2++;
}elsif($result == 0){
$draw++;
}
}
print "player1: $player1 \n";
print "player2: $player2 \n";
print "draw: $draw \n";
sub doMarubatsu{
my @masu = (0,0,0,0,0,0,0,0,0);
my @turn = &shuffle(0..8);
my $result;
for(0..8){
@masu[$turn[$_]] = $_ % 2 + 2;
$result = &marubatsuJudge(@masu);
if($result != 0){last;}
}
return $result;
}
sub marubatsuJudge{
my @winCase = ([0,1,2],[0,3,6],[0,4,8],[1,4,7],[2,5,8],[2,4,6],[3,4,5],[6,7,8]);
my @masu = @_;
my $checkNum;
for(@winCase){
$checkNum = 1;
for(@$_){
$checkNum *= $masu[$_];
}
if($checkNum == 8 or $checkNum == 27){last;}
}
if($checkNum == 8){
return 1;
}elsif($checkNum == 27){
return 2;
}else{
return 0;
}
}
sub shuffle{
my @array = @_;
my @newArray;
while(@array){
push(@newArray,splice(@array,rand(@array),1));
}
return @newArray;
}
|
直線に3個揃っているかの判定を整数計算ですませる方法。 縦3本、横3本、斜め2本、計8本の直線に素数 2,3,5,7,11,13,17,19 を対応させる。 各マスには通っている直線の素数の積を対応させる。つまり 2*7*17 2*11 2*13*19 3*7 3*11*17*19 3*13 5*7*19 5*11 5*13*17 のように整数を配置する。 ○(または×)が置いてあるマスの場所の整数すべての積をとって それが (2*3*5*7*11*13*17*19)^2 を 割り切ったらどの直線でも3個揃っていない、 割り切れなかったらどこかの直線で3個揃っている、 と判定できる。 コードはさかいさんの #6209 の判定関数のインタフェースをお借りしてます。
1 2 3 4 5 6 7 8 9 | judgeTable :: Array Pos Integer
judgeTable = array ((0,0), (2,2)) [
((0,0), 2*7*17), ((0,1), 2*11 ), ((0,2), 2*13*19),
((1,0), 3*7 ), ((1,1), 3*11*17*19), ((1,2), 3*13 ),
((2,0), 5*7*19), ((2,1), 5*11 ), ((2,2), 5*13*17)]
win :: Board -> View -> Bool
win b v = (2*3*5*7*11*13*17*19)^2 `mod` pr /= 0 where
pr = product [judgeTable!p | (p, ox) <- assocs b, ox == Just (v True)]
|
先手:後手:引分 = 5xxx:2xxx:1xxx でした。
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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /*-
* The MIT License
*
* Copyright (c) 2008 虹原いんく
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* ox.c : http://ja.doukaku.org/173/ 寄稿用 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct tag_game
{
int map_x;
int map_o;
} OXGAME , *LPOXGAME;
#define Match(x,pat) ((((x) & 0x0FFF) & (pat)) == (pat))
/* フィールド固有フラグ */
#define NO_0 0x0001
#define NO_1 0x0002
#define NO_2 0x0004
#define NO_3 0x0010
#define NO_4 0x0020
#define NO_5 0x0040
#define NO_6 0x0100
#define NO_7 0x0200
#define NO_8 0x0400
/* フィールド逆引き用 */
static int map_num[9] = {
NO_0, NO_1, NO_2,
NO_3, NO_4, NO_5,
NO_6, NO_7, NO_8,
};
/* フィールド表示 */
static char ox_print_sub(LPOXGAME g, int pat)
{
if(Match(g->map_o, (pat))) return 'O';
if(Match(g->map_x, (pat))) return 'X';
return ' ';
}
int ox_print(LPOXGAME g)
{
printf( "+-+-+-+\n"
"|%c|%c|%c|\n"
"+-+-+-+\n"
"|%c|%c|%c|\n"
"+-+-+-+\n"
"|%c|%c|%c|\n"
"+-+-+-+",
ox_print_sub(g,NO_0), ox_print_sub(g,NO_1), ox_print_sub(g,NO_2),
ox_print_sub(g,NO_3), ox_print_sub(g,NO_4), ox_print_sub(g,NO_5),
ox_print_sub(g,NO_6), ox_print_sub(g,NO_7), ox_print_sub(g,NO_8));
return 0;
}
/* 点数計算 */
/* @- int ox_match(LPOXGAME g)
* ^^^ > 0 : win O
* == 0 : tie
* < 0 : win X
*/
static int ox_match_sub(int x)
{
int pts = 0;
if(Match( x , (NO_0|NO_1|NO_2))) pts++;
if(Match( x , (NO_3|NO_4|NO_5))) pts++;
if(Match( x , (NO_6|NO_7|NO_8))) pts++;
if(Match( x , (NO_0|NO_3|NO_6))) pts++;
if(Match( x , (NO_1|NO_4|NO_7))) pts++;
if(Match( x , (NO_2|NO_5|NO_8))) pts++;
if(Match( x , (NO_0|NO_4|NO_8))) pts++;
if(Match( x , (NO_2|NO_4|NO_6))) pts++;
return pts;
}
int ox_match(LPOXGAME g)
{
return ox_match_sub(g->map_o) - ox_match_sub(g->map_x);
}
/* 打ち位置計算 ロボ */
/* @- int ox_play(LPOXGAME g)
* ^^^ != 0 : 打ち位置
* ^^^ == 0 : error
*/
int ox_play(LPOXGAME g)
{
int x, m;
m = g->map_o | g->map_x;
if(Match(m, (NO_0|NO_1|NO_2|
NO_3|NO_4|NO_5|
NO_6|NO_7|NO_8))) return 0; /* map full */
for(;;) {
x = map_num[rand() % 9]; /* ←頭脳 */
if(!Match(m, x)) return x; /* 空いてるならここ */
}
return 0;
}
/* フィールドにコマ打ち */
int ox_write(LPOXGAME g, const char player, const int x)
{
int m;
m = g->map_o | g->map_x;
if(Match(m, (NO_0|NO_1|NO_2|
NO_3|NO_4|NO_5|
NO_6|NO_7|NO_8))) return 0; /* map full */
if(Match(m, x)) return 0; /* error */
if(player == 'O') g->map_o |= x; /* O プレイヤ処理 */
if(player == 'X') g->map_x |= x; /* X プレイヤ処理 */
return 0;
}
/* フィールド初期化処理 */
int ox_init(LPOXGAME g)
{
g->map_o = 0;
g->map_x = 0;
return 0;
}
/* OX プロセス */
int ox_proc(LPOXGAME g)
{
int x,t;
ox_init(g);
for(;;) {
/*---------*/
x = ox_play(g); /* 打てる場所を捜す */
ox_write(g, 'O', x); /* 打つ */
t = ox_match(g); /* 並んでいるか? */
if(t) break; /* 並んでいる */
if(!x) break; /* map full */
/*---------*/
x = ox_play(g);
ox_write(g, 'X', x);
t = ox_match(g);
if(t) break;
if(!x) break; /* map full */
}
ox_print(g); /* フィールド表示 */
t = ox_match(g);
if(t > 0) printf("... [Win: O]\n");
else if(t < 0) printf("... [Win: X]\n");
else printf("... [Tie]\n");
return t;
}
/* */
int main()
{
int i, t;
int o = 0,x = 0, ox = 0;
OXGAME g;
srand(time(0));
for(i= 0; i< 10000; i++) {
t = ox_proc(&g);
if(t > 0) o++;
else if(t < 0) x++;
else ox++;
/* printf("%ld) O:%ld, X:%ld, T:%ld\n", i, o, x, ox);*/
}
printf("O:%ld, X:%ld, T:%ld\n", o, x, ox);
return 0;
}
/**
O:5828, X:2899, T:1273
O:5785, X:2911, T:1304
O:5863, X:2899, T:1238
**/
|
実行結果:
arc> (nplay-ox 10000)
o win: 5835
x win: 2889
draw: 1276
nil
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 | (def lset<= (s1 s2)
(if (is s1 nil) t
(mem (car s1) s2) (lset<= (cdr s1) s2)
nil))
(def check (lis)
(when (< (len lis) 3) nil)
(or (lset<= '(1 2 3) lis)
(lset<= '(1 4 7) lis)
(lset<= '(1 5 9) lis)
(lset<= '(2 5 8) lis)
(lset<= '(3 5 7) lis)
(lset<= '(3 6 9) lis)
(lset<= '(4 5 6) lis)
(lset<= '(7 8 9) lis)))
(def o-player (omark xmark pool)
(withs (picked (random-elt pool)
om (cons picked omark))
(if (check om) 'o
(is (cdr pool) nil) 'd
(x-player om xmark (rem picked pool)))))
(def x-player (omark xmark pool)
(withs (picked (random-elt pool)
xm (cons picked xmark))
(if (check xm) 'x
(o-player omark xm (rem picked pool)))))
(def nplay-ox (n)
(let wl (map (fn (_) (o-player nil nil (range 1 9))) (range 1 n))
(prn "o win: " (count 'o wl))
(prn "x win: " (count 'x wl))
(prn "draw: " (count 'd wl))
nil))
|
作ったので投稿してみます 実行結果 Player1 : 585272 Player2 : 288196 Draw : 126532
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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | #include <iostream>
#include <ctime>
//#define DEBUG
const int CLEAR_LINE = 3;
const int HEIGHT = 3;
const int WIDTH = 3;
const int TEST = 1000000;
enum FIELD_STATE
{
NON_PLAYER,
PLAYER1,
PLAYER2
};
enum RESULT
{
NEXT,
PLAYER1_WIN,
PLAYER2_WIN,
DRAW
};
class Field
{
private:
FIELD_STATE m_field[WIDTH][HEIGHT];
public:
Field() { }
~Field() { }
void init()
{
for(int i=0; i<WIDTH; i++)
{
for(int j=0; j<HEIGHT; j++)
{
m_field[i][j] = NON_PLAYER;
}
}
}
void print()
{
for(int i=0; i<HEIGHT; i++)
{
for(int j=0; j<WIDTH; j++)
{
switch(m_field[i][j])
{
case PLAYER1: std::cout << "○" << std::flush; break;
case PLAYER2: std::cout << "×" << std::flush; break;
default : std::cout << " " << std::flush; break;
}
if(j != WIDTH-1) std::cout << "|" << std::flush;
}
if(i != HEIGHT-1) std::cout << "\n----------" << std::endl;
}
}
void setField(int x, int y, FIELD_STATE in_PlayerType)
{
m_field[x][y] = in_PlayerType;
}
FIELD_STATE getField(int in_height, int in_width)
{
return m_field[in_height][in_width];
}
};
class Player
{
private:
FIELD_STATE m_player;
public:
Player() { }
virtual ~Player() { }
Player(FIELD_STATE in_player)
{
m_player = in_player;
}
void setPlayer(FIELD_STATE in_player)
{
m_player = in_player;
}
FIELD_STATE getPlayer()
{
return m_player;
}
virtual void turn(Field* in_field)
{
int x;
int y;
while(true)
{
x = rand() % WIDTH;
y = rand() % HEIGHT;
if(in_field->getField(x, y) == NON_PLAYER)
{
in_field->setField(x, y, m_player);
return;
}
}
}
};
class Judg
{
public:
Judg() { }
virtual ~Judg() { }
RESULT check(Field* in_field)
{
int player1 = 0;
int player2 = 0;
// 横
for(int i=0; i<WIDTH; i++)
{
player1 = 0;
player2 = 0;
for(int j=0; j<HEIGHT; j++)
{
switch(in_field->getField(i, j))
{
case NON_PLAYER: break;
case PLAYER1: player1++; break;
case PLAYER2: player2++; break;
}
if(player1+player2 != j+1) break;
}
if(player1 == CLEAR_LINE) return PLAYER1_WIN;
if(player2 == CLEAR_LINE) return PLAYER2_WIN;
}
// 縦
for(int i=0; i<HEIGHT; i++)
{
player1 = 0;
player2 = 0;
for(int j=0; j<WIDTH; j++)
{
switch(in_field->getField(j, i))
{
case NON_PLAYER: break;
case PLAYER1: player1++; break;
case PLAYER2: player2++; break;
}
if(player1+player2 != j+1) break;
}
if(player1 == CLEAR_LINE) return PLAYER1_WIN;
if(player2 == CLEAR_LINE) return PLAYER2_WIN;
}
// 斜め
if(WIDTH != HEIGHT) return NEXT;
player1 = 0;
player2 = 0;
for(int i=0, j=0; i<WIDTH && j<HEIGHT; i++, j++)
{
switch(in_field->getField(i, j))
{
case NON_PLAYER: break;
case PLAYER1: player1++; break;
case PLAYER2: player2++; break;
}
if(player1+player2 != j+1) break;
else if(player1 == CLEAR_LINE) return PLAYER1_WIN;
else if(player2 == CLEAR_LINE) return PLAYER2_WIN;
}
player1 = 0;
player2 = 0;
for(int i=WIDTH-1, j=0; i>=0 && j<HEIGHT; i--, j++)
{
switch(in_field->getField(i, j))
{
case NON_PLAYER: break;
case PLAYER1: player1++; break;
case PLAYER2: player2++; break;
}
if(player1+player2 != j+1) break;
else if(player1 == CLEAR_LINE) return PLAYER1_WIN;
else if(player2 == CLEAR_LINE) return PLAYER2_WIN;
}
for(int i=0; i<WIDTH; i++)
{
for(int j=0; j<HEIGHT; j++)
{
if(in_field->getField(i, j) == NON_PLAYER)
{
return NEXT;
}
}
}
return DRAW;
}
};
int main()
{
Field field;
Judg judg;
RESULT state;
Player player[2];
int winner[3] = {0, 0, 0};
int i;
bool game;
srand( static_cast<unsigned int>(time(NULL)) );
player[0].setPlayer(PLAYER1);
player[1].setPlayer(PLAYER2);
for(int s=0; s<TEST; s++)
{
i = 0;
game = true;
field.init();
while(game)
{
player[i].turn(&field);
#ifdef DEBUG
std::cout << std::endl;
field.print();
std::cout << std::endl;
#endif
state = judg.check(&field);
switch(state)
{
case NEXT: break;
case PLAYER1_WIN:
case PLAYER2_WIN:
#ifdef DEBUG
std::cout << "Player " << (i+1) << " Won" << std::endl;
#endif
winner[i]++;
game = false;
break;
case DRAW:
#ifdef DEBUG
std::cout << "Draw Game" << std::endl;
#endif
winner[2]++;
game = false;
break;
}
i = i ^ 0x00000001;
}
}
std::cout << "Result" << std::endl;
std::cout << "Player1 :" << winner[0] << std::endl;
std::cout << "Player2 :" << winner[1] << std::endl;
std::cout << "Draw :" << winner[2] << std::endl;
return EXIT_SUCCESS;
}
|
0x1c0, 0x124, 0x111, 0x92, 0x54, 0x49, 0x38, 0x7
は3つ並んだかどうか判定するためのマスクです。例えば、0x111は2進数で100010001となりますが、これを3ビットずつ並べると
100
010
001
となり、斜めの判定パターンになってることが分かります。これと○・×のビット配列の論理和を取ることで、並んだかの判定を行うことが出来ます。
実行例です。
ruby marubatu.rb
{"PLAYER 0"=>5870, "PLAYER 1"=>2828, "DRAW"=>1302}
PLAYER 0が先手、 PLAYER 1が後手です。
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 | class Judge
def initialize(player1, player2)
@players = []
@players[0] = player1
@players[1] = player2
end
def is_end?(ban)
[0x1c0, 0x124, 0x111, 0x92, 0x54, 0x49, 0x38, 0x7].each do |mask|
if ban & mask == mask then
return true
end
end
return false
end
KOMA = {0 => " ", 1 => "○", 2 => "×"}
def display(play1, play2)
print "---\n"
(0..2).each do |i|
(0..2).each do |j|
print KOMA[(play1 & 1) + ((play2 & 1) << 1)]
play1 >>= 1
play2 >>= 1
end
print "\n"
end
print "---\n"
print "\n"
end
def play(vervose = false)
bans = [0, 0]
curplayer = 0
(1..9).each do |no|
other = 1 - curplayer
bans[curplayer] = @players[curplayer].think(bans[curplayer], bans[other])
if vervose then
display(bans[0], bans[1])
end
if is_end?(bans[curplayer]) then
return "PLAYER #{curplayer}"
end
curplayer = other
end
"DRAW"
end
end
class Player
def think(myban, yourban)
r = 0
begin
r = rand(9)
end while ((myban | yourban) & (1 << r) != 0)
myban | (1 << r)
end
end
result = Hash.new(0)
jd = Judge.new(Player.new, Player.new)
(1..10000).each do |n|
result[jd.play] += 1
end
p result
|
wins:勝ちのパターン
isWinner:勝者かどうか
judge:ゲームの終了判定
operators:可能な手を返す
game:2つの戦略を対戦させる
randomDecision:ランダム・プレーヤーの戦略
対戦させると、マルの5877勝2835敗1288分
Timing[
result=Table[game[randomDecision,randomDecision],{10000}];
Count[result,#]&/@{1,-1,0}
]
{7.688 Second, {5877, 2835, 1288}}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | wins={{1,2,3},{4,5,6},{7,8,9},{1,4,7},{2,5,8},{3,6,9},{1,5,9},{3,5,7}};
isWinner[player_]:=MemberQ[Length[Intersection[player,#]]&/@wins,3]
judge[{p1_,p2_}]:=
If[isWinner@p1,1,
If[isWinner@p2,-1,
If[Length@p1==5,0,Null]]]
operators[state_]:=Complement[Range@9,Flatten@state]
game[decision1_,decision2_]:=Module[{state={{},{}},result=Null},
While[result===Null,
If[Length@state[[1]]==Length@state[[2]],
AppendTo[state[[1]],decision1[Sort/@state]],
AppendTo[state[[2]],decision2[Sort/@state]]];
result=judge@state];
result]
randomDecision[state_]:=With[{x=operators@state},
x[[Random[Integer,{1,Length@x}]]]]
|
微妙に偏りが・・・。 win:5396 lose:2607 draw:1997 win:5531 lose:2929 draw:1541 win:5604 lose:2793 draw:1604 10万回 win:55665 lose:28507 draw:15829
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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | <?php
main();
exit();
function main()
{
$player1 = new Player(1);
$player2 = new Player(2);
$field = new Field();
if (!(is_object($player1) && is_object($player2) && is_object($field)))
return false;
$field->reset_field();
$winCount = array("win" => 0, "lose" => 0, "draw" => 0);
while (array_sum($winCount) <= 100000) {
if (!($player1->set_mark($field)))
return false;
$check = $field->check_winner($player1->senko);
if ($check == -1) {
return false;
} else if ($check == 1) {
$winCount["win"]++;
} else if ($check == 2) {
$winCount["draw"]++;
}
if ($check > 0) {
$field->reset_field();
continue;
}
if (!($player2->set_mark($field)))
return false;
$check = $field->check_winner($player2->senko);
if ($check == -1) {
return false;
} else if ($check == 1) {
$winCount["lose"]++;
$field->reset_field();
}
}
print "win:{$winCount['win']} lose:{$winCount['lose']} draw:{$winCount['draw']}";
return;
}
//盤面クラス
class Field
{
//盤面情報
protected $field;
//盤面リセット
function reset_field()
{
$this->field = array(array(0,0,0),array(0,0,0),array(0,0,0));
}
/*
* 盤面取得
*/
function get_field()
{
return $this->field;
}
/*
* 勝者チェック
* @param integer $player 1->先攻 2->後攻
* @return integer -1->失敗 0->未勝利 1->勝利 2->引き分け
*/
function check_winner($player)
{
if (!($player == 1 || $player == 2))
return -1;
//縦横
for ($i = 0; $i < 2; $i++) {
if (($this->field[$i][0] == $player &&
$this->field[$i][1] == $player &&
$this->field[$i][2] == $player) ||
($this->field[0][$i] == $player &&
$this->field[1][$i] == $player &&
$this->field[2][$i] == $player)) {
return 1;
}
}
//斜め
if ($this->field[1][1] == $player &&
($this->field[0][0] == $player &&
$this->field[2][2] == $player) ||
($this->field[0][2] == $player &&
$this->field[2][0] == $player)) {
return 1;
}
//引き分け
$draw = true;
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
if ($this->field[$i][$j] == 0)
$draw = false;
}
}
if ($draw)
return 2;
return 0;
}
/*
* マークをつける
* @return boolean
* @param integer $player 1->先攻 2->後攻
* @param integer $x x座標
* @param integer $y y座標
*/
function set_mark($player, $x, $y)
{
if (!($player == 1 || $player == 2))
return false;
if (!(0 <= $x && $x <= 2))
return false;
if (!(0 <= $y && $y <= 2))
return false;
if ($this->field[$y][$x] != 0)
return false;
$this->field[$y][$x] = $player;
return true;
}
}
//プレイヤー
class Player
{
//先攻後攻 1->先攻 2->後攻
public $senko;
/*
* コンストラクタ
* @param integer $senko
*/
function __construct($senko)
{
if (!($senko == 1 || $senko == 2))
return false;
$this->senko = $senko;
}
/*
* 打つ
* @param Field $field
* @return boolean
*/
function set_mark(&$field)
{
$point = $this->search_field($field->get_field());
if (count($point) != 2)
return false;
if (!$field->set_mark($this->senko, $point["x"], $point["y"]))
return false;
return true;
}
/*
* 打つ場所を決める
* @param array[3][3] $field フィールド情報
* @return array 失敗時->array() 成功時->array(x, y)
*/
function search_field($field)
{
if (!(is_array($field) && count($field) == 3))
return array();
for ($i = 0; $i < 3; $i++) {
if (!(is_array($field[$i]) && count($field[$i]) == 3))
return array();
}
$able = array();
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
if ($field[$i][$j] == 0) {
$able[] = array("x" => $j, "y" => $i);
}
}
}
$count = count($able);
if ($count == 0)
return array();
return $able[mt_rand() % $count];
//return $able[rand() % $count];
}
}
|
>#6283 check_winnerの、縦横のfor文、斜めのif文にバグがいるぽぃ?
「乱数」でググっていたら偶然見つけたのですが、乱数の偏りについては、前にも以下で話題になっていたようですね。
see: 重複無し乱数 どう書く?
以下実行結果です。
1> c(tic_tac_toe).
{ok,tic_tac_toe}
2> tic_tac_toe:start().
first:5835,second:2957,draw:1208
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 | -module(tic_tac_toe).
-import(io).
-import(lists).
-import(random).
-export([start/0]).
create_board() -> lists:map(fun(_) -> 0 end,lists:seq(1,9,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, P rem 2 + 1, 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).
start() ->
R = lists:map(fun(_) -> process({fun random_choice/2,fun random_choice/2}) 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])).
|
とりあえず愚直に。 player1:5803 player2:2916 draw:1281
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 | type board = int list;;
type player = P1 | P2;;
type result = Won of player | Draw | Next of board;;
let rec mark board n =
let idx = Random.int 9 in
if List.nth board idx != 0 then mark board n
else
let _, board' = List.fold_left (fun (idx', res) n' ->
(idx' + 1, (if idx' == idx then res @ [n] else res @ [n']))
) (0, []) board in board'
let masks = [
[1; 1; 1; 0; 0; 0; 0; 0; 0];
[0; 0; 0; 1; 1; 1; 0; 0; 0];
[0; 0; 0; 0; 0; 0; 1; 1; 1];
[1; 0; 0; 1; 0; 0; 1; 0; 0];
[0; 1; 0; 0; 1; 0; 0; 1; 0];
[0; 0; 1; 0; 0; 1; 0; 0; 1];
[1; 0; 0; 0; 1; 0; 0; 0; 1];
[0; 0; 1; 0; 1; 0; 1; 0; 0];
];;
let judge board =
let judge' board =
List.exists (fun mask ->
mask = List.map (fun (a, b) -> a * b) (List.combine mask board)
) masks
in
if judge' board then Won P1
else if judge' (List.map (~-) board) then Won P2
else if List.mem 0 board then Next board else Draw
;;
let rec player1 board =
match judge (mark board 1) with
| Next board' -> player2 board'
| _ as x -> x
and player2 board =
match judge (mark board ~-1) with
| Next board' -> player1 board'
| _ as x -> x
;;
let rec play (p1, p2, draw) = function
| 0 -> (p1, p2, draw)
| _ as n ->
match player1 [0; 0; 0; 0; 0; 0; 0; 0; 0] with
| Won P1 -> play (p1 + 1, p2, draw) (n-1)
| Won P2 -> play (p1, p2 + 1, draw) (n-1)
| Draw -> play (p1, p2, draw + 1) (n-1)
| _ -> raise (Failure "unknown")
;;
let main =
Random.self_init ();
let p1, p2, draw = play (0, 0, 0) 10000 in
Printf.printf "player1:%d player2:%d draw:%d\n" p1 p2 draw
|
初めて投稿します。
そろっているかの判定には[1..100]>>=penさんの6228を利用させてもらいました。
> table(sapply(1:1000,function(x){marubatu()}))
Player1 Player2 draw
571 295 134
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | marubatu <- function(){
prime <- c(2*7*17,2*11,2*13*19,3*7,3*11*17*19,3*13,5*7*19,5*11,5*13*17)
my_rand <- sample(9,9)
ban <- c()
for(i in seq(9)){
if(i %% 2 != 0){
vec <- prime[(my_rand[((1:9) %% 2) * (1:9)])[(1:((i+1)/2))]]
if(c(2*3*5*7*11*13*17*19)^2 %% prod(vec) != 0){
return("Player1")
}else{}
}else{
vec <- prime[(my_rand[(1-((1:9) %% 2)) * (1:9)])[(1:(i/2))]]
if(c(2*3*5*7*11*13*17*19)^2 %% prod(vec) != 0){
return("Player2")
}else{
}
}
}
return("draw")
}
table(sapply(1:10000,function(x){marubatu()}))
|
判定おかしかったですね。 修正しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 79c79
< for ($i = 0; $i < 2; $i++) {
---
> for ($i = 0; $i < 3; $i++) {
92,95c92,95
< ($this->field[0][0] == $player &&
< $this->field[2][2] == $player) ||
< ($this->field[0][2] == $player &&
< $this->field[2][0] == $player)) {
---
> (($this->field[0][0] == $player &&
> $this->field[2][2] == $player) ||
> ($this->field[0][2] == $player &&
> $this->field[2][0] == $player))) {
|
賢いプレーヤのいるマルバツゲームの布石として作ってみました. 空欄の位置を取得して, 適当に選んだ位置に印を付けるようなアルゴリズムになってると思います.
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 | class Game
def initialize(n=1)
@n, @out = n, Array.new(n)
@p0, @p1 = Player.new, Player.new
@out.map!{MaruBatu.new(@p0, @p1).run} #collect results
# p @p0
end
class MaruBatu
def initialize(*players)
@size = 3
@b = Array.new(@size){Array.new(@size)} #board
@p = [:b, :w].zip(players). #players list
map{|m,p| {:mark => m, :player => p}}
end
def run
move until win? or pos_list.empty?
win? ? cur[:player].win : @p.each{|h| h[:player].even}
self
end
private
def cur() @p.first end #current move player
def pos_list #get positions blanked
scan(@b).reject(&:first).map(&:last) end
def win?
cross = [proc{|x,pos| pos.first == pos.last},
proc{|x,pos| pos.first + pos.last == @size-1}].
map{|p| scan(@b).select(&p).map(&:first)}
(@b + @b.transpose + cross).
any?{|a| a.first and a.all?{|x| a.first == x}}
end
def move
@p.reverse! #change the move of player
i,j = *cur[:player].set(pos_list)
@b[i][j] = cur[:mark] and self
end
def scan(mat,&proc) #iterate a matx with 2-dim index as a 1-dim one
mat.each_with_index.inject([]){|a,rowi|
rowi.first.each_with_index.inject(a){|b,xj|
b << [xj.first, [rowi,xj].map(&:last)]}}.map(&proc)
end
end
class Player
def initialize() @win, @even = 0, 0 end
def set(pos) pos.choice end
def win() @win += 1 end
def even() @even += 1 end
end
end
Game.new(100)
|
「プレイヤーを簡単に差し換えることができる設計を目指してください。」まったく考えずに作りました。
先手勝利=6162 後手勝利=2590 引き分け=1248
圧倒的に先手有利になりました。
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 | import Random
import List
data WinDraw = First|Second|Draw deriving (Eq,Show)
-- shuffle
randomN :: Int->StdGen -> (StdGen,[Int])
randomN n stdGen = mapAccumL (\r lim->swapTuple (randomR (0,lim) r)) stdGen [n-1,n-2..0]
where swapTuple (a,b) = (b,a)
bingo ::Int->StdGen->([Int],StdGen)
bingo n stdGen = let (nextGen,xs)= (randomN n stdGen)
in (snd $ mapAccumL f [1..n] xs,nextGen)
where f xs x = (deleteAt x xs,xs !! x)
deleteAt i xs = take i xs ++ tail (drop i xs)
whichWin xs = let (first,second) = separate xs
turnFirst = turnToWin first
turnSecond = turnToWin second
in case (turnFirst,turnSecond) of
(Nothing,Nothing) -> Draw
(tf,Nothing) -> First
(Nothing,ts) -> Second
(tf,ts)|tf <= ts -> First
|otherwise -> Second
--[1,2,3,4,5,6,7,8,9] -> ([1,3,5,7,9],[2,4,6,8])
separate xs = (map head $ every 2 xs,map head $ every 2 $ tail xs)
every n xs = unfoldr f xs
where f [] = Nothing
f cs = Just (splitAt n cs)
-- how many turns do you need to win?
turnToWin xs = elemIndex True $ map isWinPattern $ inits xs
isWinPattern xs = or $ map (contain xs) [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]]
contain xs ys = length xs - (length (xs \\ ys) ) == length ys
--
test n = whichWin $ fst $ bingo 9 (mkStdGen n)
main = let wl =map test [1..10000]
in print $ [length $ filter (==First) wl,length $ filter (==Second) wl,length $ filter (==Draw) wl]
{-
*Main> main
[6162,2590,1248]
-}
|
groovyのカバレッジをあげるべく、書いてみました。 動かしてみたら、すごく遅かったです。
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 | class Report{
def circleWin = 0
def xrosWin = 0
def tie = 0
}
class Game {
def curPlayer
def circlePlayer
def xrosPlayer
def report
def board
def play(){
board = new Board()
circlePlayer.type = 'CIRCLE'
xrosPlayer.type = 'XROS'
curPlayer = xrosPlayer
while( !board.getWinner() ){
curPlayer.set(board)
swapPlayer()
}
def winner = board.getWinner()
if (winner == 'CIRCLE') report.circleWin ++
if (winner == 'XROS') report.xrosWin ++
if (winner == 'TIE') report.tie++
}
def swapPlayer(){
curPlayer = (curPlayer == circlePlayer) ? xrosPlayer : circlePlayer
}
}
class Player {
def type
def set(board){
board.set(select(board.available()),type[0])
}
def select(available){
def idx = (Math.floor(Math.random() * available.size())) as int
available[idx]
}
}
class Board {
def field = []
def winPattern = [ [0,1,2], [3,4,5], [6,7,8],
[0,3,6], [1,4,7], [2,5,8],
[0,4,8], [2,4,6]]
Board(){
9.times{field[it] = ''}
}
def available(){
def result = []
field.eachWithIndex{var, idx ->
if (!var) result << idx
}
result
}
def getWinner(){
def winner = ''
winPattern.each{ pattern ->
def var = ''
pattern.each{ idx ->
var += field[idx]
}
switch (var){
case 'CCC': winner = 'CIRCLE'; break;
case 'XXX': winner = 'XROS'; break;
}
}
if (winner == '' && available() == []) winner = 'TIE'
winner
}
def set(idx, var){ field[idx] = var }
}
def report = new Report()
def player1 = new Player()
def player2 = new Player()
10000.times{
new Game(circlePlayer:player1, xrosPlayer:player2, report:report).play()
}
println report.dump()
|
groovyのカバレッジをあげるべく、書いてみました。動かしたら遅かったです。
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 | class Report{
def circleWin = 0
def xrosWin = 0
def tie = 0
}
class Game {
def curPlayer
def circlePlayer
def xrosPlayer
def report
def board
def play(){
board = new Board()
circlePlayer.type = 'CIRCLE'
xrosPlayer.type = 'XROS'
curPlayer = xrosPlayer
while( !board.getWinner() ){
curPlayer.set(board)
swapPlayer()
}
def winner = board.getWinner()
if (winner == 'CIRCLE') report.circleWin ++
if (winner == 'XROS') report.xrosWin ++
if (winner == 'TIE') report.tie++
}
def swapPlayer(){
curPlayer = (curPlayer == circlePlayer) ? xrosPlayer : circlePlayer
}
}
class Player {
def type
def set(board){
board.set(select(board.available()),type[0])
}
def select(available){
def idx = (Math.floor(Math.random() * available.size())) as int
available[idx]
}
}
class Board {
def field = []
def winPattern = [ [0,1,2], [3,4,5], [6,7,8],
[0,3,6], [1,4,7], [2,5,8],
[0,4,8], [2,4,6]]
Board(){
9.times{field[it] = ''}
}
def available(){
def result = []
field.eachWithIndex{var, idx ->
if (!var) result << idx
}
result
}
def getWinner(){
def winner = ''
winPattern.each{ pattern ->
def var = ''
pattern.each{ idx ->
var += field[idx]
}
switch (var){
case 'CCC': winner = 'CIRCLE'; break;
case 'XXX': winner = 'XROS'; break;
}
}
if (winner == '' && available() == []) winner = 'TIE'
winner
}
def set(idx, var){ field[idx] = var }
}
def report = new Report()
def player1 = new Player()
def player2 = new Player()
10000.times{
new Game(circlePlayer:player1, xrosPlayer:player2, report:report).play()
}
println report.dump()
|






syat
#6190()
Rating5/5=1.00
マルバツゲームは3×3の格子に交互に○と×を書き込み、先に縦・横・斜めに記号をそろえたほうが勝ちというおなじみのゲームです。
「毎ターン乱数を使って手を決めるランダムプレイヤー同士を対戦させる」というのが今回のお題です。 1万回対戦させ、勝ち・負け・引き分けの数を表示してください。 そして先手が有利であることを確かめてください。
良い手を思考するプレイヤーについては別のお題にしようと思っています。 プレイヤーを簡単に差し換えることができる設計を目指してください。
[ reply ]