¿Cómo puedo saber si mi juego de rompecabezas siempre es posible?

He hecho una especie de juego de rompecabezas en el que el objetivo es deshacerse de todas las fichas blancas. Puedes intentarlo al final de la pregunta.

Cada vez, el tablero se genera aleatoriamente con mosaicos blancos en lugares aleatorios en una cuadrícula de 5 * 5. Puede hacer clic en cualquier ficha en esa cuadrícula y cambiará su color y todas las fichas que la tocan en sus lados. Mi dilema es el hecho de que no sé si generará un tablero imposible. ¿Cuál es la mejor manera de verificar cosas como esta?

<div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code">

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>
<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">
<div class="title">Tiles</div>
<div class="button" onclick="newgame()">New Game</div>

<div id="moves">Moves: 0</div>
</div> </div>
 
</div> </div>

68
Realmente deberías cambiar la respuesta aceptada. Robert le ofrece una forma matemática de generar una cuadrícula aleatoria Y verificar inmediatamente su factibilidad.
agregado el autor Johannes Setiabudi, fuente
Si estás interesado en este tipo de juegos de rompecabezas, consulta Colección de rompecabezas portátil . Aparte de este tipo (llamado Flip allí), puedes encontrar variantes de muchos puzzles japoneses y otros. Todo está bajo licencia BSD y probablemente una lectura interesante.
agregado el autor S.C. Madsen, fuente
Quiero seguir jugando, pero debido a tu pregunta, ¡la incertidumbre de si realmente ganaré me está comiendo! Divertido juego :)
agregado el autor Alen Kwane, fuente
@stephenwade hecho
agregado el autor Claas Rover, fuente
@MrDuk codepen.io/qwertyquerty/pen/WMGwVW ¡aquí está el proyecto terminado! Este es fijo y pulido. También he hecho una aplicación electrónica.
agregado el autor Claas Rover, fuente
¿Qué tal ingeniería inversa? Comience con una tabla en blanco, luego automatice, digamos 20 clics en cuadrados al azar. De esa forma sabes que debe haber una solución al final.
agregado el autor Denny, fuente
@Qwerty cuando intenté ver su pluma en la vista de página completa, recibí el mensaje "El propietario de esta pluma debe verificar su dirección de correo electrónico para habilitar la vista de página completa". Verifique su dirección de correo electrónico en CodePen para que pueda disfrutar de su juego en la ventana completa. :)
agregado el autor Joseph, fuente

6 Respuestas

Este es el tipo de juego donde el mismo movimiento realizado dos veces invierte el tablero a su estado anterior. Así que para asegurar que una tabla sea solucionable, genérela jugando en reversa. Comience con un tablero resuelto (en blanco), luego comience a hacer "clic" de manera aleatoria un cierto número de veces o hasta que el tablero tenga el número deseado de cuadrados blancos. Una solución es simplemente realizar los mismos movimientos en el orden inverso. Es posible que existan otras soluciones más cortas, pero se garantiza que tiene al menos una.

Otra solución mucho más compleja es definir un algoritmo de resolución que recorra todos los estados posibles del juego desde su posición inicial para tratar de encontrar la solución. Esto tardaría mucho más en implementarse y ejecutarse, pero permitiría que los tableros se generen de forma verdaderamente aleatoria. No voy a entrar en detalles de esta solución, porque no es tan buena idea.

161
agregado
Para lo que vale la pena, si bien esta es la respuesta correcta a la pregunta matemática sobre cómo responder a este problema, esta suele ser una práctica dudosa desde el punto de vista del diseño. Este tipo de generación, sin ningún plan en particular, generalmente conduce a rompecabezas que se sienten como "similares", sin ningún punto de interés en particular o cualquier tema unificador. Es posible 'generar de manera procesal' instancias de problemas interesantes para un juego de rompecabezas, pero por lo general requiere un análisis mucho más detallado de cuáles son las características interesantes de sus rompecabezas.
agregado el autor Steven Stadnicki, fuente
@Qwerty: para su problema específico, hacer clic en el mismo cuadrado dos veces se cancela, por lo que nunca hay razón para hacer clic en ningún cuadrado más de una vez. Es posible que desee elegir un cierto número de cuadrados para hacer clic sin repetir, o considerar una solución que asigne un XX% de probabilidad de clic a cada cuadrado del tablero. (Ed: Buena respuesta, +1!)
agregado el autor Tiago Fernandez, fuente
@Monty Absolutamente cierto, es una buena representación, pero algunos de esos estados dan como resultado estados de luz equivalentes, ya que hay 2 ^ 25 estados de luz y no todos pueden resolverse. Lo mismo se aplica a las soluciones que enumeré. El truco para generar un número aleatorio es que un buen acertijo es buscar un conteo particular de luces o conjuntos de bits, que puede ser difícil de ajustar con un número aleatorio puro.
agregado el autor Tiago Fernandez, fuente
@JeffBowman De hecho, el conjunto de juegos solubles se puede tratar como un valor de 25 bits, y cada bit corresponde a un cuadrado que es el número de veces que se ha cambiado el modo 2. Así que uno puede generar un número aleatorio en el rango 0. .33,554,432 y luego calcule el valor de cada cuadrado en el tablero en poco tiempo.
agregado el autor ytpillai, fuente
@JeffBowman Supongo que depende de tu definición de "buen rompecabezas". Un buen primer corte es el número de "1" bits de los 25 generados inicialmente. Entonces, si lo desea, puede contarlos, y si hay menos de 13, XOR el valor con 0x1FFFFF, invirtiendo todos sus bits. O simplemente imagínese que las probabilidades de generar aleatoriamente un rompecabezas trivial son tan bajas que, si esto sucede, el jugador se resuelve con facilidad esta vez, pero la próxima vez probablemente será difícil.
agregado el autor ytpillai, fuente
Esto es exactamente lo que hice cuando diseñé mis niveles para mi variante de este diseño que llamé "Congruencia". No sentí que pudiera pasar el tiempo para hacer la opción matemática, ya que presenté un montón de otras mecánicas que se mezclarían, así que lo que hice fue crear un editor que comenzó en una versión "resuelta" y podía hacer clic hasta se veía bien y se mezclaba y hacía clic en un botón para "exportar" el nivel.
agregado el autor user49905, fuente
He hecho casi el mismo juego anterior y terminé usando este enfoque. Incluí una animación al comienzo que muestra el estado resuelto yendo al estado no resuelto de manera rápida; era bonito.
agregado el autor user86873, fuente
"Otra solución, mucho más compleja, [...] permitiría que los tableros se generen de forma verdaderamente aleatoria". No hay nada "falso" sobre el método de su primer párrafo.
agregado el autor Will, fuente
Supongo que lo que realmente quise decir con eso fue que el algoritmo de generación de tableros no tendría que cambiar de su implementación un tanto más aleatoria de elegir cuadrados para activarse de manera arbitraria.
agregado el autor Valentin Tihomirov, fuente
@JaredGoguen extraño, lo agregué y regresé aquí para ver su comentario.
agregado el autor Claas Rover, fuente

Si bien las respuestas anteriores son inteligentes (y, probablemente, cómo lo haría de todos modos), este juego en particular es muy conocido. Se llama Lights Out y se resolvió matemáticamente. Hay una solución si y solo si dos sumas de varios elementos (que figuran en la página de wikipedia) se agregan a cero mod 2 (es decir, un número par). En general, un poco de álgebra lineal debe proporcionar condiciones de solución similares para los juegos en cualquier tablero.

93
agregado
@OrangeDog: Incluso 'Lights Out' no era original, esa es solo la marca que se hizo popular en los años 90. El artículo de wikipedia, por ejemplo, lista this y this
agregado el autor DLS3141, fuente
@Qwerty Aquí es un documento interesante para encontrar una configuración que se pueda encontrar . Además, indica que hay dos vectores de columna específicos n1 y n2 , de modo que para una configuración dada b , si dot ( b, n1) = 0 y punto (b, n2) = 0 , entonces b es solucionable. Aquí b es el estado actual de su campo convertido en un vector de columna.
agregado el autor cosmosparda, fuente
¿A qué respuestas se refiere como "las respuestas anteriores"? No está claro, ya que en mi pantalla solo hay una respuesta por encima de la suya. Recuerda que las respuestas cambian según los votos y las opciones de usuario. Siempre debe vincular a respuestas específicas en lugar de referirse a algo "arriba".
agregado el autor Will, fuente
Este juego en particular existe, ¡pero siempre puedes ampliar la idea! Añadir más características! Agregue diferentes reglas para lo que sucede cuando hace clic en algún lugar, como los colores que se juntan según la dirección desde la cual se activó/desactivó. Agrega diferentes "herramientas" que tienes que usar. Añadir tablas no rectangulares! Un montón de cosas divertidas para hacer. Solo recuerda que un movimiento siempre debe revertirse.
agregado el autor Valentin Tihomirov, fuente
@Qwerty hay muy pocas ideas originales, y ciertamente no es necesario tener una para tener éxito (c.f. Rovio, King).
agregado el autor nardecky, fuente
@ BlueRaja-DannyPflughoeft Lo sé, tuve un Merlin :)
agregado el autor nardecky, fuente
Es un poco triste descubrir que ya se ha hecho. Pensé que estaba en algo.
agregado el autor Claas Rover, fuente

Ir al revés al generar su rompecabezas.

En lugar de seleccionar aleatoriamente los mosaicos y convertirlos de blanco a negro, comience desde una pizarra en blanco, luego seleccione los mosaicos pero en lugar de convertir el azulejo que a negro, hágalo como si el usuario lo seleccionara, resultando en voltear todas las otras baldosas a su alrededor.

De esta manera, se le garantizará tener al menos una solución: el usuario tendrá que deshacer lo que hizo el jugador "AI" para crear el nivel.

13
agregado

Ed y Alexandre tienen el derecho de hacerlo.

Pero si do quiere saber si todas las soluciones son posibles, hay maneras.

Hay un número finito de rompecabezas posibles

Hacer clic en el mismo cuadrado dos veces produce el mismo resultado que no hacer clic en él, sin importar cuántos clics se hayan hecho entre ellos. Eso significa que cada solución se puede describir dando a cada cuadrado un valor binario de 'clic' o 'no clic'. De manera similar, cada rompecabezas se puede describir dando a cada cuadrado un valor binario de 'alternado' o 'no conmutado'. Eso significa que hay 2 ^ 25 rompecabezas posibles y 2 ^ 25 soluciones posibles. Si puede probar que cada solución resuelve un rompecabezas único, entonces debe haber una solución para cada rompecabezas. Del mismo modo, si encuentra dos soluciones que resuelven el mismo enigma, entonces no puede haber una solución para cada enigma.

Además, 2 ^ 25 es 33,554,432. Eso es bastante, pero no es un número inmanejable. Un buen algoritmo y una computadora decente probablemente podrían utilizar una fuerza bruta en un par de horas, especialmente si se considera que la mitad de los rompecabezas son inversos a la otra mitad.

7
agregado
@PeterTaylor Definitivamente tomará mucho más tiempo codificar el simulador que ejecutar los resultados.
agregado el autor Douglas Anderson, fuente
33.5 millones? Reemplace "un par de horas" por "un par de segundos" y probablemente sea pesimista.
agregado el autor Sheldon, fuente
Tiempo matemático: si desea calcular el número de tablas distintas (independientemente de la solvencia), teniendo en cuenta todas las simetrías, entonces el lema de Burnside es el camino a seguir. rotaciones, cuatro reflexiones, y luego cada una de esas 8 combinadas con la inversión de encendido/apagado), y para cada una de esas simetrías, algunas tablas no cambian por completo. Si tomas el promedio de tablas completamente sin cambios por simetría, es igual a la cantidad de tablas distintas.
agregado el autor boris, fuente
Más de la mitad son "inversos"; además de los reflejos horizontales, tiene reflejos y rotaciones verticales.
agregado el autor Robert S., fuente
Resulta que, como lo mencionó Robert Mastragostino en su respuesta, este es realmente un problema bien conocido y bien estudiado. Cada rompecabezas solucionable tiene exactamente 4 soluciones, y la mayoría de los tableros al azar no tienen solución. Buscar todo ese espacio puede ser divertido, pero como ya existe una prueba ( math.ksu.edu/math551/math551a.f06/lights_out.pdf ) también puede hacer un par de productos de puntos y tener la misma respuesta en unos pocos microsegundos. :)
agregado el autor GrandOpener, fuente
@ Clockwork-Muse, sí, pero es más difícil calcular un número exacto, porque mientras que los diseños asimétricos se pueden girar y voltear en 8 permutaciones, los diseños simétricos tienen menos permutaciones. Así que solo mencioné la inversión blanco/negro, ya que cada solución tiene exactamente 1 inversa. (Aunque para que funcione a la inversa, tienes que demostrar que puedes voltear todo el tablero)
agregado el autor user110428, fuente

Respuesta generalizada:

  1. Crea una matriz de tamaño (# movimientos) x (# luces).
  2. Coloca un 1 en una celda si el movimiento correspondiente a esa fila cambia la luz correspondiente a esa columna, 0 en caso contrario.
  3. Realice la eliminación de Gauss-Jordan (módulo 2) en la matriz.
  4. Si la matriz que resulta tiene un solo 1 en cada columna, y cada fila tiene como máximo un solo 1, entonces cada cuadrícula puede resolverse.
4
agregado

Otros ya han mencionado formas de encontrar si tu rompecabezas generado aleatoriamente es solucionable. Sin embargo, la pregunta que también deberías hacerte es si realmente quieres acertijos generados aleatoriamente.

Todos los rompecabezas generados al azar tienen el mismo defecto central: su dificultad es bastante impredecible. Los posibles rompecabezas que podría obtener pueden ir desde los ya resueltos, a los triviales (la solución es obvia), a los difíciles (la solución no es obvia) a los imposibles (el rompecabezas no se puede resolver en absoluto). Debido a que la dificultad es impredecible, se convierte en una experiencia insatisfactoria para el jugador, especialmente si hacen múltiples rompecabezas seguidos. Es muy poco probable que obtengan una curva de dificultad suave, lo que puede hacer que se aburran o se frustren según los rompecabezas que tengan.

Otro problema de la generación aleatoria es que el tiempo que tarda en inicializarse el rompecabezas es impredecible. En general, obtendrás un rompecabezas que se puede resolver (casi) de inmediato, pero con un poco de mala suerte, tus rompecabezas generados al azar podrían terminar en una racha de rompecabezas sin solución.

Una forma de resolver ambos problemas es tener vectores predefinidos de cada rompecabezas resuelto disponibles, organizados en grupos de dificultad, y luego seleccionando un rompecabezas aleatorio de los rompecabezas resuelvebles según la dificultad. De esta manera, estarás seguro de que cada rompecabezas es solucionable, que la dificultad es predecible y que la generación se realizará en un tiempo constante.

1
agregado