Visualizza una scheda Nim come un esperto


10

sfondo

Nel gioco di Nim , i giocatori si alternano rimuovendo "pietre" da "pile": ad ogni turno, un giocatore deve rimuovere tra una e tutte le pietre da una singola pila. Lo scopo di Nim è prendere l'ultima pietra o, nella variante sbagliata, forzare il tuo avversario a farlo - tuttavia, risulta che le strategie sono quasi identiche.

Nim è un divertente gioco da bar. Puoi usare fiammiferi o monete per le "pietre" e le "pile" sono generalmente disposte in una linea. Di seguito è una configurazione classica con pile di 1, 3, 5 e 7:

nim con fiammiferi

Se non hai mai giocato a Nim prima, potresti provare a giocarci prima di provare questa sfida. Ecco una versione chiamata "Pearls Before Swine" .

Strategia

La strategia ottimale in Nim è abbastanza complicata da far perdere la maggior parte dei laici a un esperto, ma semplice da descrivere con l' aritmetica binaria .

Fare operazioni XOR binarie mentali, tuttavia, è difficile, quindi per fortuna c'è un modo equivalente di visualizzare la strategia corretta che è più facile da implementare in tempo reale, anche quando è ubriaco.

Ci sono solo tre passaggi:

  1. Raggruppa mentalmente le "pietre" in ogni riga in sottogruppi le cui dimensioni sono potenze di 2, a partire dalla dimensione più grande possibile: 8, 4, 2 e 1 sono sufficienti per la maggior parte dei giochi.
  2. Cerca di abbinare ogni gruppo con un gemello in un'altra linea, in modo che ogni gruppo abbia una coppia.
  3. Se ciò non è possibile, rimuovi le "pietre" non abbinate da un'unica riga (ciò sarà sempre possibile - vedi il link di Wikipedia per il motivo) in modo che il passaggio 2. diventi possibile.

Oppure, ha detto in un altro modo: "Rimuovi alcune pietre da una singola pila in modo tale che se poi raggruppi le pile in poteri di 2, tutti i gruppi possono essere accoppiati con un gruppo in un'altra pila." Con l'avvertenza che non puoi dividere i poteri più grandi di 2 in quelli più piccoli - ad esempio, non puoi raggruppare una linea con 8 pietre in due gruppi di 4.

Ad esempio, ecco come visualizzeresti la scheda sopra:

fiammiferi bilanciati

Questa tavola è perfettamente bilanciata, quindi vorresti che il tuo avversario si muovesse per primo.

La sfida

Dato un elenco di numeri interi positivi che rappresentano le dimensioni di "pile" di Nim, restituisce una visualizzazione in testo semplice della scheda Nim vista da un esperto.

Ciò che costituisce una visualizzazione valida è spiegato meglio con l'esempio, ma è necessario:

  1. Assegna un carattere distinto a ciascun "sottogruppo di potenza di 2" e alla sua coppia (i sottogruppi non accoppiati non si qualificano) e usa quel carattere per rappresentare le "pietre" sia nel sottogruppo che nella coppia.
  2. Rappresentare qualsiasi "pietre" spaiati (vale a dire, quelli un esperto avrebbe rimosso durante la riproduzione normale - non Misere - Nim) utilizzando un trattino: -.

Esistono diversi modi per ottenere una visualizzazione valida e tutti sono validi. Analizziamo alcuni casi di test:

Casi test

Ingresso: 1, 3, 5, 7

Possibile uscita 1:

A
BBA
CCCCD
CCCCBBD

Opzionalmente puoi includere spazi tra i caratteri, nonché linee vuote tra le righe:

Possibile uscita 2:

A

B B A

C C C C D

C C C C B B D

Ingresso: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

L'ordine e la scelta dei personaggi può essere quello che ti piace:

Possibile uscita 1:

G
E E
E E G
C C C C
C C C C F
B B B B D D
B B B B D D F
H H I - - - - -
A A A A A A A A I
A A A A A A A A H H

Anche i simboli Unicode sono ok:

Possibile uscita 2:

◎
◈  ◈
◈  ◈  ◎
△  △  △  △
△  △  △  △  ◉
◐  ◐  ◐  ◐  ◀  ◀
◐  ◐  ◐  ◐  ◀  ◀  ◉
▽  ▽  ◒  -  -  -  -  -
▥  ▥  ▥  ▥  ▥  ▥  ▥  ▥  ◒ 
▥  ▥  ▥  ▥  ▥  ▥  ▥  ▥  ▽  ▽  

Ingresso: 7

Dalle regole segue che ogni "pila singola" deve essere completamente rimossa.

Possibile uscita 1:

-------

Possibile uscita 2:

- - - - - - -

Ingresso: 5, 5

Uscita possibile:

A A A A B
A A A A B

Regole aggiuntive

  • Questo è il golf del codice con regole standard. Il codice più corto vince.
  • L'input è flessibile e può essere preso in qualunque forma di elenco sia conveniente per te.
  • Anche l'output è flessibile, come illustrano gli esempi sopra. Saranno consentite le variazioni più ragionevoli. Chiedi se non sei sicuro di qualcosa.

1
Esiste un limite al numero di pietre che può contenere ciascuna pila o al numero di caratteri distinti necessari per la visualizzazione? (In casi estremi, cosa accadrebbe se, ad esempio, fosse necessario più del numero di caratteri ASCII stampabili o più di 255 caratteri distinti?)
Maniglia della porta

@Doorknob Puoi presumere che non accadrà. Si potrebbe anche supporre che le lettere dell'alfabeto siano sufficienti per qualsiasi input.
Giona

@Jonah sarebbe un output valido per il secondo caso di test? ["H","EE","EEH","CCCC","CCCCI","DDDDFF","DDDDFFI","AAAAAAAA","AAAAAAAA-","----------"]
ngn,

1
@ Οurous Penso che la risposta semplice si. Tecnicamente in AAAABBBBrealtà non è valido, e ABBnon lo è - ma rende l'output meno leggibile, quindi penso che solo la riduzione esplicita all'interno di una riga sia la migliore.
Giona

1
@JonathanAllan Sì, mi affido alla logica che tutti e 3 i passaggi devono avvenire contemporaneamente. Quindi, se esegui i passaggi 1 e 2 ma non riesci a eseguire il passaggio 3, devi adattare la tua soluzione ai passaggi 1 e 2. Che posso vedere essere confuso. Ho aggiunto la tua descrizione qui sotto.
Giona,

Risposte:


2

Rubino, 169 164 148 byte

->a{s=eval a*?^
c=?@
m={}
a.map{|x|z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0
n=1
eval'x&1>0?[$><<(m[n]||c.next!)*n,m[n]=!m[n]&&c*1]:0;n*=2;x/=2;'*x
puts}}

Provalo online!

Innanzitutto, inizializziamo

  • il nim-sum con s=eval a*?^(che è più breve di a.reduce:^)
  • la variabile c, che memorizza il primo carattere univoco non utilizzato
  • una mappa mche associa la potenza di due lunghezze ai caratteri usati per rappresentarli

Quindi, eseguendo il ciclo su ogni pila, eseguiamo quanto segue:

z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0

Secondo la strategia di Wikipedia , se la pila XOR nim-sum è inferiore alla pila , dovremmo rimuovere le pietre da quella pila in modo tale che la sua lunghezza diventi pila XOR nim-sum . Memorizzando la differenza nella variabile z, possiamo verificare se questa differenza è positiva e, in tal caso, 1.) stampare molti trattini, 2.) sottrarla dalla pila e 3.) impostare la variabile nim-sum su zero per impedire l'ulteriore rimozione della pietra.

n=1
eval'[...];n*=2;x/=2;'*x

Ora noi "loop" sopra ogni bit e tenere traccia dei loro valori ripetutamente dividendo xda 2e moltiplicando l'accumulatore nda 2. Il ciclo è in realtà una stringa valutata xvolte, che è molto maggiore delle log2(x)volte necessarie, ma non viene fatto alcun danno (a parte l'inefficienza). Per ogni bit, eseguiamo quanto segue se il bit è 1 ( x&1>0):

$><<(m[n]||c.next!)*n

Stampa un personaggio nvolte. Se abbiamo già stampato un gruppo spaiato di tante pietre, usa quel personaggio; in caso contrario, usa il personaggio non utilizzato successivo (avanzando csul posto a causa del !).

m[n]=!m[n]&&c*1

Se m[n]esiste (ovvero abbiamo appena completato una coppia), m[n]viene ripristinato. Altrimenti, abbiamo appena iniziato una nuova coppia, quindi imposta m[n]il personaggio che abbiamo usato ( *1è un modo breve per fare una copia di c).


4

Python 2 , 150 196 206 byte

def f(p):
 c=48;s=[l*'.'for l in p];m=2**len(bin(l))
 while m:
  if sum(m*'.'in l for l in s)>1:
   for i,l in enumerate(s):s[i]=l.replace('.'*m,chr(c)*m,`s`.count(chr(c)*m)<2)
   c+=1
  else:m/=2
 return s

Provalo online!


Non penso che funzioni per 4, 9, 10.
Neil,

@Neil Nice catch, ora dovrebbe essere risolto
TFeld

1
Mi dispiace, sono riuscito a ingannarlo di nuovo, questa volta con 14, 21, 35.
Neil,

Non riesce anche per [1, 3, 4, 5], dove dovrebbe rimuovere l'intero secondo palo.
Maniglia della porta

@Doorknob, è necessario? Le regole diconoThere will be multiple ways to achieve a valid visualization, and all are valid
TFeld,

1

JavaScript (ES6), 215 byte

f=
(a,c=0,x=eval(a.join`^`),i=a.findIndex(e=>(e^x)<e),b=a.map(_=>``),g=e=>(d=e&-e)&&a.map((e,i)=>e&d&&(a[i]-=d,b[i]=(c++>>1).toString(36).repeat(d)+b[i]))&&g(e-d))=>g(eval(a.join`|`),b[i]='-'.repeat(a[i]-(a[i]^=x)))||b
<textarea oninput=o.textContent=/\d/.test(this.value)?f(this.value.match(/\d+/g)).join`\n`:``></textarea><pre id=o>

Visualizza solo fino a 36 personaggi diversi. Sono sollevato che questo funzioni per 1, 3, 4, 5.


Veramente bello. È divertente giocarci in tempo reale.
Giona

1

Pulito , 454 byte

ancora golf

import StdEnv,Text,Data.List
$p=join"\n"[{#toChar c+'-'\\c<-e}\\e<-[take i(e++[0,0..])\\e<-r[[~c\\c<-reverse e,_<-[1..c]]\\e<-hd[q\\q<-foldr(\h t=[[a:b]\\a<-h,b<-t])[[]][[c\\c<-subsequences(takeWhile((>=)k)(iterate((*)2)1))|sum c<=k]\\k<-p]|sum[1\\a<-q&b<-p|sum a<>b]<2&&foldr(bitxor)0(flatten q)==0]]1&i<-p]]
r[]_=[]
r[h:t]n|all((<)0)h=[h:r t n]
#[m:_]=removeDup[e\\e<-h|e<0]
#(a,[x:b])=span(all((<>)m))t
=r([[if(e==m)n e\\e<-k]\\k<-[h:a]++[x]]++b)(n+1)

Provalo online!

Definisce la funzione $ :: [Int] -> String, prendendo le dimensioni della pila e restituendo una stringa in cui -indicare le pietre da rimuovere e i gruppi sono rappresentati da caratteri ASCII da cui ascendono -. Se sono necessari abbastanza gruppi, i personaggi torneranno indietro alla fine, e a causa difoldr ciò, richiede più di un gigabyte di memoria per eseguire il secondo test-case.

Versione indentata della comprensione gigante:

$p=join"\n"[
    {#
        toChar c+'-'
        \\c<-j
    }
    \\j<-[
        take i(e++[0,0..])
        \\e<-r[
            [
                ~c
                \\c<-reverse e
                ,_<-[1..c]
            ]
            \\e<-hd[
                q
                \\q<-foldr(\h t=[
                    [a:b]
                    \\a<-h
                    ,b<-t
                ])[[]][
                    [
                        c
                        \\c<-subsequences(takeWhile((>=)k)(iterate((*)2)1))
                        |sum c<=k
                    ]
                    \\k<-p
                ]
                |sum[
                    1
                    \\a<-q
                    &b<-p
                    |sum a<>b
                ]<2&&foldr(bitxor)0(flatten q)==0
            ]
        ]1
        &i<-p
    ]
]

Solo curioso, Clean sembra simile a Hashell ... quali sono i suoi vantaggi rispetto a Haskell?
Giona,

1
@Jonah È abbastanza simile, sì. Dalla parte superiore della mia testa, ha una manipolazione di tipo di livello inferiore, IL / Assembly in linea e interoperabilità con C tutti realizzati più facilmente rispetto a Haskell. Tuttavia, per un uso effettivo, a causa dell'oscurità e della natura sperimentale / accademica di Clean, consiglierei Haskell (che ha anche un maggiore supporto in termini di biblioteche e informazioni di riferimento). Mi piace solo che Clean sia tutto.
Οuroso
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.