Perl, 147 Bytes (nicht konkurrierend, dauert mehr als 10 Sekunden pro Zug)
Beinhaltet +4 für -0p
Das Programm wird abgespielt X
. Es wird ein perfektes Spiel spielen.
Geben Sie die Karte über STDIN ein, z.
tictaclatin.pl
-X-O
-X--
X-X-
O--O
^D
Der Ausgang wird das gleiche Board sein, wobei alle X
durch ersetzt werden O
und umgekehrt. Die leeren Stellen werden mit einer Zahl gefüllt, die das Ergebnis angibt, wenn X dort spielen würde. 1
Dies bedeutet, dass das Ergebnis ein Gewinn, 2
ein Unentschieden und 3
ein Verlust ist. Ein fertiges Spiel gibt nur die gleiche Position mit umgekehrten Farben zurück.
In diesem Beispiel wäre die Ausgabe:
1O1X
1O33
O3O3
X33X
Die Position ist also ein Gewinn, X
wenn er an den drei Stellen oben und links spielt. Alle anderen Züge verlieren.
Diese verwirrende Ausgabe ist praktisch, wenn Sie wissen möchten, wie das Spiel nach einem Zug fortgesetzt wird. Da das Programm immer abgespielt wird X
, müssen Sie tauschen X
und O
die Züge für sehen O
. Hier ist zum Beispiel ziemlich klar, dass man X
gewinnt, wenn man oben links spielt, aber was ist, wenn man oben X
auf der dritten Position spielt? Kopieren Sie einfach die Ausgabe, setzen Sie eine O
anstelle der von Ihnen ausgewählten Bewegung ein und ersetzen Sie alle anderen Zahlen -
erneut durch. Hier also:
-OOX
-O--
O-O-
X--X
Ergebend:
3XXO
3X33
X3X3
O33O
Offensichtlich sollte jeder Zug O
verlieren. Wie verliert er also, wenn er oben links spielt? Tun Sie dies erneut, indem Sie O
oben links eingeben und die Ziffern ersetzen durch -
:
OXXO
-X--
X-X-
O--O
Geben:
XOOX
1O33
O3O3
X33X
X hat also nur einen Weg, um seinen Sieg zu erringen:
XOOX
OO--
O-O-
X--X
Geben
OXXO
XX33
X3X3
O33O
Die Situation für O
bleibt hoffnungslos. Es ist jetzt leicht zu erkennen, dass jeder Zug X
sofort gewinnen kann. Versuchen wir wenigstens, 3 O's hintereinander zu machen:
OXXO
XX--
X-X-
O-OO
Geben:
XOOX
OO13
O3O3
X3XX
X
spielt den einzigen Gewinnzug (beachten Sie, dass dies XXXO
entlang der dritten Spalte erfolgt:
XOOX
OOO-
O-O-
X-XX
Hier ist die Ausgabe:
OXXO
XXX-
X-X-
O-OO
weil das Spiel schon beendet war. Sie können den Gewinn in der dritten Spalte sehen.
Das eigentliche Programm 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)
Auf das leere Board angewendet werden 9506699 Positionen ausgewertet, was auf meinem Computer 30 GB und 41 Minuten dauert. Das Ergebnis ist:
2222
2222
2222
2222
Also zieht jeder Startzug. Das Spiel ist also ein Unentschieden.
Die extreme Speichernutzung wird hauptsächlich durch die Rekursion mit verursacht do$0
. Die Verwendung dieser 154-Byte-Version mit einer einfachen Funktion benötigt 3 GB und 11 Minuten:
#!/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
Das ist erträglicher (aber immer noch zu viel, etwas muss immer noch Speicher verlieren).
Das Kombinieren einer Reihe von Beschleunigungen führt zu dieser 160-Byte-Version (5028168 Positionen, 4 Minuten und 800 MB für die leere Karte):
#!/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
Letzteres wird 0
für einen Sieg (nicht zu verwechseln mit O
), 1
für ein Unentschieden und 2
für einen Verlust verwendet. Die Ausgabe von diesem ist auch verwirrender. Bei einem Gewinn ohne Farbtausch wird der Gewinnzug für X ausgefüllt. Wenn das Eingabespiel jedoch bereits gewonnen wurde, wird der Farbtausch immer noch ausgeführt und es wird kein Zug ausgefüllt.
Alle Versionen werden natürlich schneller und verbrauchen weniger Speicher, wenn die Karte voll ist. Die schnelleren Versionen sollten in weniger als 10 Sekunden einen Zug erzeugen, sobald 2 oder 3 Züge gemacht wurden.
Grundsätzlich sollte diese 146-Byte-Version auch funktionieren:
#!/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)
aber auf meinem Computer löst es einen Perl-Fehler aus und entleert den Kern.
Alle Versionen funktionieren im Prinzip immer noch, wenn das 6-Byte-Positions-Caching von $$_||=
entfernt wird, dies jedoch so viel Zeit und Speicher benötigt, dass es nur für fast gefüllte Boards funktioniert. Aber theoretisch habe ich zumindest eine 140-Byte-Lösung.
Wenn Sie $\=
kurz vor dem (Kosten: 3 Bytes) setzen $@<=>0
, folgt auf jede Ausgabekarte der Status der gesamten Karte: 1
für X
Gewinne, 0
für Unentschieden und -1
für Verluste.
Hier ist ein interaktiver Treiber, der auf der oben genannten schnellsten Version basiert. Der Treiber hat keine Logik, wann das Spiel beendet ist, so dass Sie sich selbst stoppen müssen. Der Golfcode weiß es jedoch. Wenn der vorgeschlagene Zug ohne -
Ersetzung durch irgendetwas zurückkehrt, ist das Spiel beendet.
#!/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;
}