マルバツゲーム:賢いプレイヤー
Posted feedbacks - Perl
4つの戦略(どのように石を置くか)を作りました:
1. put_stone_random ... ランダムに置く。
2. ps_prevent_losing ... リーチをかけられてるなら、防ぐ。さもなくば1.の戦略をとる。
3. ps_grasp_chance ...リーチをかけてるなら、そこにおいてゲームに勝つ。さもなくば2.の戦略をとる。
4. ps_smart ...
リーチをかけてるなら、そこにおいてゲームに勝つ。
リーチをかけられてるなら、防ぐ。
さもなくば、可能な限り、ダブルリーチを仕掛けに行く。
ps_smart を書くために、マルバツゲームを手で解きました。
各盤面に対する最善手を %ptn_pos_h に押し込めました。
蛇足:%ptn_pos_h を調整することで、 ps_smart の強さを調整できます。
実行結果: perl marubatsu.pl
***** ps_smart vs put_stone_random *****
game 1..10000
D=>115 o=>9885
***** put_stone_random vs ps_smart *****
game 10001..20000
D=>2373 x=>7627
***** ps_smart vs ps_smart *****
game 20001..20100
D=>100
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 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | $board = [map {[map {'-'} 0..2]} 0..2];
sub game {
clear($board);
game_loop(@_);
}
sub game_loop {
my ($player1, $player2) = @_;
my $w;
$player1->{put_stone}($player1->{stone});
if ($w=check($board)) { return $w; }
game_loop($player2,$player1);
}
sub put_stone_random {
my $stone = shift;
while (1) {
my($r,$c) = (int rand @$board, int rand @$board);
if (0<=$r and $r<@$board and
0<=$c and $c<@$board and
$board->[$r][$c] eq '-') {
$board->[$r][$c] = $stone;
last;
}
}
}
sub ps_prevent_losing { # ps :abbrev: put_stone
my $stone = shift;
my($r,$c) = pos_oppreach($board,$stone);
if (defined($r) and defined($c)) {
$board->[$r][$c] = $stone;
} else {
put_stone_random($stone);
}
}
sub ps_grasp_chance {
my $stone = shift;
my($r,$c) = pos_oppreach($board,oppstone($stone));
if (defined($r) and defined($c)) {
$board->[$r][$c] = $stone;
} else {
ps_prevent_losing($stone);
}
}
sub ps_smart {
my $stone = shift;
# ps_grasp_chance
my($r,$c) = pos_oppreach($board,oppstone($stone));
if (defined($r) and defined($c)) {
$board->[$r][$c] = $stone;
return;
}
# ps_prevent_losing
($r,$c) = pos_oppreach($board,$stone);
if (defined($r) and defined($c)) {
ps_prevent_losing($stone);
return;
}
# attack dreach # dreach :abbrev: double reach
($r,$c) = pos_smart($board,$stone);
if (defined($r) and defined($c)) {
$board->[$r][$c] = $stone;
return;
}
put_stone_random($stone);
}
sub pos_oppreach {
my($board,$stone) = @_;
my($r,$c);
($r,$c) = pos_oppreach_row($board,$stone);
if (defined($r) and defined($c)) { return ($r,$c); }
($r,$c) = pos_oppreach_column($board,$stone);
if (defined($r) and defined($c)) { return ($r,$c); }
($r,$c) = pos_oppreach_crossline($board,$stone);
if (defined($r) and defined($c)) { return ($r,$c); }
(undef, undef)
}
sub pos_oppreach_row {
my($board,$stone) = @_;
my @board_strs = join0s($board);
for (0..(@$board-1)) {
my $i = index_oppreach($board_strs[$_], $stone);
if (defined($i)) {
return ($_, $i);
}
}
(undef, undef);
}
sub pos_oppreach_column {
my($board,$stone) = @_;
my @board_strs = ($board->[0][0].$board->[1][0].$board->[2][0],
$board->[0][1].$board->[1][1].$board->[2][1],
$board->[0][2].$board->[1][2].$board->[2][2]);
for (0..(@$board-1)) {
my $i = index_oppreach($board_strs[$_], $stone);
if (defined($i)) {
return ($i, $_);
}
}
(undef, undef);
}
sub pos_oppreach_crossline {
my($board,$stone) = @_;
my @board_strs = get_crosslines($board);
my $i = index_oppreach($board_strs[0], $stone);
if (defined($i)) {
return ($i,$i);
}
$i = index_oppreach($board_strs[1], $stone);
if (defined($i)) {
return (@$board-1-$i,$i);
}
(undef, undef);
}
sub index_oppreach { # test: index_oppreach('o-o', 'x') == 1
my $s = $_[0];
my $o = oppstone($_[1]);
if ($o eq 'o') {
if ($s eq '-oo') { return 0; }
if ($s eq 'o-o') { return 1; }
if ($s eq 'oo-') { return 2; }
} else { # $o eq 'o'
if ($s eq '-xx') { return 0; }
if ($s eq 'x-x') { return 1; }
if ($s eq 'xx-') { return 2; }
}
undef;
}
sub oppstone {
$_[0] eq 'o' ? 'x' : $_[0] eq 'x' ? 'o' : undef;
}
my %ptn_pos_h;
{
my $ptn_pos_href = {
# play first
'--- --- ---' => [0,0],
'ox- --- ---' => [1,1], # [2,0] is also ok : [2,0] [1,0] [2,2]
'ox- -o- --x' => [1,0], # dreach
'o-x --- ---' => [2,0],
'o-x x-- o--' => [2,2], # dreach
'o-- --x ---' => [2,0], # dreach by gc
'o-- --- --x' => [2,0],
'o-- x-- o-x' => [0,2], # dreach ... same as 'o-x x-- o--'
'o-- -x- ---' => [2,2],
'o-x -x- --o' => [2,0], # dreach
# play second / first ps pos == (0 0)
'o-- --- ---' => [1,1],
'o-- -x- --o' => [2,1], # draw by gc
'o-- -x- -o-' => [2,2], # dreach by gc 1/5 , draw by gc 4/5
# play second / first ps pos == (1 1)
'--- -o- ---' => [0,0],
'x-- -o- --o' => [0,2], # draw by gc
# play second / first ps pos == (0 1)
'-o- --- ---' => [0,0],
'xoo --- ---' => [2,0],
'xoo o-- x--' => [2,2], # dreach
'xo- --o ---' => [2,0], # dreach by gc
'xo- --- o--' => [1,1], # draw by gc
'xo- o-- ---' => [2,1],
'xoo o-- -x-' => [2,2], # dreach
'xo- o-- ox-' => [0,2], # draw by gc
'xo- o-- -xo' => [0,2], # draw by gc
'xo- --- --o' => [1,1], # dreach by gc 1/5 , draw by gc 4/5
};
%ptn_pos_h = %$ptn_pos_href;
}
sub pos_smart { # assume: play first => 'o', play second => 'x', marubatsuptn
my($board,$stone) = @_;
for my $m (0..1) { # means: nflip
for my $n (0..3) { # means: nrot
my $ptn = match_board(brott($n,bflipt($m,$board)));
if (defined $ptn) {
#print "$ptn => @{$ptn_pos_h{$ptn}} : $m $n : debugwrite";
return to_a(pflipt($m,prott(-$n,$ptn_pos_h{$ptn})));
}
}
}
(undef, undef);
}
sub brott {
my($n, $board) = @_;
dotimes($n % 4, $board, \&brot);
}
sub bflipt {
my($n, $board) = @_;
dotimes($n % 2, $board, \&bflip);
}
sub pflipt {
my($n, $pos) = @_;
dotimes($n % 2, $pos, \&pflip);
}
sub prott {
my($n, $pos) = @_;
dotimes($n % 4, $pos, \&prot);
}
sub dotimes {
my($n, $obj, $coderef) = @_;
$n==0 ? $obj : dotimes($n-1, $coderef->($obj), $coderef);
}
sub brot {
trans(bflip($_[0]));
}
sub bflip {
[map {[reverse @$_];} @{$_[0]}];
}
sub pflip {
my @b = @{$_[0]};
$b[1] = 2 - $b[1]; # pos flip
\@b;
}
sub prot {
my @b = @{$_[0]};
$b[0]-=1; $b[1]-=1;
my @c = (-$b[1], $b[0]); # pos rot ... 90 degree
$c[0]+=1; $c[1]+=1;
\@c;
}
sub match_board {
my $board_str = join(' ', join0s(shift));
my @mp = grep { $board_str eq $_; } keys %ptn_pos_h;
(scalar @mp) ? $mp[0] : undef;
}
sub to_a {
@{$_[0]};
}
sub check {
my @b = @{$_[0]};
my $w;
# check rows
for my $r (0..2) {
my $l = $b[$r]->[0]; # abbrev: left of board
if ($l ne'-' and $l eq$b[$r]->[1] and $l eq$b[$r]->[2]) { return $l; }
}
# check columns
for my $c (0..2) {
my $t = $b[0]->[$c]; # abbrev: top of board
if ($t ne'-' and $t eq$b[1]->[$c] and $t eq$b[2]->[$c]) { return $t; }
}
# check crosslines
my $p = $b[0]->[0]; # abbrev: pos
if ($p ne'-' and $p eq$b[1]->[1] and $p eq$b[2]->[2]) { return $p; }
$p = $b[2]->[0];
if ($p ne'-' and $p eq$b[1]->[1] and $p eq$b[0]->[2]) { return $p; }
# check draw
if (filledp(\@b)) { return 'D'; } # means: draw
undef;
}
sub join0s {
my @b = @{$_[0]};
my @a;
$a[0] = join '', @{$b[0]};
$a[1] = join '', @{$b[1]};
$a[2] = join '', @{$b[2]};
@a;
# map {join '', @$_} @{$_[0]};
}
sub trans {
my $m = shift;
my $n = [];
for my $r (0..2) {
for my $c (0..2) {
$n->[$c][$r] = $m->[$r][$c];
}
}
$n;
}
sub get_crosslines {
my $b = shift;
join0s [[map {$b->[ $_][$_]} 0..(@$b-1)],
[map {$b->[@$b-1-$_][$_]} 0..(@$b-1)]];
}
sub filledp {
my $b = shift;
for my $r (0..(@$b-1)) {
for my $c (0..(@{$b->[$r]}-1)) {
if ($b->[$r][$c] eq '-') { return 0; }
}
}
1;
}
# utilities
sub clear {
my $b = shift;
for my $r (0..(@$b-1)) {
for my $c (0..(@{$b->[$r]}-1)) {
$b->[$r][$c] = '-';
}
}
}
################### run game ###################
$gc = 0; # abbrev: game count
sub game_times {
my($player1,$player2,$ngame) = @_;
my %wc;
print "***** $player1 vs $player2 *****\n";
print "game ", $gc+1, "..", $gc+$ngame, "\n";
$gc += $ngame;
for (1..$ngame) {
my $w = game({put_stone=>\&$player1, stone=>'o'},
{put_stone=>\&$player2, stone=>'x'});
$wc{$w}++;
}
while (my($k,$v) = each(%wc)) {
print "$k=>$v ";
}
print "\n";
}
$ngame = 10000;
game_times('ps_smart', 'put_stone_random', $ngame);
game_times('put_stone_random', 'ps_smart', $ngame);
game_times('ps_smart', 'ps_smart', 100);
|


syat
#6207()
Rating0/2=0.00
マルバツゲームで、賢いプレイヤーの思考ルーチンを実装してください。
賢いといってもいろいろありますが、
1.負けない
2.できるだけ勝つ
という条件でいってみたいと思います。
ランダムプレイヤーと1万回バトルした結果(勝ち・負け・分け)を表示してください。
先攻になっても後攻になっても無敗!となれば言うことなしです。
[ reply ]