Diventa il campione


11

Tic-Tac-latino!

Questa è una storia vera, quindi i nomi sono stati modificati.

Il mio insegnante di latino, Mr. Latin, ha creato la sua variante proprietaria (non scherzosa) di tic tac toe. Chiamiamolo tic-tac-latino. Il gioco è semplice, è essenzialmente tic tac toe giocato su una griglia quattro per quattro.

Dichiarazione di regola formale

Una linea può essere una riga, una colonna o una diagonale. Esistono due simboli, "X" e "O", ma uno o entrambi possono essere sostituiti con un simbolo diverso.
Ottieni un punto quando hai tre dei tuoi simboli e uno dell'altro personaggio.

Queste disposizioni segnano:

--- O
-O--
XXXO
XOOX

O -XX
- O -
- X -
--- O

Questi non segnano:

----
XXXX
----
OOOO

----
xxx-
----
OOO-

Il gioco si vince ogni volta che un giocatore ha più punti di un altro. Il gioco è un pareggio solo se il tavolo viene riempito.

Sfida

Risolvi questo gioco. Il tuo compito è quello di fornire un modo per garantire una vittoria o un pareggio, qualunque sia il risultato ottimale.

La tua soluzione può scegliere di iniziare prima o seconda (e quindi può scegliere il suo simbolo). Non è obbligatorio implementare un gioco interattivo in cui gli input dell'utente si spostano e il display corrispondente cambia. Può anche essere una funzione o un programma che accetta input come stato di gioco e genera una nuova scheda o una descrizione della loro mossa . Entrambe le opzioni devono essere eseguite entro circa dieci secondi per mossa effettuata.


Giocare il tuo giocatore contro qualsiasi sequenza di mosse deve dare il risultato ottimale. Ciò significa che puoi presumere che la posizione di input sia quella raggiungibile dal gioco con il tuo giocatore. Le presentazioni devono essere deterministiche e non devono necessariamente fornire una prova di ottimalità, ma se vengono violate (se vengono battute), le comunicazioni verranno considerate non valide (si potrebbe lasciar perdere, ma aggiungere (crackare) nel titolo).
Questo è un compito non banale, quindi ogni invio valido è impressionante ed è degno di un segno di spunta accettato, ma farò del golf del codice il criterio vincente principale.

Il vincitore viene scelto scendendo questo elenco fino a quando viene scelto un vincitore.

  • Implementazione risolta più breve che vince sempre
  • Implementazione più breve

1
"In primo luogo viene esaminata la qualità del gioco" Non pensi che sia soggettivo?
user48538

Il compito di fornire un'interfaccia per giocare sembra periferico nello scrivere un giocatore perfetto. Suggerirei semplicemente di passare l'attuale stato di gioco come input e di richiedere al codice di produrre una mossa vincente, o anche semplicemente una valutazione in perfetto gioco (vincere, pescare, perdere).
xnor

1
Una soluzione può essere golfosa facendo una ricerca inefficiente della forza bruta. Stai bene se il codice viene eseguito molto lentamente?
xnor

1
" Vinci la partita se segnerai e nel processo non segnerai per il tuo avversario. " Questo significa che posso vincere solo quando piazzo un pezzo, non quando il mio avversario lo fa? Cosa succede se una mossa crea linee vincenti per entrambi i giocatori: partita disegnata o giocata?
Peter Taylor,

1
@RohanJhunjhunwala Dovresti chiarire l'input consentito dello stato del gioco, altrimenti è possibile che le persone possano trarre vantaggio dal formato di input attualmente non definito e scegliere un formato che aiuti molto la loro soluzione.
ASCII,

Risposte:


6

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 Xsostituito da Oe viceversa. I punti vuoti saranno riempiti con un numero che indica il risultato se X giocasse lì, con 1il risultato che il risultato sarà una vittoria, 2un pareggio e 3una 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 Xse 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 Xdevi scambiare Xe Ovedere le mosse per O. Qui ad esempio è abbastanza chiaro che Xvince giocando in alto a sinistra, ma che dire se Xgioca in terza posizione in alto? Basta copiare l'output, mettere Oal 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 Odovrebbe perdere, quindi come perde se gioca in alto a sinistra? Fallo di nuovo inserendo Oin 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 Orimane senza speranza. È facile vedere ora che ogni mossa consente Xdi vincere immediatamente. Proviamo almeno a cercare 3 O di seguito:

OXXO
XX--
X-X-
O-OO

Dando:

XOOX
OO13
O3O3
X3XX

Xgioca l'unica mossa vincente (nota che questo fa XXXOlungo 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 0per una vittoria (non confondere con O), 1per un pareggio e 2per 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 $@<=>0allora ogni scheda di output sarà seguita dallo stato di tutta la scheda: 1per le Xvittorie, 0per il pareggio e -1per 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;
}

Bella risposta. Potresti fornirmi un modo semplice per testarlo? Idealmente, mi piacerebbe vedere una versione interattiva ... (questo è solo per la mia curiosità).
Rohan Jhunjhunwala,

@RohanJhunjhunwala Ok, ha aggiunto un semplice driver interattivo
Ton Hospel,

La variabile "$ move" non è dichiarata a prog.pl:2
Rohan Jhunjhunwala

Esiste una soluzione euristica che un essere umano può attuare?
Rohan Jhunjhunwala,

@RohanJhunjhunwala Ho appena ricontrollato il programma del driver. Funziona bene, $moveè dichiarato sulla linea 11. Non ho idea se ci sia un euristico umano. Questo programma fa solo minimax sull'albero del gioco, non ha alcuna conoscenza strategica.
Ton Hospel,

2

JavaScript (ES6) 392 byte

a=>b=>(c="0ed3b56879a4c21f",r=[],k=f=>r.push([a.filter(f),b.filter(f)]),[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)),k(n=>n%5==0),k(n=>n&&n-15&&!(n%3)),g=r.find(o=>(o[0].length==1&&o[1].length==2)||(o[0].length==2&&o[1].length==1)),g?parseInt(c[30-[...g[0],...g[1]].map(i=>parseInt(c[i],16)).reduce((p,c)=>p+c)],16):[...a,...b].indexOf(15-a[0])+1?15-a.find(i=>b.indexOf(15-i)==-1):15-a[0])

uso

Il "bot" suonerà il secondo.

Disegna una griglia 4x4 numerata in questo modo:

+----+----+----+----+
|  0 |  1 |  2 |  3 |
+----+----+----+----+
|  4 |  5 |  6 |  7 |
+----+----+----+----+
|  8 |  9 | 10 | 11 |
+----+----+----+----+
| 12 | 13 | 14 | 15 |
+----+----+----+----+

Eseguiamo questo nella console del browser: basta mettere f=prima del codice

Quindi, se voglio iniziare 1, corro f([1])([])e mi darà 14.

Bella mossa ... Cosa succede se gioco 2dopo? f([2,1])([14]). Tornerà 13.

Fammi provare ad arrendermi. Gioca 3. f([3,2,1])([14,13]). Oh 0! Mi hai!

Giocare 0? f([0,2,1])([14,13]). 15Ok continuiamo a giocare ...

Nota

  1. Gioca in modo interattivo. Inizia con f([your-step])([]).

  2. Prepara il tuo prossimo passo. (Vedi la demo sopra)

  3. Aiuta il "bot" a inserire i suoi passi. Non ti darà buoni risultati se gli dai un'impostazione casuale. (Come f([1,2,4])([14,12])darà 14- Ehi, il robot voleva giocare 13alla sua seconda mossa!

Breve sintesi

Finché non ti arrendi, il robot eseguirà una mossa speculare.

Grazie @EHTproductions per avermi detto che ho letto male le regole del gioco e i suggerimenti sul golf: P

Ora rileverà anche se ha scacco matto. Se sì, bloccalo!

Le sue priorità: Block> Mirror> (fallback) cercano modi per riprodurre un mirror


Mi piace molto la tattica della "mossa speculare" :) Potrei essere frainteso, ma non è 3,2,1per te e 0per il bot una vittoria per te?
ETHproductions

Oops, ho frainteso come "chi cattura un modello di 3 di un tipo e 1 di un altro". Fammi modificare un po 'la soluzione .. Grazie @ETHproductions.
Sunny Pun

Un paio di consigli sul golf: si [0,1,2,3].map(i=>{k(n=>n%4==i);k(n=>Math.floor(n/4)==i);})può giocare a golf [0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)).
ETHproductions

Non credo che questo sia dimostrabilmente vincibile
Rohan Jhunjhunwala,

0 - 14 - 12 -13 lo
spezza
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.