Come posso sapere se il mio puzzle game è sempre possibile?


68

Ho realizzato una sorta di puzzle game in cui l'obiettivo è quello di eliminare tutte le tessere bianche. Puoi provarlo alla fine della domanda.

Ogni volta, la scheda viene generata casualmente con tessere bianche in punti casuali su una griglia 5 * 5. Puoi fare clic su qualsiasi tessera su quella griglia e cambierà il colore di essa e tutte le tessere toccandole sui lati. Il mio dilemma è il fatto che non so se genererà una scheda impossibile. Qual è il modo migliore per controllare cose come questa?

function newgame() {
 moves = 0;
    document.getElementById("moves").innerHTML = "Moves: "+moves;

  for (var i = 0; i < 25; i++) {
   if (Math.random() >= 0.5) {
$(document.getElementsByClassName('block')[i]).toggleClass("b1 b2")
   }
}
}
newgame();
function toggle(a,b) {  
  moves += 1;
  document.getElementById("moves").innerHTML = "Moves: "+moves;
$(document.getElementsByClassName('block')[a+(b*5)]).toggleClass("b1 b2");

if (a<4) {$(document.getElementsByClassName('block')[(a+1)+(b*5)]).toggleClass("b1 b2")}
  
  
if (a>0) {$(document.getElementsByClassName('block')[(a-1)+(b*5)]).toggleClass("b1 b2")}
  
  
if (b<4) {$(document.getElementsByClassName('block')[a+((b+1)*5)]).toggleClass("b1 b2")}
  
if (b>0) {$(document.getElementsByClassName('block')[a+((b-1)*5)]).toggleClass("b1 b2")}
}
body {
  background-color: #000000;
}

.game {
  float: left;
  background-color: #000000;
  width: 300px;
  height: 300px;
  overflow: hidden;
  overflow-x: hidden;
  user-select: none;
  display: inline-block;
}

.container {
  border-color: #ffffff;
  border-width: 5px;
  border-style: solid;
  border-radius: 5px;
  width: 600px;
  height: 300px;
  text-align: center;
}

.side {
  float: left;
  background-color: #000000;
  width: 300px;
  height: 300px;
  overflow: hidden;
  overflow-x: hidden;
  user-select: none;
  display: inline-block;
}

.block {
  transition: background-color 0.2s;
  float: left;
}

.b1:hover {
  background-color: #444444;
  cursor: pointer;
}

.b2:hover {
  background-color: #bbbbbb;
  cursor: pointer;
}

.row {
  width: 300px;
  overflow: auto;
  overflow-x: hidden;
}

.b1 {
  display: inline-block;
  height: 50px;
  width: 50px;
  background-color: #000000;
  border-color: #000000;
  border-width: 5px;
  border-style: solid;
}




.b2 {
  display: inline-block;
  height: 50px;
  width: 50px;
  background-color: #ffffff;
  border-color: #000000;
  border-width: 5px;
  border-style: solid;
}



.title {
  width: 200px;
  height: 50px;
  color: #ffffff;
  font-size: 55px;
  font-weight: bold;
  font-family: Arial;
  display: table-cell;
  vertical-align: middle;
}

.button {
  cursor: pointer;
  width: 200px;
  height: 50px;
  background-color: #000000;
  border-color: #ffffff;
  border-style: solid;
  border-width: 5px;
  color: #ffffff;
  font-size: 25px;
  font-weight: bold;
  font-family: Arial;
  display: table-cell;
  vertical-align: middle;
  border-radius: 5px;
  transition: background-color 0.3s, color 0.3s;
}

.button:hover {
  background-color: #ffffff;
  color: #000000;
}

.sidetable {
  padding: 30px 0px;
  height: 200px;
}


#moves {
  width: 200px;
  height: 50px;
  color: #aaaaaa;
  font-size: 30px;
  font-weight: bold;
  font-family: Arial;
  display: table-cell;
  vertical-align: middle;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<center>
  <div class="container">
  
  
  <div class="game"><div class="row"><div onclick="toggle(0,0);" class="block b1"></div><div onclick="toggle(1,0);" class="block b1"></div><div onclick="toggle(2,0);" class="block b1"></div><div onclick="toggle(3,0);" class="block b1"></div><div onclick="toggle(4,0);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,1);" class="block b1"></div><div onclick="toggle(1,1);" class="block b1"></div><div onclick="toggle(2,1);" class="block b1"></div><div onclick="toggle(3,1);" class="block b1"></div><div onclick="toggle(4,1);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,2);" class="block b1"></div><div onclick="toggle(1,2);" class="block b1"></div><div onclick="toggle(2,2);" class="block b1"></div><div onclick="toggle(3,2);" class="block b1"></div><div onclick="toggle(4,2);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,3);" class="block b1"></div><div onclick="toggle(1,3);" class="block b1"></div><div onclick="toggle(2,3);" class="block b1"></div><div onclick="toggle(3,3);" class="block b1"></div><div onclick="toggle(4,3);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,4);" class="block b1"></div><div onclick="toggle(1,4);" class="block b1"></div><div onclick="toggle(2,4);" class="block b1"></div><div onclick="toggle(3,4);" class="block b1"></div><div onclick="toggle(4,4);" class="block b1"></div></div></div>
    
    <div class="side">
      <center class="sidetable">
        <div class="title">Tiles</div>
        <br>
        <div class="button" onclick="newgame()">New Game</div>
        <br><br>
        <div id="moves">Moves: 0</div>
      </center>
    </div>
    
  </div>
    </center>


9
Se sei interessato a questo tipo di giochi puzzle, dai un'occhiata alla collezione di puzzle portatili di Simon Tatham . Oltre a questo tipo (chiamato Flip lì), puoi trovare varianti di molti puzzle giapponesi e altri. Tutto è sotto licenza BSD e probabilmente una lettura interessante.
Dubu

10
Che ne dici di decodificare? Inizia con una lavagna vuota, quindi automatizza, ad esempio 20 clic su quadrati casuali. In questo modo sai che alla fine ci deve essere una soluzione.
AJFaraday,

3
Voglio continuare a giocare, ma a causa della tua domanda, l'incertezza sul fatto che vincerò o meno mi sta mangiando! Gioco divertente :)
MrDuk,

@MrDuk codepen.io/qwertyquerty/pen/WMGwVW ecco il progetto finito! Questo è riparato e lucidato. Ho anche realizzato un'app elettronica.
Qwerty

@Qwerty quando ho provato a visualizzare la tua penna nella visualizzazione a pagina intera, ho ricevuto il messaggio "Il proprietario di questa penna deve verificare il proprio indirizzo e-mail per abilitare la visualizzazione a pagina intera". Verifica il tuo indirizzo e-mail su CodePen in modo che io possa goderti il ​​tuo gioco in tutta la finestra! :)
stephenwade, l'

Risposte:


161

Questo è il tipo di gioco in cui la stessa mossa eseguita due volte riporta la tavola al suo stato precedente. Quindi, per garantire che una tavola sia risolvibile, generala giocando al contrario. Inizia con una scheda (vuota) risolta, quindi inizia programmaticamente "facendo clic" in modo casuale un determinato numero di volte o fino a quando la scheda non ha il numero desiderato di quadrati bianchi. Una soluzione è quindi semplicemente eseguire le stesse mosse nell'ordine inverso. Potrebbero esistere altre soluzioni più brevi, ma sei sicuro di averne almeno una.

Un'altra soluzione molto più complessa è quella di definire un algoritmo di risoluzione che attraversi tutti i possibili stati di gioco dalla posizione iniziale per cercare di trovare la soluzione. Ciò richiederebbe molto più tempo per essere implementato ed eseguito, ma permetterebbe alle schede di essere veramente generate casualmente. Non entrerò nei dettagli di questa soluzione, perché non è un'idea altrettanto buona.


22
@Qwerty: per il tuo problema specifico, fare clic due volte sullo stesso quadrato si annulla, quindi non c'è mai motivo di fare clic su un quadrato più di una volta. Puoi scegliere un certo numero di quadrati su cui fare clic senza ripetere, oppure prendere in considerazione una soluzione che assegna una probabilità del 20% di clic a ciascun quadrato sul tabellone. (Ed: bella risposta, +1!)
Jeff Bowman

3
Ho fatto quasi lo stesso gioco prima e ho finito con questo approccio. All'inizio ho incluso un'animazione che mostra lo stato risolto che passa rapidamente allo stato irrisolto; è stato carino.
Jared Goguen,

1
@JaredGoguen strano, l'ho aggiunto e sono tornato qui per vedere il tuo commento.
Qwerty,

4
@JeffBowman In effetti, l'insieme di giochi risolvibili può essere trattato come un valore di 25 bit, con ogni bit corrispondente a un quadrato che è il numero di volte in cui è stato capovolto la mod 2. Quindi si può generare un numero casuale nell'intervallo 0. .33.554.432 e quindi calcolare il valore di ciascun quadrato sul tabellone da esso in breve tempo.
Monty Harder,

7
Per quello che vale, mentre questa è la risposta corretta alla domanda matematica su come rispondere a questo problema, questa è di solito una pratica dubbia dal punto di vista del design. Questo tipo di generazione, senza alcun piano particolare, generalmente porta a enigmi che si sentono molto "uguali", senza particolari punti di interesse o temi unificanti. È possibile "generare proceduralmente" istanze problematiche interessanti per un gioco puzzle, ma di solito richiede uno sguardo molto più duro a quali sono le caratteristiche interessanti dei tuoi puzzle.
Steven Stadnicki,

92

Mentre le risposte di cui sopra sono intelligenti (e probabilmente come lo farei comunque), questo particolare gioco è molto noto. Si chiama Lights Out ed è stato risolto matematicamente. C'è una soluzione se e solo se due somme di vari elementi (fornite nella pagina di Wikipedia) si sommano a zero mod 2 (cioè un numero pari). In generale, una piccola algebra lineare dovrebbe fornire condizioni di soluzione simili per i giochi su qualsiasi tavola.


2
È un po 'triste scoprire che è già stato realizzato. Pensavo di essere su qualcosa.
Qwerty,

39
@Qwerty ci sono pochissime idee originali e sicuramente non hai bisogno di averne una per avere successo (vedi Rovio, Re).
Smetti di fare del male a Monica l'

14
Questo particolare gioco esiste, ma puoi sempre ampliare l'idea! Aggiungi più funzionalità! Aggiungi regole diverse per ciò che accade quando fai clic da qualche parte, come i colori che si sommano in base alla direzione da cui è stato attivato / disattivato. Aggiungi diversi "strumenti" che devi usare. Aggiungi schede non rettangolari! Molte cose divertenti da fare. Ricorda solo che una mossa deve sempre invertirsi.
Ed Marty,

7
@OrangeDog: Anche "Lights Out" non era originale, questo è solo il nome del marchio che è diventato popolare negli anni '90. L'articolo di Wikipedia, ad esempio, elenca questo e questo
BlueRaja - Danny Pflughoeft

1
A quali risposte ti riferisci come "le risposte sopra"? Non è del tutto chiaro, poiché sul mio schermo c'è solo una risposta sopra la tua. Ricorda che le risposte cambiano l'ordine in base ai voti e alle opzioni dell'utente. Dovresti sempre collegarti a risposte specifiche invece di fare riferimento a qualcosa "sopra".
David Richerby,

13

Fai il contrario quando generi il tuo puzzle.

Invece di selezionare casualmente le tessere e trasformarle da bianche a nere, inizia da un'ardesia vuota, quindi seleziona le tessere ma invece di trasformare quella tessera in nero, rendila come se l'utente l'avesse selezionata, con il risultato di capovolgere tutte le altre tessere intorno ad esso.

In questo modo avrai la garanzia di avere almeno una soluzione: l'utente dovrà annullare ciò che il tuo giocatore "AI" ha fatto per creare il livello.


7

Ed e Alexandre ne hanno il diritto.

Ma se non volete sapere se ogni soluzione è possibile, ci sono modi.

Esistono un numero finito di possibili puzzle

Fare clic due volte sullo stesso quadrato produce lo stesso risultato del non fare clic su di esso, indipendentemente dal numero di clic effettuati tra di essi. Ciò significa che ogni soluzione può essere descritta assegnando a ciascun quadrato un valore binario di "cliccato" o "non cliccato". Allo stesso modo, ogni enigma può essere descritto dando ad ogni quadrato un valore binario di 'toggled' o 'not toggled'. Ciò significa che ci sono 2 ^ 25 possibili puzzle e 2 ^ 25 possibili soluzioni. Se puoi provare che ogni soluzione risolve un puzzle unico, allora deve esserci una soluzione per ogni puzzle. Allo stesso modo, se trovi due soluzioni che risolvono lo stesso puzzle, allora non può esserci una soluzione per ogni puzzle.

Inoltre, 2 ^ 25 è 33.554.432. È abbastanza, ma non è un numero ingestibile. Un buon algoritmo e un computer decente potrebbero probabilmente creare una forza bruta in un paio d'ore, specialmente se si considera che metà dei puzzle sono inversioni dell'altra metà.


4
Più della metà sono "inverse" - oltre ai riflessi orizzontali, ci sono riflessi e rotazioni verticali.
Clockwork-Muse

@ Clockwork-Muse, sì, ma è più difficile calcolare un numero esatto, perché mentre i disegni asimmetrici possono essere ruotati e capovolti in 8 permutazioni, i disegni simmetrici hanno meno permutazioni. Quindi ho citato solo l'inversione bianco / nero, poiché ogni soluzione ha esattamente 1 inverso. (Anche se per far sì che quell'inverso funzioni, devi dimostrare che puoi capovolgere l'intera scacchiera)
Arcanist Lupus

Si scopre, come ha affermato Robert Mastragostino nella sua risposta, questo è in realtà un problema ben noto e ben studiato. Ogni puzzle risolvibile ha esattamente 4 soluzioni e la maggior parte delle schede casuali non è risolvibile. Cercare in tutto quello spazio potrebbe essere divertente, ma poiché esiste già una prova ( math.ksu.edu/math551/math551a.f06/lights_out.pdf ) potresti anche fare un paio di prodotti punto e avere la stessa risposta in pochi microsecondi. :)
GrandOpener

Tempo matematico: se vuoi calcolare il numero di schede distinte (indipendentemente dalla solvibilità), tenendo conto di tutte le simmetrie, allora il lemma di Burnside è la strada da percorrere: ci sono 16 simmetrie (una banale, tre rotazioni, quattro riflessioni e quindi ciascuno di questi 8 combinato con l'inversione di on / off), e per ognuna di quelle simmetrie un certo numero di schede è completamente invariato. Se prendi la media di schede completamente invariate per simmetria, è uguale al numero di schede distinte.
Arthur

1
@PeterTaylor Ci vorrà sicuramente molto più tempo per codificare il simulatore che non per eseguire i risultati.
corsiKa

4

Risposta generalizzata:

  1. Crea una matrice di dimensioni (# mosse) x (# luci).
  2. Inserisci un 1 in una cella se lo spostamento corrispondente a quella riga attiva o disattiva quella luce corrispondente a quella colonna, 0 altrimenti.
  3. Eseguire l'eliminazione di Gauss-Jordan (modulo 2) sulla matrice.
  4. Se la matrice risultante ha un solo 1 in ogni colonna e ogni riga ha al massimo un singolo 1, allora ogni griglia è risolvibile.

1

Altri hanno già menzionato i modi per scoprire se il tuo puzzle generato casualmente è risolvibile. la domanda che dovresti anche porre, tuttavia, è se desideri effettivamente puzzle generati casualmente.

I puzzle generati casualmente hanno tutti lo stesso difetto di base: la loro difficoltà è praticamente imprevedibile. I possibili puzzle che potresti ottenere possono variare da già risolti, a banali (la soluzione è ovvia) a difficili (la soluzione non è ovvia) a impossibili (il puzzle non è affatto risolvibile). Poiché la difficoltà è imprevedibile, rende l'esperienza insoddisfacente per il giocatore, soprattutto se fanno più puzzle di fila. È altamente improbabile che ottengano una curva di difficoltà regolare, che può renderli annoiati o frustrati a seconda dei puzzle che ottengono.

Un altro problema della generazione casuale è che il tempo necessario per l'inizializzazione del puzzle è imprevedibile. In generale, otterrai un puzzle risolvibile (quasi) immediatamente, ma con un po 'di sfortuna, i tuoi puzzle generati casualmente potrebbero finire su una serie di puzzle irrisolvibili.

Un modo per risolverli entrambi è avere vettori predefiniti di ogni puzzle risolvibile disponibile, disposti in gruppi di difficoltà, quindi selezionando un puzzle casuale tra i puzzle risolvibili in base alla difficoltà. In questo modo, sarai sicuro che ogni enigma è risolvibile, che la difficoltà è prevedibile e che la generazione sarà fatta in tempo costante.

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.