Qual è la tua prossima mossa?


18

Questa sfida è quella di scrivere una funzione minimax in una lingua a tua scelta, per produrre la mossa migliore successiva in un gioco NxN di tic-tac-toe dato l' attuale stato del board . L'input della scheda può essere accettato come Matrix, collezione 2D o qualsiasi altra cosa che abbia senso per te, ma aderisce alle regole . L'output è la mossa migliore per chi è il turno in cui è attualmente , dove X è considerato iniziato .

Sfondo rapido sull'algoritmo Minimax

L'idea di base dell'algoritmo minimax è quella di enumerare tutti i possibili risultati come DAG, quindi ponderarli in base al vantaggio che la sequenza di mosse ha sul giocatore, regolato dalla prima mossa effettuata. Tutti i possibili risultati vengono quindi "messi a segno" dalla prima mossa e vengono assegnati in base alla somma di tutti i risultati (-1 per una perdita, 0 per un pareggio e 1 per una vittoria). Nelle implementazioni che richiedono a più giocatori di giocare, si elencano tutte le possibili mosse da parte del giocatore e tutte le possibili risposte anche degli avversari. Ad esempio, in una partita di tic-tac-toe (dopo la prima mossa) ci sono 8 possibili prime mosse che puoi fare, e possono sembrare tutte uguali quando si analizza solo il turno successivo. Ma ripetendo tutti i possibili risultati per ogni possibile serie di mosse che si traducono in un risultato finale e riassumendoli tutti,

Per un riepilogo migliore, più approfondito e contestuale dell'algoritmo mini-max in termini di tic-tac-toe, leggi di più qui: http://neverstopbuilding.com/minimax

XKCD (solo soluzione 3x3)

Tutte le mosse possibili per un gioco 3x3 di tic-tac-toe.

Le regole

  • È possibile utilizzare qualsiasi linguaggio, ma non sono consentite librerie esterne minimax.
  • L'output può essere una coordinata (0-n, 0-n) o un numero (1-n * n) indicativo della migliore mossa successiva.
    • Inoltre, devi essere in grado di identificare quando lo scenario migliore è una perdita o un pareggio anziché una vittoria.
    • Il modo in cui indichi una perdita o un pareggio dipende ancora una volta da te.
  • L'input deve usare le X e le O tradizionali, e devi assumere prima le mosse X; gli spazi vuoti possono essere rappresentati da qualsiasi cosa.
  • Puoi presumere che tutti gli input che entrano nel tuo programma abbiano n O e n + 1 X, in altre parole puoi supporre che stai ottenendo una scheda ben formata.
  • Lo stato corrente della scheda deve essere l'unico input per il programma, se si utilizza la ricorsione, è necessario creare metodi di supporto per facilitare i requisiti di input. Vedi /codegolf//a/92851/59376 per chiarimenti.
  • È necessario supportare qualsiasi valore di 10> = n> = 1; se il tuo programma "scade" per n> 10, lo trovo anche accettabile, poiché alcune lingue hanno una potenza di elaborazione significativamente inferiore (specialmente utilizzando le console rivolte al web).

A giudicare

  • Questo è code-golf, quindi vince il conteggio di byte più basso del programma e le scappatoie standard sono universalmente vietate.
  • In caso di pareggio, vincerà il programma che supporta la più grande "n".

Ingressi di esempio

2x2

[[X,O]
 [-,-]]

Output: 2 o [0,1] (3 o [1,1] sarebbero anche probabilmente corretti) (Qualche forma di indicazione della posizione, arbitraria a condizione che tu possa facilmente spiegare il formato che hai usato)


3x3

[[X,O,X]
 [O,X,-]
 [-,-,-]]

Uscita: -1 (Perdita)


Ancora una volta è consentito qualsiasi formato di input desiderato, ma è necessario utilizzare X e O, gli esempi forniti non sono stati concepiti per vincolare a quel formato, ma solo per ispirare.


Mi dispiace DJMCMhemhem, in realtà ho provato a taggare quelle cose ma non ci sono riuscito, dato che sono nuovo qui.
Magic Octopus Urn

Anche il bonus è stato rimosso, aggiunto solo tedio.
Magic Octopus Urn

È consentito il seguente formato di output: un diagramma della posizione della scacchiera con su ogni spazio originariamente vuoto un carattere unico che indica se giocare lì porta a una vittoria / perdita / pareggio (ad es. W, L e D)
Ton Hospel

1
Nell'esempio 3x3, O dovrebbe perdere qualunque cosa suoni, ma dici che l'output dovrebbe essere [2,1], perché?
Dada,

A cura, buona cattura. Non so cosa stavo pensando, quello era l'esempio negativo.
Magic Octopus Urn,

Risposte:


8

Perl, 101 98 byte

Include +4per-0p

Esegui con l'input su STDIN

tictactoe.pl
OXO
---
--X
^D

L'output è lo stesso diagramma, ma con ogni mossa aggiornata con il suo stato, 1rappresenta una vittoria, 2rappresenta un pareggio e 3rappresenta una perdita. Per questo caso sarebbe

OXO
223
21X

quindi 3 mosse pareggiano, 1 vince e 1 perde (aggiornerò la soluzione se questo formato di output è inaccettabile, ma il codice di base rimarrà lo stesso)

tictactoe.pl:

#!/usr/bin/perl -0p
m%@{[map"O.{$_}"x"@-"."O|",1-/.(
)(.)/,@-]}Z%sx||s%-%$_="$`X$'";y/XO/OX/;do$0%eg?/1/?3:1+/2/:2

Questo è già dolorosamente lento e utilizza molta memoria per la scheda 3 * 3 vuota (perché in realtà la ricorsione non va così in profondità. Deve esserci una perdita di memoria). L'aggiunta di memoizing costa 6 byte ma è molto più sana:

#!/usr/bin/perl -0p
$$_||=m%@{[map"O.{$_}"x"@-"."O|",1-/.(\n)(.)/,@-]}Z%sx||s%-%$_="$`X$'";y/XO/OX/;do$0%eg?/1/?3:1+/2/:2

Wow, trascurando che è pl e probabilmente non funzionerebbe assolutamente per n = 10 con un sacco di vuoti ... Hai fatto entrambe le cose che speravo di vedere qualcuno fare. Una stringa di input e mappatura del risultato per tutte le mosse, non solo il migliore. Bravo.
Magic Octopus Urn,

Se una funzione ricorsiva 'perde' come può essere ok ??? Un linguaggio troppo alto non fa vedere il registro a 32 bit nella CPU (o qualcosa del genere come la semplice istruzione)
RosLuP

@RosLup La perdita in questo contesto non significa necessariamente perdita di memoria irraggiungibile. Perl è piuttosto peculiare quando rilascia memoria, abbastanza spesso facendo più tardi di quanto ti aspetti e quindi usando molta più memoria di quanto ti aspetteresti. Inoltre, tende ad allocare più del necessario direttamente nell'aspettativa di crescita delle strutture di dati. In questo caso l'uso della ricorsione "normale" con una funzione anziché l'abuso di do$0userebbe una memoria 10 volte inferiore. Intendiamoci, questo caso è così estremo che potrebbe effettivamente essere una vera perdita di memoria.
Ton Hospel,

Non solo uno non vede i registri o le istruzioni di base (dalle istruzioni di hlls) ma perde il controllo dell'uso della memoria ... Per me non si ridimensionano ...
RosLuP

È passato abbastanza tempo, hai vinto il mio uomo, purtroppo non abbiamo avuto altri tentativi.
Magic Octopus Urn,

2

Javascript (ES6), 320 294 byte

(b,p,d,M,S=-2)=>(T=(p,q,r,s)=>b[p][q]==(n=b[r][s|0])&&n!='-',w=0,b.map((r,y)=>(l=r.length-1,m=15,r.map((c,x)=>(m&=8*T(l-x,x,l)+4*T(x,x,0)+2*T(x,y,0,y)+T(y,x,y))),w|=m)),w?-1:(b.map((r,y)=>r.map((c,x)=>S<1&&c=='-'&&(r[x]='O.X'[p+1],(s=-f(b,-p,1))>S&&(S=s,M=[x,y]),r[x]=c))),S=S+2?S:0,d?S:[M,S]))

Ingresso

1) Un array di array di personaggi che descrivono la scheda corrente, come ad esempio:

[['X', '-'], ['-', 'O']]

2) Un numero intero che descrive la svolta corrente: 1 = X, -1 =O

Produzione

Una matrice composta da:

  • un array che descrive la migliore mossa in [x, y]formato
  • il risultato del gioco come numero intero: 1 = vittoria, -1 = perdita, 0 = pareggio

Esempio

Nel seguente esempio, Xè garantito vincere giocando [1, 2].

let f =
(b,p,d,M,S=-2)=>(T=(p,q,r,s)=>b[p][q]==(n=b[r][s|0])&&n!='-',w=0,b.map((r,y)=>(l=r.length-1,m=15,r.map((c,x)=>(m&=8*T(l-x,x,l)+4*T(x,x,0)+2*T(x,y,0,y)+T(y,x,y))),w|=m)),w?-1:(b.map((r,y)=>r.map((c,x)=>S<1&&c=='-'&&(r[x]='O.X'[p+1],(s=-f(b,-p,1))>S&&(S=s,M=[x,y]),r[x]=c))),S=S+2?S:0,d?S:[M,S]))

console.log(JSON.stringify(f(
  [['O','X','O'],
   ['-','-','-'],
   ['-','-','X']],
  1
)));

UN GIOCO STRANO. L'UNICO MOVIMENTO VINCENTE NON È GIOCO.
COME SI INTENDE UN GIOCO DI SCACCHI PIACEVOLE?


Ben fatto, buona prima entrata. Solo le osservazioni che ho sono il potenziale per salvare byte con le informazioni fornite "X si sposterà sempre per prima". E hai provato con una scheda non 3x3;)?
Magic Octopus Urn

@carusocomputing - Non sei sicuro di capire cosa hai in mente con "La X si sposterà sempre per prima". Potrebbe essere usato per dedurre da che parte è in movimento dato il solo board, ma il calcolo costerebbe effettivamente più byte; quindi immagino tu stia parlando di qualcos'altro. Sì, ho fatto alcuni test con schede leggermente più grandi. Dovrebbe funzionare come previsto finché ... err ... non ci sono troppe posizioni vuote. :-)
Arnauld

La sfida dice The current state of the board must be the only input to your program. Il tuo codice ha bisogno di due input, che infrange questa regola.
Dada,

1
@Dada - Me lo stavo chiedendo, ma supponevo che il colore attivo fosse parte dello stato della scacchiera (proprio come una posizione degli scacchi viene sempre con il colore attivo + en passant square + castling availability). Quindi immagino che il PO dovrebbe chiarire questo punto. (E se hai ragione, sembra un'ulteriore inutile difficoltà, IMHO.)
Arnauld

1
Mmm .. mi piace molto la spiegazione dello stato della scheda nella sua risposta. Pensandoci, alcune lanague possono usare solo stringhe come input, avendo una scheda come XXOOXO-OO sarebbe difficile decifrare in conteggi di byte bassi senza ulteriori informazioni come le dimensioni della scheda. Permetterò qualsiasi input aggiuntivo che contribuisca allo stato del board, anche se penso ancora che l'informazione "supponi che X si muova prima" sia diversa da "dato chi è il turno". Alcune lingue ne trarranno vantaggio come presupposto;).
Magic Octopus Urn,
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.