Perl, 147 byte (non in competizione, richiede più di 10 secondi per mossa)
Include +4 per -0p
Il programma suona X
. Giocherà un gioco perfetto.
Inserire la scheda su STDIN, ad es .:
tictaclatin.pl
-X-O
-X--
X-X-
O--O
^D
L'output sarà lo stesso board con tutto X
sostituito da O
e viceversa. I punti vuoti saranno riempiti con un numero che indica il risultato se X giocasse lì, con 1
il risultato che il risultato sarà una vittoria, 2
un pareggio e 3
una perdita. Un gioco finito restituisce solo la stessa posizione con i colori invertiti.
In questo esempio l'output sarebbe:
1O1X
1O33
O3O3
X33X
Quindi la posizione è una vittoria X
se gioca nei 3 punti in alto e a sinistra. Tutte le altre mosse perdono.
Questo risultato confuso è in realtà conveniente se vuoi sapere come il gioco continua dopo una mossa. Dal momento che il programma suona sempre X
devi scambiare X
e O
vedere le mosse per O
. Qui ad esempio è abbastanza chiaro che X
vince giocando in alto a sinistra, ma che dire se X
gioca in terza posizione in alto? Basta copiare l'output, mettere O
al posto della mossa selezionata e sostituire di nuovo tutti gli altri numeri -
, quindi qui:
-OOX
-O--
O-O-
X--X
Con il risultato di:
3XXO
3X33
X3X3
O33O
Ovviamente ogni mossa O
dovrebbe perdere, quindi come perde se gioca in alto a sinistra? Fallo di nuovo inserendo O
in alto a sinistra e sostituendo le cifre con -
:
OXXO
-X--
X-X-
O--O
Dando:
XOOX
1O33
O3O3
X33X
Quindi X ha solo un modo per vincere:
XOOX
OO--
O-O-
X--X
Dando
OXXO
XX33
X3X3
O33O
La situazione per O
rimane senza speranza. È facile vedere ora che ogni mossa consente X
di vincere immediatamente. Proviamo almeno a cercare 3 O di seguito:
OXXO
XX--
X-X-
O-OO
Dando:
XOOX
OO13
O3O3
X3XX
X
gioca l'unica mossa vincente (nota che questo fa XXXO
lungo la terza colonna:
XOOX
OOO-
O-O-
X-XX
Qui l'output è:
OXXO
XXX-
X-X-
O-OO
perché il gioco era già finito. Puoi vedere la vittoria sulla terza colonna.
Il programma attuale 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)
Applicato alla scheda vuota, valuta 9506699 posizioni che richiedono 30 Gb e 41 minuti sul mio computer. Il risultato è:
2222
2222
2222
2222
Quindi ogni mossa iniziale pareggia. Quindi il gioco è un pareggio.
L'utilizzo estremo della memoria è principalmente causato dall'uso della ricorsione do$0
. L'utilizzo di questa versione da 154 byte con una funzione semplice richiede 3Gb e 11 minuti:
#!/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
che è più sopportabile (ma ancora troppo, qualcosa deve ancora perdere la memoria).
La combinazione di una serie di accelerazioni porta a questa versione da 160 byte (5028168 posizioni, 4 minuti e 800 M per la scheda vuota):
#!/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
Quest'ultimo utilizza 0
per una vittoria (non confondere con O
), 1
per un pareggio e 2
per una perdita. L'output di questo è anche più confuso. Riempie la mossa vincente per X in caso di vittoria senza scambio di colore, ma se il gioco di input è già stato vinto, lo scambio di colore viene comunque effettuato e non viene inserito alcun movimento.
Tutte le versioni ovviamente diventano più veloci e usano meno memoria mentre la scheda si riempie. Le versioni più veloci dovrebbero generare una mossa in meno di 10 secondi non appena sono state eseguite 2 o 3 mosse.
In linea di principio questa versione di 146 byte dovrebbe funzionare anche:
#!/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)
ma sulla mia macchina innesca un bug perl e scarica core.
In linea di principio, tutte le versioni continueranno a funzionare se la memorizzazione nella posizione a 6 byte eseguita da $$_||=
viene rimossa, ma che utilizza così tanto tempo e memoria che funziona solo per schede quasi piene. Ma in teoria almeno ho una soluzione da 140 byte.
Se metti $\=
(costo: 3 byte) poco prima di $@<=>0
allora ogni scheda di output sarà seguita dallo stato di tutta la scheda: 1
per le X
vittorie, 0
per il pareggio e -1
per la perdita.
Ecco un driver interattivo basato sulla versione più veloce di cui sopra. L'autista non ha logica per quando il gioco è finito, quindi devi fermarti. Il codice del golf lo sa però. Se la mossa suggerita ritorna senza essere -
sostituita da qualcosa, il gioco è finito.
#!/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;
}