Posso spazzare le mine?


29

Minesweeper è un popolare gioco di puzzle in cui devi scoprire quali tessere sono "miniere" senza fare clic su quelle tessere. Invece, fai clic sulle tessere vicine per rivelare il numero di mine adiacenti. Un aspetto negativo del gioco è che è possibile finire in uno scenario in cui ci sono più risposte valide e si può solo indovinare. Ad esempio, prendi la seguente scheda:

1110
2*31
3*??
2*4?
112?

In questo formato, un numero rappresenta il numero di miniere adiacenti, un *rappresenta una miniera nota e un "?" rappresenta una potenziale miniera. La cosa spiacevole di questo particolare puzzle è che ci sono quattro potenziali soluzioni distinte e valide :

1110    1110    1110    1110    
2*31    2*31    2*31    2*31
3*4*    3*5*    3**2    3**1
2*42    2*4*    2*4*    2*42
112*    1121    1121    112*

Ciò significa che la scheda è irrisolvibile . Ecco un esempio di una scheda risolvibile :

1121
1??*
12?*
0122

Questa scheda è risolvibile perché esiste solo una possibile soluzione valida:

1121
1*4*
12**
0122

Il tuo compito è quello di scrivere un programma o una funzione che accetta una scheda dragamine valida e determina se è risolvibile o meno. Con "scheda dragamine valida" intendo che l'input sarà sempre rettangolare, avrà almeno una soluzione e non conterrà caratteri non validi.

Il tuo input può essere un array di caratteri, un array di stringhe, una stringa contenente newline, ecc. L'output deve essere un valore di verità se è risolvibile e un valore di falsa se non lo è. Non sono estremamente preoccupato per le prestazioni, ma la tua soluzione deve teoricamente funzionare per input di qualsiasi dimensione.

Come al solito, si applicano scappatoie standard e vince la soluzione più breve in byte!

Esempi:

I seguenti esempi sono tutti risolvibili:

1121
1??*
12?*
0122

1110
1???
1110
0000

1110
3???
??20
*310

****
****
****
****

0000
0000
0000
0000

1100
*100
2321
??*2
13*2
1221
1*10
1110

1121
2*??
2*31
2220
1*10

I seguenti esempi sono tutti irrisolvibili:

1110
2*31
3*??
2*4?
112?

01??11*211
12??2323*1
1*33*2*210
12?2122321
13?3101**1
1***101221

1***
3*52
2*31
12??
02??
01??

00000111
000012*1
00001*21
22101110
**100111
?31123*1
?311**31
**113*20

Possiamo supporre che la scheda sia rettangolare con almeno una cella? Inoltre, possiamo presumere che l'input ammetterà sempre almeno una soluzione? (Ad esempio, 2?non ha soluzioni, il che significa che non può provenire da un vero gioco di Minesweeper. Quindi non è considerato una "scheda Minesweeper" ... sì?)
Mathmandan,

2
Non vale nulla che in MineSweeper hai informazioni aggiuntive che qui mancano: il numero di mine.
edc65,

@mathmandan Sì, l'input sarà sempre rettangolare con almeno una cella e almeno una soluzione valida.
DJMcMayhem

Risposte:


20

GNU Prolog, 493 byte

z(_,[_,_]).
z(F,[A,B,C|T]):-call(F,A,B,C),z(F,[B,C|T]).
i([],[],[],[]).
i([H|A],[I|B],[J|C],[H-I-J|T]):-i(A,B,C,T).
c(A/_-B/_-C/_,D/_-_/T-E/_,F/_-G/_-H/_):-T#=A+B+C+D+E+F+G+H.
r(A,B,C):-i(A,B,C,L),z(c,L).
q(63,V):-var(V).
q(42,1/_).
q(X,0/Y):-Y#=X-48.
l([],[0/_]).
l([H|T],[E|U]):-q(H,E),l(T,U).
p([],[[0/_,0/_]],0).
p([],[[0/_|T]],N):-M#=N-1,p([],[T],M).
p([H|T],[[0/_|E]|U],N):-p(T,U,N),l(H,E).
m([H|A],B):-length(H,N),p([],[R],N),p([H|A],M,N),z(r,[R|M]),p(B,M,N).
s(A):-setof(B,m(A,B),[_]).

Un predicato aggiuntivo che può essere utile per i test (non parte dell'invio):

d([]).
d([H|T]):-format("~s~n",[H]),d(T).

Prolog è sicuramente la lingua giusta per risolvere questo compito dal punto di vista pratico. Questo programma praticamente indica solo le regole di Minesweeper e consente al risolutore di vincoli di GNU Prolog di risolvere il problema da lì.

ze isono funzioni di utilità ( zesegue una sorta di operazione simile a una piega ma su insiemi di tre elementi adiacenti anziché 2; itraspone 3 elenchi di n elementi in un elenco di n 3 tuple). Memorizziamo internamente una cella come , dove x è 1 per una miniera e 0 per una nonmina, e y è il numero di mine adiacenti; esprime questo vincolo alla lavagna. si applica ad ogni riga del tabellone; e quindi controlla sex/ycrcz(r,M)M è una scheda valida.

Sfortunatamente, il formato di input richiesto per far funzionare direttamente questo lavoro è irragionevole, quindi ho anche dovuto includere un parser (che probabilmente rappresenta più codice rispetto al motore delle regole reale e la maggior parte del tempo impiegato per il debug; il motore delle regole di Minesweeper ha funzionato praticamente prima volta, ma il parser era pieno di thinkos). qconverte una singola cella tra un codice carattere e il nostro formato. converte una linea del tabellone (lasciando una cella nota per non essere una miniera, ma con un numero sconosciuto di mine vicine, su ciascun bordo della linea come bordo);x/ylp converte l'intero tabellone (incluso il bordo inferiore, ma escluso quello in alto). Tutte queste funzioni possono essere eseguite sia in avanti che all'indietro, quindi possono sia analizzare che stampare graziosamente la scheda. (C'è qualche fastidioso sussulto con il terzo argomento ap, che specifica la larghezza della scheda; questo perché Prolog non ha un tipo di matrice, e se non vincolo la tavola ad essere rettangolare, il programma andrà in un ciclo infinito provando progressivamente bordi più ampi attorno alla tavola.)

mè la principale funzione di risoluzione dei dragamine. Analizza la stringa di input, generando una scheda con un bordo corretto (tramite l'uso del caso ricorsivo pper convertire la maggior parte della scheda, quindi chiamando direttamente il caso base per generare il bordo superiore, che ha la stessa struttura del bordo inferiore). Quindi chiamaz(r,[R|M])per eseguire il motore delle regole di Minesweeper, che (con questo modello di chiamata) diventa un generatore che genera solo schede valide. A questo punto, il consiglio è ancora espresso come un insieme di vincoli, che è potenzialmente imbarazzante per noi; potremmo forse avere un unico insieme di vincoli che potrebbero rappresentare più di una scheda. Inoltre, non abbiamo ancora specificato da nessuna parte che ogni quadrato contiene al massimo una miniera. Come tale, abbiamo bisogno di "collassare esplicitamente la forma d'onda" di ogni quadrato, richiedendo che sia specificamente una (singola) miniera o un nonmine, e il modo più semplice per farlo è far scorrere il parser all'indietro ( var(V)il case di scorrere all'indietro e quindi il deparing della scheda lo costringe ad essere completamente noto). Infine, restituiamo la scheda analizzata daq(63,V)la custodia è progettata per impedire il?m; mdiventa così un generatore che prende una scheda parzialmente sconosciuta e genera tutte le schede conosciute coerenti con essa.

Questo è davvero abbastanza per risolvere Minesweeper, ma la domanda chiede esplicitamente di verificare se esiste esattamente una soluzione, piuttosto che trovare tutte le soluzioni. Come tale, ho scritto un predicato extra sche converte semplicemente il generatore min un set, e quindi afferma che il set ha esattamente un elemento. Ciò significa che srestituirà verità ( yes) se esiste davvero esattamente una soluzione, o falso ( no) se c'è più di una o meno di una.

dnon fa parte della soluzione e non è incluso nel bytecount; è una funzione per stampare un elenco di stringhe come se fosse una matrice, il che rende possibile ispezionare le schede generate da m(per impostazione predefinita, GNU Prolog stampa le stringhe come un elenco di codici ASCII, poiché tratta i due in modo sinonimo; questo formato è abbastanza difficile da leggere). È utile durante i test o se si desidera utilizzare mcome pratico risolutore di dragamine (poiché utilizza un risolutore di vincoli, è altamente efficiente).


11

Haskell, 193 169 168 byte

c '?'="*!"
c x=[x]
g x|t<-x>>" ",w<-length(words x!!0)+1=1==sum[1|p<-mapM c$t++x++t,and[sum[1|m<-[-1..1],n<-[j-w,j,j+w],p!!(m+n)=='*']==read[d]|(j,d)<-zip[0..]p,d>'/']]

Esempio di utilizzo: g "1121 1??* 12?* 0122"-> True.

Come funziona: crea un elenco di tutte le schede possibili con quelle ?sostituite con *o !( !significa ignorare in seguito). Questo viene fatto tramite mapM c, ma prima di anteporre e aggiungere un mucchio di spazi alla stringa di input in modo che la nostra indicizzazione non sia fuori portata. Per ognuna di queste schede controlla se è una scheda valida eseguendo il loop su tutti gli elementi (indice j) e se è un numero ( d>'/') anche sui suoi vicini (indice n, m), conta *e confronta con il numero. Infine controlla la lunghezza dell'elenco di schede valide.


7

Mathematica, 214 192 190 180 176 174 168 165 byte

0&/@Cases[b="*";If[!FreeQ[#,q="?"],(x#0@MapAt[x&,#,#&@@#~Position~q])/@{b,0},BlockMap[If[#[[2,2]]==b,b,Count[#,b,2]]&,#~ArrayPad~1,{3,3},1]]&@#,#/.q->_,All]=={0}&

Contiene U + F4A1 (uso privato). Questa funzione senza nome trova tutte le combinazioni possibili per "?"(cioè sostituendo tutte le "?"s con "*"o 0) e verifica se esiste una sola soluzione valida.

Spiegazione

b="*";

Imposta bsu "*".

!FreeQ[#,q="?"]

Impostato qsulla stringa "?". Controlla se c'è "?"nell'input.

If[ ..., (x#0 ... ,0}, BlockMap[ ... ]]

Se True...

(x#0@MapAt[x&,#,#&@@#~Position~q])/@{b,0}

Sostituire la prima occorrenza di q(= "?") con b(= "*") o 0(ovvero due uscite) e applicare nuovamente l'intera funzione.


Se False...

#~ArrayPad~1

Riempi l'input con uno strato di 0.

BlockMap[If[#[[2,2]]==b,b,Count[#,b,2]]&, ... ,{3,3},1]

Partizionare l'input in matrici 3 x 3 con offset 1. Per ogni partizione, applicare una funzione tale che se il valore medio è b(= "*"), l'output è il b(= "*") e se il valore medio non è b(= "*"), il output è il numero di b(= "*") nell'input. Questo passaggio rivaluta tutte le celle numeriche.


Cases[ ... ,#/.q->_,All]

Da tutti i risultati, trova quelli che corrispondono all'input

0&/@ ... =={0}

Controllare se l'ingresso è di lunghezza 1.


7

Perl, 215 byte

213 byte di codice + -p0flag (2 byte).

/.*/;$c="@+";$_=A x$c."
$_".A x$c;s/^|$/A/mg;sub t{my($_)=@_;if(/\?/){for$i(0..8,"*"){t(s/\?/$i/r)}}else{$r=1;for$i(/\d/g){$r&=!/(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%!=$i?"":R})/}$e+=$r}}t$_;$_=$e==1

L'idea del codice è quella di testare ogni possibilità e vedere se ce n'è una sola che porta a una scheda completamente piena che è valida.

Più leggibile, il codice appare come:

/.*/ ; $ c = "@ +" ; # conta le dimensioni di una linea. 
$ _            = A x $ c . "\ n $ _" . A x $ c ; # aggiungi una riga di "A" all'inizio e un'altra alla fine. 
s / ^ | $ / A / mg ; # aggiungi una "A" all'inizio e alla fine di ogni riga.          

# La funzione che risolve effettivamente il problema
 sub t { my $ _ = pop ; # Ottieni il parametro, memorizzalo in $ _ (argomento predefinito su regex). if ( / \? / ) { # se esiste un altro carattere sconosciuto. per $ i ( 0 .. 8 , "*" ) # non più caratteri sconosciuti, quindi qui controlliamo se la scheda è valida 
        $ r = 1 ; # se r == 1 alla fine, la scheda è valida, altrimenti non è per $ i ( / \ d / g ) { 
     
        
          { # prova ogni possibilità 
            t ( s / \? / $ i / r ) # chiamata ricorsiva in cui è stato sostituito il primo carattere sconosciuto } } else {   
        
       
          # per ogni numero presente nel tabellone
             # la seguente regex controlla se c'è un numero circondato da # miniere troppo o troppo poco. # (come funziona: magia!) 
         $ r & =! /(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%! = $ i? "": R}) / } 
        $ e + = $ r # Aumenta il numero di schede valide. } } 
t $ _ ;
            
             
        
    
 # Chiama la funzione precedente 
$ _ = $ e == 1 # Verifica se esiste una sola scheda valida ($ _ è implicitamente stampato grazie al flag -p). 

A proposito della regex nel mezzo:

/(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%!=$i?"":R})/

Nota che [^V]sta per "qualsiasi personaggio, incluso \ n".
Quindi l'idea è: 3 caratteri su una riga, quindi 3 su quello successivo (con $ial centro), quindi 3 su quello successivo. quei 3 gruppi di 3 numeri sono allineati, grazie [^V]{$c}e il numero a cui siamo interessati è nel mezzo.
E poi, "$1$2$3"=~y%*%%conta il numero di *(bombe) tra quei 9 caratteri: se è diverso $i, aggiungiamo una stringa vuota da abbinare ( ""=> corrispondenza istantanea, la regex restituisce vero), altrimenti forziamo un fallimento cercando di abbinare R( che non può essere nella stringa).
Se le partite regex, quindi il consiglio non è valido, così abbiamo deciso $rdi 0con $r&=!/.../.
Ed è per questo che ne aggiungiamo alcuniAovunque intorno a ogni riga: quindi non dobbiamo preoccuparci di bordi casi di numeri che si trovano vicino ai bordi del tabellone: ​​avranno Acome vicini, che non sono mine (ovviamente, quasi tutti i caratteri potrebbero avere lavoro, Ho scelto A).

È possibile eseguire il programma dalla riga di comando in questo modo:

perl -p0E '/.*/;$c="@+";$_=A x$c."\n$_".A x$c;s/^|$/A/mg;sub t{my($_)=@_;if(/\?/){for$i(0..8,"*"){t(s/\?/$i/r)}}else{$r=1;for$i(/\d/g){$r&=!/(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%!=$i?"":R})/}$e+=$r}}t$_;$_=$e==1' <<< "1121
1??*
12?*
0122"

La complessità non potrebbe essere peggiore: è O(m*9^n)dove si ntrova il numero di ?sulla scheda ed mè il numero di celle sulla scheda (senza contare la complessità della regex nel mezzo, il che è probabilmente piuttosto negativo). Sulla mia macchina, funziona abbastanza velocemente per un massimo di 4 ?, e inizia a essere più lento 5, impiega qualche minuto per 6 e non ho provato per numeri più grandi.


3

JavaScript (ES6), 221 229

g=>(a=>{for(s=i=1;~i;g.replace(x,c=>a[j++],z=j=0).replace(/\d/g,(c,p,g)=>([o=g.search`
`,-o,++o,-o,++o,-o,1,-1].map(d=>c-=g[p+d]=='*'),z|=c)),s-=!z)for(i=a.length;a[--i]='*?'[+(c=a[i]<'?')],c;);})(g.match(x=/\?/g)||[])|!s

Se si prevede che tutti gli input siano validi, ovvero con almeno 1 soluzione, è possibile salvare un byte cambiando s==1cons<2

Meno golf

g=>{
  a = g.match(/\?/g) || []; // array of '?' in a
  s = 1; // counter of solutions
  for(i=0; ~i;) // loop to find all configurations of ? and *
  {
    // get next configuration
    for(i = a.length; a[--i] = '*?'[+( c = a[i] < '?')], c; );
    z = 0; // init at 0, must stay 0 if all cells count is ok
    g
    .replace(/\?/g,c=>a[j++],j=0) // put ? and * at right places
    .replace(/\d/g,(c,p,g)=>(
       // look for mines in all 8 directions
       // for each mine decrease c
       // if c ends at 0, then the count is ok
       [o=g.search`\n`,-o,++o,-o,++o,-o,1,-1].map(d=>c-=g[p+d]=='*'),
       z|=c // z stays at 0 if count is ok
    )) // check neighbour count
    s-=!z // if count ok for all cells, decrement number of solutions
  }
  return s==0 // true if exactly one solution found
}

Test

F=
g=>(a=>{for(s=i=1;~i;g.replace(x,c=>a[j++],z=j=0).replace(/\d/g,(c,p,g)=>([o=g.search`
`,-o,++o,-o,++o,-o,1,-1].map(d=>c-=g[p+d]=='*'),z|=c)),s-=!z)for(i=a.length;a[--i]='*?'[+(c=a[i]<'?')],c;);})(g.match(x=/\?/g)||[])|!s

out=x=>O.textContent+=x+'\n'

Solvable=['1121\n1??*\n12?*\n0122'
,'1110\n1???\n1110\n0000'
,'1110\n3???\n??20\n*310'
,'****\n****\n****\n****'
,'0000\n0000\n0000\n0000'
,'1100\n*100\n2321\n??*2\n13*2\n1221\n1*10\n1110'
,'1121\n2*??\n2*31\n2220\n1*10']
Unsolvable=['1110\n2*31\n3*??\n2*4?\n112?'
,'01??11*211\n12??2323*1\n1*33*2*210\n12?2122321\n13?3101**1\n1***101221'
,'1***\n3*52\n2*31\n12??\n02??\n01??'
,'00000111\n000012*1\n00001*21\n22101110\n**100111\n?31123*1\n?311**31\n**113*20']
out('Solvable')
Solvable.forEach(t=>out(t+'\n'+F(t)+'\n'))
out('Unsolvable')
Unsolvable.forEach(t=>out(t+'\n'+F(t)+'\n'))
<pre id=O></pre>


Op ha detto che puoi giocare a golf quel byte.
Limone distruttibile

@DestructibleWatermelon grazie, ho rivisto tutto e ho salvato ancora qualche byte in più
edc65

0

JavaScript (Node.js) , 167 byte

s=>g=(r=c='',[p,...q]=s,w)=>w?0:p?(g(r+0,q,p=='*')+g(r+1,q,1/p),c==1):c-=-![...s].some((p,i)=>p>' '&&[-1,1,-q,-1-q,-2-q,q,q+1,q+2].map(j=>p-=~~r[i+j])|p,q=s.search`
`)

Provalo online!

Anche se op dice "l'input sarà sempre rettangolare, avrà almeno una soluzione", il falso campione 3 non corrisponde, quindi ho ancora bisogno di 1 soluzione, non <2 soluzione

s=>(        // p.s. Here "block" can also mean \n
  c=0,          // possible mine count
  g=(           // recursive
    r='',       // mine states
    [p,...q]=s, // known info to check possible state for a block
    w           // invert condition, stop if true
  )=>
    w?0:
      p?(       // for each block
        g(r+0,q,p=='*')+   // possibly not bomb if doesn't say so
        g(r+1,q,1/p),      // number/newline can't be bomb
        c==1               // only one bomb
      ):
        c-=-![...s].some(  // no block doesn't satisfy
          (p,i)=>
            p>' '&& // \n don't mean number
                    // other symbols turn into NaN when counting
            [-1,1,-q,-1-q,-2-q,q,q+1,q+2].map(j=>p-=~~r[i+j])
                    // subtract each neighbor, OOB = 0
            |p,     // difference between intended and actual
            q=s.search('\n') // how many blocks in a line
        )
)

sembra che "non abbinare" sia un errore di battitura, risolverlo rende 4 soluzioni
l4m2
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.