Perl、147バイト(非競合、1移動あたり10秒以上かかります)
+4を含む -0p
プログラムが再生されXます。それは完璧なゲームをプレイします。
STDINでボードを入力します。例:
tictaclatin.pl
-X-O
-X--
X-X-
O--O
^D
ouptutは同じボードで、すべてがX置き換えられO、その逆も同様です。空のスポットは、Xがそこでプレーする場合の結果を示す数字で埋められ1、結果は勝ち、2引き分け3、負けとなります。終了したゲームは、色を逆にして同じ位置に戻るだけです。
この例では、出力は次のようになります。
1O1X
1O33
O3O3
X33X
そのためX、彼が左上にある3つのスポットでプレーすれば、そのポジションは勝ちです。他のすべての動きは負けます。
この混乱する出力は、移動後のゲームの継続方法を知りたい場合に実際に便利です。プログラムは常にプレイXするので、交換XしOて、の動きを見る必要がありますO。たとえばX、左上でプレーすることで勝つことは明らかですXが、3番目の位置でプレーする場合はどうでしょうか?出力をコピーし、O選択した移動の代わりに置き、他のすべての数字を-再度置き換えてください。
-OOX
-O--
O-O-
X--X
その結果:
3XXO
3X33
X3X3
O33O
明らかにすべての動きOは負けるはずです。左上でプレーするとどうやって負けますか?再びこれを行うにOは、左上に配置し、数字を-次のように置き換えます。
OXXO
-X--
X-X-
O--O
与える:
XOOX
1O33
O3O3
X33X
そのため、Xには勝つための唯一の方法があります。
XOOX
OO--
O-O-
X--X
与える
OXXO
XX33
X3X3
O33O
の状況Oは絶望的です。すべての動きがXすぐに勝つことができるようになったので、簡単にわかります。少なくとも3つのOを続けて試してみましょう。
OXXO
XX--
X-X-
O-OO
与える:
XOOX
OO13
O3O3
X3XX
X勝つ唯一の動きを再生します(これはXXXO3番目の列に沿って行います:
XOOX
OOO-
O-O-
X-XX
出力は次のとおりです。
OXXO
XXX-
X-X-
O-OO
ゲームはすでに終了しているためです。3列目で勝利を確認できます。
実際のプログラムtictaclatin.pl:
#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)
空のボードに適用すると、9506699の位置が評価され、コンピューターで30Gbと41分かかります。結果は次のとおりです。
2222
2222
2222
2222
したがって、すべての開始移動は引き分けです。だからゲームは引き分けです。
極端なメモリ使用量は、主にを使用した再帰によって引き起こされdo$0ます。プレーン関数を使用してこの154バイトバージョンを使用するには、3Gbと11分が必要です。
#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+&f%eeg&&(/1/||/2/-1)}f
これはより耐えられます(ただし、まだ多すぎるため、何かがメモリをリークしている必要があります)。
多くの高速化を組み合わせることで、この160バイトバージョン(5028168ポジション、4分、空のボードで800M)になります。
#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}f
最後の1つは0、勝利(混同しないでくださいO)、1引き分け、および2損失に使用します。これの出力も混乱を招きます。カラースワップのない勝利の場合、Xの勝ちの動きを埋めますが、入力ゲームが既に勝った場合は色のスワップを行い、動きを埋めません。
もちろん、ボードがいっぱいになると、すべてのバージョンが高速になり、使用するメモリが少なくなります。高速バージョンでは、2〜3回の移動が行われるとすぐに10秒以内に移動が生成されます。
原則として、この146バイトバージョンも動作するはずです。
#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^/sx,--$|;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)
しかし、私のマシンではperlのバグを引き起こし、コアをダンプします。
によって行われた6バイトの位置キャッシュ$$_||=が削除された場合、すべてのバージョンは原則として引き続き機能しますが、ほとんどのボードでのみ機能するほど多くの時間とメモリを使用します。しかし、理論的には少なくとも140バイトのソリューションがあります。
あなたが置く場合$\=(コスト:3バイト)の直前に$@<=>0、各出力ボードは、ボード全体の状態が続きます:1ためXの勝利、0引き分けと-1損失のために。
上記の最速バージョンに基づく対話型ドライバーを次に示します。ドライバーには、ゲームが終了したときのロジックがないため、自分で停止する必要があります。しかし、ゴルフのコードは知っています。提案された動きが-何にも置き換わることなく戻る場合、ゲームオーバーです。
#!/usr/bin/perl
sub f{
if ($p++ % 100000 == 0) {
local $| = 1;
print ".";
}
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}
# Driver
my $tomove = "X";
my $move = 0;
@board = ("----\n") x 4;
while (1) {
print "Current board after move $move ($tomove to move):\n ABCD\n";
for my $i (1..4) {
print "$i $board[$i-1]";
}
print "Enter a move like B4, PASS (not a valid move, just for setup) or just press enter to let the program make suggestions\n";
my $input = <> // exit;
if ($input eq "\n") {
$_ = join "", @board;
tr/OX/XO/ if $tomove eq "O";
$p = 0;
$@="";
%a = ();
my $start = time();
my $result = f;
if ($result == 1) {
tr/OX/XO/ if $tomove eq "O";
tr/012/-/;
} else {
tr/OX/XO/ if $tomove eq "X";
tr/012/123/;
}
$result = -$result if $tomove eq "O";
my $period = time() - $start;
print "\nSuggested moves (evaluated $p positions in $period seconds, predicted result for X: $result):\n$_";
redo;
} elsif ($input =~ /^pass$/i) {
# Do nothing
} elsif (my ($x, $y) = $input =~ /^([A-D])([1-4])$/) {
$x = ord($x) - ord("A");
--$y;
my $ch = substr($board[$y],$x, 1);
if ($ch ne "-") {
print "Position already has $ch. Try again\n";
redo;
}
substr($board[$y],$x, 1) = $tomove;
} else {
print "Cannot parse move. Try again\n";
redo;
}
$tomove =~ tr/OX/XO/;
++$move;
}