Se me ocurrió una solución que probablemente no sea la más eficiente, pero funciona lo suficientemente bien. Básicamente:
- Ordenar todas las palabras por longitud, descendente.
- Tome la primera palabra y colóquela en el pizarrón.
- Toma la siguiente palabra.
- Busque en todas las palabras que ya están en la pizarra y vea si hay posibles intersecciones (letras comunes) con esta palabra.
- Si hay una posible ubicación para esta palabra, recorra todas las palabras que están en el tablero y verifique si la nueva palabra interfiere.
- Si esta palabra no rompe el tablero, colóquelo allí y vaya al paso 3; de lo contrario, continúe buscando un lugar (paso 4).
- Continúe este ciclo hasta que todas las palabras se coloquen o no se puedan colocar.
Esto hace un crucigrama funcional, pero a menudo bastante pobre. Hubo una serie de modificaciones que hice a la receta básica anterior para obtener un mejor resultado.
- Al final de generar un crucigrama, califíquelo en función de cuántas palabras se colocaron (cuanto más, mejor), qué tan grande es el tablero (cuanto más pequeño, mejor) y la relación entre la altura y el ancho (cuanto más cerca a 1 el mejor). Genere una cantidad de crucigramas y luego compare sus puntajes y elija el mejor.
- En lugar de ejecutar un número arbitrario de iteraciones, he decidido crear tantos crucigramas como sea posible en un tiempo arbitrario. Si solo tiene una pequeña lista de palabras, obtendrá decenas de crucigramas posibles en 5 segundos. Un crucigrama más grande solo se puede elegir entre 5-6 posibilidades.
- Al colocar una nueva palabra, en lugar de ubicarla inmediatamente al encontrar una ubicación aceptable, asigne una puntuación a esa ubicación en función de cuánto aumenta el tamaño de la cuadrícula y cuántas intersecciones hay (lo ideal sería que cada palabra fuera atravesado por otras 2-3 palabras). Mantenga un registro de todas las posiciones y sus puntajes y luego elija la mejor.
Recientemente escribí el mío en Python. Puede encontrarlo aquí: http://bryanhelmig.com/python-crossword-puzzle-generator/ . No crea los densos crucigramas de estilo NYT, sino el estilo de crucigramas que puede encontrar en el libro de rompecabezas de un niño.
A diferencia de algunos algoritmos que descubrí que implementaban un método aleatorio de fuerza bruta para colocar palabras como algunos han sugerido, traté de implementar un enfoque de fuerza bruta ligeramente más inteligente en la colocación de palabras. Aquí está mi proceso:
Al final, tienes un crucigrama decente o un rompecabezas de búsqueda de palabras, ya que son casi lo mismo. Suele funcionar bastante bien, pero avíseme si tiene alguna sugerencia sobre mejoras. Las cuadrículas más grandes corren exponencialmente más lento; listas de palabras más grandes linealmente. Las listas de palabras más grandes también tienen muchas más posibilidades de obtener mejores números de colocación de palabras.
fuente
array.sort(key=f)
es estable, lo que significa (por ejemplo) que simplemente ordenar una lista de palabras alfabéticas por longitud mantendría todas las palabras de 8 letras ordenadas alfabéticamente.De hecho, escribí un programa de generación de crucigramas hace unos diez años (era críptico, pero las mismas reglas se aplicarían para los crucigramas normales).
Tenía una lista de palabras (y pistas asociadas) almacenadas en un archivo ordenado por uso descendente hasta la fecha (de modo que las palabras menos utilizadas estaban en la parte superior del archivo). Se eligió al azar una plantilla, básicamente una máscara de bits que representa los cuadrados negros y libres, de un grupo proporcionado por el cliente.
Luego, para cada palabra no completa en el rompecabezas (básicamente encuentre el primer cuadrado en blanco y vea si el que está a la derecha (palabra cruzada) o el que está debajo (palabra abajo) también está en blanco), se realizó una búsqueda de el archivo busca la primera palabra que encaja, teniendo en cuenta las letras que ya están en esa palabra. Si no había una palabra que pudiera encajar, simplemente marcó la palabra completa como incompleta y siguió adelante.
Al final habría algunas palabras incompletas que el compilador tendría que completar (y agregar la palabra y una pista al archivo si lo desea). Si no se les ocurriera ninguna idea, podrían editar el crucigrama manualmente para cambiar las restricciones o simplemente pedir una regeneración total.
Una vez que el archivo de palabra / pista alcanzó un cierto tamaño (y agregaba entre 50 y 100 pistas por día para este cliente), rara vez hubo que hacer más de dos o tres arreglos manuales para cada crucigrama. .
fuente
Este algoritmo crea 50 crucigramas densos de flecha 6x9 en 60 segundos. Utiliza una base de datos de palabras (con palabras + consejos) y una base de datos de placas (con placas preconfiguradas).
¡Una base de datos de palabras más grande disminuye considerablemente el tiempo de generación y algunos tipos de tableros son más difíciles de llenar! ¡Las tablas más grandes requieren más tiempo para llenarse correctamente!
Ejemplo:
Tarjeta 6x9 preconfigurada:
(# significa una punta en una celda,% significa dos puntas en una celda, no se muestran flechas)
Tablero 6x9 generado:
Consejos [línea, columna]:
fuente
Aunque esta es una pregunta anterior, intentaré una respuesta basada en un trabajo similar que he realizado.
Hay muchos enfoques para resolver problemas de restricción (que generallay están en la clase de complejidad NPC).
Esto está relacionado con la optimización combinatoria y la programación de restricciones. En este caso, las restricciones son la geometría de la cuadrícula y el requisito de que las palabras sean únicas, etc.
Los enfoques de aleatorización / recocido también pueden funcionar (aunque dentro del entorno adecuado).
¡La simplicidad eficiente podría ser la sabiduría suprema!
Los requisitos eran para un compilador de crucigramas más o menos completo y un generador (visual WYSIWYG).
Dejando a un lado la parte del constructor WYSIWYG, el resumen del compilador fue este:
Cargue las listas de palabras disponibles (ordenadas por longitud de palabra, es decir, 2,3, .., 20)
Encuentre los espacios de palabras (es decir, palabras de la cuadrícula) en la cuadrícula construida por el usuario (por ejemplo, palabra en x, y con longitud L, horizontal o vertical) (complejidad O (N))
Calcule los puntos de intersección de las palabras de la cuadrícula (que deben completarse) (complejidad O (N ^ 2))
Calcule las intersecciones de las palabras en las listas de palabras con las distintas letras del alfabeto utilizadas (esto permite buscar palabras coincidentes mediante el uso de una plantilla, por ejemplo, la tesis de Sik Cambon como la utilizada por cwc ) (complejidad O (WL * AL))
Los pasos .3 y .4 permiten hacer esta tarea:
a. Las intersecciones de las palabras de la cuadrícula con ellas mismas permiten crear una "plantilla" para tratar de encontrar coincidencias en la lista de palabras asociadas de palabras disponibles para esta palabra de la cuadrícula (mediante el uso de las letras de otras palabras de intersección con esta palabra que ya están llenas en un cierto paso del algoritmo)
si. Las intersecciones de las palabras en una lista de palabras con el alfabeto permiten encontrar palabras coincidentes (candidatas) que coinciden con una "plantilla" dada (por ejemplo, 'A' en primer lugar y 'B' en tercer lugar, etc.)
Entonces, con estas estructuras de datos implementadas, el algoritmo utilizado fue algo así:
NOTA: si la cuadrícula y la base de datos de palabras son constantes, los pasos anteriores solo se pueden hacer una vez.
El primer paso del algoritmo es seleccionar un espacio de palabras vacío (palabra de cuadrícula) al azar y llenarlo con una palabra candidata de su lista de palabras asociada (la aleatorización permite producir diferentes soluciones en ejecuciones consecutivas del algoritmo) (complejidad O (1) u O ( N))
Para cada espacio de palabras aún vacío (que tiene intersecciones con espacios de palabras ya llenos), calcule una razón de restricción (esto puede variar, algo simple es el número de soluciones disponibles en ese paso) y clasifique los espacios de palabras vacíos por esta razón (complejidad O (NlogN ) o O (N))
Recorra los espacios de palabras vacíos calculados en el paso anterior y para cada uno intente una serie de soluciones cancdidadas (asegurándose de que "se conserve la consistencia del arco", es decir, la cuadrícula tiene una solución después de este paso si se usa esta palabra) y ordénelas de acuerdo con disponibilidad máxima para el siguiente paso (es decir, el siguiente paso tiene el máximo de soluciones posibles si esta palabra se usa en ese momento en ese lugar, etc.) (complejidad O (N * MaxCandidatesUsed))
Rellene esa palabra (márquela como rellenada y vaya al paso 2)
Si no se encuentra una palabra que satisfaga los criterios del paso .3, intente retroceder a otra solución candidata de algún paso anterior (los criterios pueden variar aquí) (complejidad O (N))
Si se encuentra un retroceso, use la alternativa y, opcionalmente, restablezca las palabras ya rellenadas que puedan necesitar restablecerse (márquelas como no rellenadas nuevamente) (complejidad O (N))
Si no se encuentra un retroceso, no se puede encontrar la solución (al menos con esta configuración, inicialización, etc.)
De lo contrario, cuando todos los lotes de palabras están llenos, tienes una solución
Este algoritmo realiza un recorrido aleatorio consistente del árbol de solución del problema. Si en algún momento hay un callejón sin salida, realiza un retroceso a un nodo anterior y sigue otra ruta. Hasta que se encuentre una solución encontrada o se agote el número de candidatos para los distintos nodos.
La parte de coherencia asegura que una solución encontrada sea realmente una solución y la parte aleatoria permite producir diferentes soluciones en diferentes ejecuciones y también, en promedio, tener un mejor rendimiento.
PD. todo esto (y otros) se implementaron en la capacidad de JavaScript puro (con procesamiento paralelo y WYSIWYG)
PS2 El algoritmo se puede paralelizar fácilmente para producir más de una solución (diferente) al mismo tiempo
Espero que esto ayude
fuente
¿Por qué no utilizar un enfoque probabilístico aleatorio para comenzar? Comience con una palabra, y luego elija repetidamente una palabra al azar e intente ajustarla al estado actual del rompecabezas sin romper las restricciones en el tamaño, etc. Si falla, simplemente comience de nuevo.
Te sorprenderá la frecuencia con la que funciona un enfoque de Monte Carlo como este.
fuente
Aquí hay un código JavaScript basado en la respuesta de nickf y el código Python de Bryan. Solo publícalo en caso de que alguien más lo necesite en js.
fuente
Generaría dos números: longitud y puntuación de Scrabble. Suponga que un puntaje bajo de Scrabble significa que es más fácil unirse (puntajes bajos = muchas letras comunes). Ordene la lista por longitud descendente y puntuación de Scrabble ascendente.
A continuación, solo baja la lista. Si la palabra no se cruza con una palabra existente (verifique cada palabra por su longitud y puntaje de Scrabble, respectivamente), luego póngala en la cola y verifique la siguiente palabra.
Enjuague y repita, y esto debería generar un crucigrama.
Por supuesto, estoy bastante seguro de que esto es O (n!) Y no se garantiza que complete el crucigrama por usted, pero tal vez alguien pueda mejorarlo.
fuente
He estado pensando en este problema. Mi sensación es que para crear un crucigrama realmente denso, no puede esperar que su lista de palabras limitada sea suficiente. Por lo tanto, es posible que desee tomar un diccionario y colocarlo en una estructura de datos "trie". Esto le permitirá encontrar fácilmente palabras que llenen los espacios sobrantes. En un trie, es bastante eficiente implementar un recorrido que, por ejemplo, le da todas las palabras de la forma "c? T".
Por lo tanto, mi pensamiento general es: crear algún tipo de enfoque de fuerza bruta como se describe aquí para crear una cruz de baja densidad y completar los espacios en blanco con palabras del diccionario.
Si alguien más ha adoptado este enfoque, hágamelo saber.
fuente
Estaba jugando con el motor generador de crucigramas, y esto me pareció lo más importante:
0.
!/usr/bin/python
a.
allwords.sort(key=len, reverse=True)
si. haga algún elemento / objeto como el cursor que caminará alrededor de la matriz para una fácil orientación a menos que desee iterar por elección aleatoria más adelante.
el primero, toma el primer par y colócalos a través de 0,0; almacenar el primero como nuestro crucigrama actual 'líder'.
mover el cursor por orden diagonal o aleatorio con mayor probabilidad diagonal a la siguiente celda vacía
iterar sobre las palabras me gusta y usar la longitud del espacio libre para definir la longitud máxima de la palabra:
temp=[] for w_size in range( len( w_space ), 2, -1 ) : # t for w in [ word for word in allwords if len(word) == w_size ] : # if w not in temp and putTheWord( w, w_space ) : # temp.append( w )
para comparar la palabra con el espacio libre que usé, es decir:
Después de cada palabra utilizada con éxito, cambie de dirección. Bucle mientras se llenan todas las celdas O se queda sin palabras O por límite de iteraciones y luego:
# CHANGE ALL WORDS LIST inexOf1stWord = allwords.index( leading_w ) allwords = allwords[:inexOf1stWord+1][:] + allwords[inexOf1stWord+1:][:]
... e iterar de nuevo nuevo crucigrama.
Haga el sistema de puntuación por facilidad de llenado y algunos cálculos de estimación. Proporcione puntaje para el crucigrama actual y limite la opción posterior agregándolo a la lista de crucigramas hechos si el sistema de puntaje satisface el puntaje.
Después de la primera sesión de iteración, repita nuevamente desde la lista de crucigramas hechos para finalizar el trabajo.
Mediante el uso de más parámetros, la velocidad puede mejorarse en gran medida.
fuente
Obtendría un índice de cada letra utilizada por cada palabra para conocer posibles cruces. Entonces elegiría la palabra más grande y la usaría como base. Seleccione el próximo grande y crúcelo. Enjuague y repita. Probablemente sea un problema de NP.
Otra idea es crear un algoritmo genético donde la métrica de la fuerza es cuántas palabras puedes poner en la cuadrícula.
Lo difícil que encuentro es cuándo no se puede cruzar una determinada lista.
fuente
He codificado una solución JavaScript / jQuery para este problema:
Demostración de muestra: http://www.earthfluent.com/crossword-puzzle-demo.html
Código fuente: https://github.com/HoldOffHunger/jquery-crossword-puzzle-generator
La intención del algoritmo que utilicé:
Describiré el algoritmo que utilicé:
Agrupe las palabras de acuerdo con las que comparten una letra común.
A partir de estos grupos, cree conjuntos de una nueva estructura de datos ("bloques de palabras"), que es una palabra principal (que se ejecuta a través de todas las demás palabras) y luego las otras palabras (que se ejecutan a través de la palabra principal).
Comience el crucigrama con el primero de estos bloques de palabras en la posición superior izquierda del crucigrama.
Para el resto de los bloques de palabras, comenzando desde la posición más inferior derecha del crucigrama, muévase hacia arriba y hacia la izquierda, hasta que no haya más espacios disponibles para llenar. Si hay más columnas vacías hacia arriba que hacia la izquierda, muévase hacia arriba y viceversa.
fuente
var crosswords = generateCrosswordBlockSources(puzzlewords);
. Solo consola registre este valor. No olvides que hay un "modo trampa" en el juego, en el que puedes hacer clic en "Revelar respuesta" para obtener el valor de inmediato.Este aparece como un proyecto en el curso AI CS50 de Harvard. La idea es formular el problema de generación de crucigramas como un problema de satisfacción de restricciones y resolverlo con retroceso con diferentes heurísticas para reducir el espacio de búsqueda.
Para comenzar necesitamos un par de archivos de entrada:
``
``
Un vocabulario de entrada (lista de palabras / diccionario) del que se elegirán las palabras candidatas (como la que se muestra a continuación).
a abandon ability able abortion about above abroad absence absolute absolutely ...
Ahora el CSP está definido y se resolverá de la siguiente manera:
A continuación se muestra el resultado que se obtuvo utilizando una implementación del algoritmo de resolución CSP:
``
La siguiente animación muestra los pasos de retroceso:
Aquí hay otro con una lista de palabras en idioma bengalí (bengalí):
fuente