Giochi ottimali di Tic Tac Torus


31

Questa sfida riguarda il gioco Tic Tac Toe, ma si gioca su un toro.

Come giocare

Per creare il tabellone di gioco necessario, inizi con un tabellone di gioco Tic Tac Toe normale. Per prima cosa piegalo in un cilindro unendo il bordo sinistro e quello destro. Quindi piegalo in toro unendo il bordo superiore e quello inferiore. Ecco una semplice visualizzazione di un tale tabellone con alcune mosse giocate (abilità Sick Paint!).

Tic Tac Torus

Le regole di Tic Tac Toe su un toro sono le stesse del normale Tic Tac Toe. Ogni giocatore piazza alternando X e O. Vince il primo con 3 simboli uguali di fila, una colonna o un diagonale.

Poiché un toro è piuttosto difficile da visualizzare, semplicemente proiettiamo la scheda su un foglio. Ora possiamo giocare come un normale Tic Tac Toe. L'unica differenza è che puoi anche vincere con 3 simboli uguali in una diagonale rotta. Ad esempio, il giocatore 1 (X) vince il seguente tabellone. Puoi vederlo facilmente cambiando un po 'la vista sul toro.

Il giocatore 1 (X) vince a causa di 3 X in una diagonale rotta

Se sei interessato, puoi giocare a Tic Tac Toe su un Torus ai Torus Games . C'è una versione per Windows, Mac e Android.

Giochi ottimali

In questa sfida erano interessati a giochi ottimali. Un gioco ottimale è un gioco in cui entrambi i giocatori giocano una strategia ottimale. Su una tavola Tic Tac Toe regolare i giochi ottimali finiscono sempre con un pareggio. Affascinante su una tavola del toro vince sempre il primo giocatore. In effetti un gioco su una tavola del toro non può mai finire in parità (anche se i giocatori non giocano in modo ottimale).

La strategia ottimale è davvero semplice:

  • Se puoi vincere posizionando il tuo simbolo, fallo.
  • Altrimenti, se il tuo avversario ha due simboli in una riga / colonna / gonal diagonale, prova a bloccarlo. Altrimenti, fai quello che vuoi.
  • Altrimenti fai quello che vuoi.

Ogni gioco ottimale consiste esattamente di 7 mosse e queste mosse possono essere descritte nel modo seguente:

  1. Il giocatore 1 piazza una X in qualsiasi punto del tabellone (9 scelte)
  2. Il giocatore 2 piazza una O in qualsiasi punto del tabellone (8 scelte)
  3. Il giocatore 1 piazza una X in qualsiasi punto del tabellone (7 scelte)
  4. La mossa del giocatore 2 può essere forzata (1 scelta), altrimenti posiziona la O ovunque (6 scelte)
  5. La mossa del giocatore 1 è forzata (1 scelta)
  6. Il giocatore 2 viene catturato in un fork (il giocatore 1 può vincere in due modi diversi), quindi il giocatore 2 deve bloccare il giocatore 1 in un modo (2 scelte)
  7. Il giocatore 1 piazza la sua ultima mossa e vince (1 scelta)

Ci sono 9 * 8 * 1 * 6 * 1 * 2 * 1 + 9 * 8 * 6 * 1 * 1 * 2 * 1 = 1728 diversi giochi ottimali sulla nostra tavola proiettata. Qui puoi vedere un tipico gioco ottimale:

Esempio di un gioco ottimale

Se etichettiamo ogni cella del tabellone con le cifre 0-8, possiamo descrivere questo gioco in base alle cifre 3518207. Innanzitutto una X è posizionata nella cella 3 (riga centrale, colonna sinistra), rispetto a una O nella cella 5 (riga centrale, colonna destra), rispetto a una X nella cella 1 (riga superiore, colonna centrale), ...

Usando questa notazione con cifre abbiamo generato automaticamente un ordine. Ora possiamo ordinare tutti i 1728 giochi ottimali e otteniamo l'elenco:

Game 0000: 0123845
Game 0001: 0123854
Game 0002: 0124735
Game 0003: 0124753
Game 0004: 0125634
   ...
Game 0674: 3518207
   ...
Game 1000: 5167423
Game 1001: 5167432
Game 1002: 5168304
   ...
Game 1726: 8765034
Game 1727: 8765043

Sfida

Questo elenco fa parte del tuo lavoro. Riceverai un numero kcompreso tra 0 e 1727 e dovrai restituire il kgioco in notazione numerica di quell'elenco ordinato.

Scrivi una funzione o un programma, che riceve il numero k(intero) calcola il gioco corrispondente. È possibile leggere l'input tramite STDIN, argomento della riga di comando, argomento prompt o funzione e stampare il risultato (7 cifre) in un formato leggibile (ad esempio 0123845o [0, 1, 2, 3, 8, 4, 5]) oppure restituirlo utilizzando una stringa (formato leggibile dall'uomo) o un numero intero (contenente tutti cifre in base 10) o in qualsiasi formato di array / elenco.

Il tipo di sfida è il code-golf. Pertanto vince il codice più breve.


Perché la prima mossa del giocatore 2 deve trovarsi nella stessa riga o colonna della prima mossa del giocatore 1? Ho giocato alcune partite nella mia testa in cui la prima mossa del giocatore 2 è invece nella stessa diagonale e seguono lo stesso schema di gioco ottimale. Mi sembra che uno degli aspetti della tavola toroidale sia che le file, le colonne e le diagonali hanno effetti simmetrici sul gioco.
Runer112,

@ Runer112 Semplificata molto la strategia ottimale. L'unica regola ora è bloccare il tuo avversario se puoi, altrimenti fai quello che vuoi.
Jakube,

7
Solo un commento secondario: in realtà ci sono molti meno giochi unici possibili qui. La simmetria traslazionale rende irrilevante la posizione della prima mossa (perché puoi sempre centrare la tua vista), quindi dividi per 9 ... e la rotazione della scacchiera fa solo 2 secondi diversi mosse (quadrato diagonale o adiacente) ... quindi a la maggior parte dei 48 giochi distinti. Se si prende in considerazione la simmetria della riflessione, si riduce ulteriormente. Questa versione Torus è molto più noiosa di quella normale. Golf lontano.
Orione,

@orion In realtà il fatto che il toro si avvolga, non ci proibisce di pensare a '0' come il 'primo' retto sulla scheda del toro e di distinguere tutti e nove i campi in generale ... Eppure siamo d'accordo sul fatto che "meridiano 0" sia a Greenwich , mentre sull'opposto della Terra possiamo essere una gamba sul posto dove è giovedì, una gamba sul posto è mercoledì (differenza di 24 ore nell'ora locale!) - e tutto a dispetto del fatto che la Terra è rotonda e non ha un "punto di partenza" ...
pawel.boczarski,

@Rodney No, è uno, non un sette. Prova a calcolarlo.
Jakube,

Risposte:


6

JavaScript (ES6), 266 308 317 334 341

Una funzione che restituisce una stringa. Modifica Trovato una soluzione aritmetica per la funzione M (finalmente!)

F=(n,x=[],M=(a,b,t=-a-b)=>(a-b)%3?a<3&b<3?3+t:a>5&b>5?21+t:12+t:9+t+a%3*3)=>
[for(a of z='012345678')for(b of z)for(c of z)for(d of z) 
a-b&&c-a&&c-b&&(b-(y=M(a,c))?d==y:d-a&&d-b&&d-c)&&(
f=M(c,e=M(b,d)),g=M(a,e),g<f?[f,g]=[g,f]:0,r=a+b+c+d+e,x.push(r+f+g,r+g+f))]
&&x[n]

Molto ingenuo , può essere abbreviato in molti modi . Enumera semplicemente tutti i possibili valori legali e restituisce ciò che si trova al posto n. La funzione M restituisce la posizione tra due celle, ovvero la mossa obbligatoria per bloccare un giocatore opposto.

Più leggibile

F=(n,x=[],
  M=(a,b,t=-a-b)=>(a-b)%3? 
     a<3&b<3?
       3+t // first row
       :a>5&b>5?
          21+t // last row
          :12+t // middle row and diags
     :9+t+a%3*3 // columns
  )=>
  [for(a of z='012345678') // enumerate the first 4 moves
     for(b of z)
       for(c of z)
         for(d of z) 
           a-b&&c-a&&c-b // avoid duplicates
           &&(b-(y=M(a,c))?d==y:d-a&&d-b&&d-c) // and check if d must block a-c or it's free
           &&(
             e=M(b,d), // forced to block b-d
             f=M(c,e),g=M(a,e),g<f?[f,g]=[g,f]:0, // f and g could be in the wrong order
             r=a+b+c+d+e, // start building a return string
             x.push(r+f+g,r+g+f) // store all values in x
  )]&&x[n] // return value at requested position

3

Ottava, 467369363309297 caratteri

297:

global t=0*(1:9)m=dec2bin([15;113;897;1170;1316;1608;2370;2216;2580])-48 a;
function g(s,p)global a t m;
if nnz(s)<8&&any((t==1)*m>2)a=[a;s];return;end;q=t==3-p;
(r=sort(~q.*(1:9)*m(:,find([3 1]*([q;t==p]*m)==6)')))||(r=find(~t));
for i=r t(i)=p;g([s i+47],3-p);t(i)=0;end;end;g('',1);f=@(n)a(n+1,:);

L'unica modifica rilevante è che non controlliamo mai se il giocatore attuale può vincere, ma controlliamo solo la possibilità dell'avversario di vincere il turno successivo . Poiché l'unico turno che il giocatore 1 può vincere è il turno 7 , questo è l'unico posto in cui l'algoritmo produrrebbe un gioco non ottimale, ma è molto facile filtrare una situazione del genere. Verifichiamo semplicemente ogni partita generata se viene vinta dal giocatore 1 - in caso contrario, la mossa a sua volta 7 non è corretta, quindi non aggiungiamo questa partita al tavolo da gioco ottimale.

(Esattamente metà dei giochi generati da questa regola sono falsi, cioè nel 7 ° turno il giocatore 1 ha sempre due possibilità di bloccare il giocatore due, ma solo uno lo farà vincere all'istante).

Uso:

$ octave
octave:1>> source script.m
octave:2>> f(634)
ans = 3270148

Il codice ungolf appare come:

 global t=0*(1:9);
 global m=dec2bin([15;113;897;1170;1316;1608;2370;2216;2580])-48;
 global allgames;
 allgames=[];

 function r=canbewon(by)
  global t m
  q=[t==by;t==(3-by)]*m;
  g=(find([3 1]*q==6))';
  r=sort((~(t==by).*(1:9)) * m(:,g));
 end

 function games_starting_with(s)
 global allgames t;
 if 7==numel(s) && any((t==1)*m==3) # it's 7th move and first player won
  allgames=[allgames;s];
  return;
 end;
 poss=(find(~t));                  # for now all free slots are possible
 player=1+mod(numel(s),2);
 moves = canbewon(3-player);
 if numel(moves) poss=moves; end;  # ... no, we must block the other player
 for i=poss
  t(i)=player;
  games_starting_with([s char(i+47)]);
  t(i)=0;
 end
end

games_starting_with('');
f=@(n)(allgames(n+1,:));

1
piccolo suggerimento: puoi tagliare le vecchie versioni se usano la stessa logica poiché sono archiviate nella cronologia delle modifiche in modo che siano ancora disponibili
masterX244
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.