Antworten:
Ich habe eine Lösung gefunden, die wahrscheinlich nicht die effizienteste ist, aber gut genug funktioniert. Grundsätzlich:
Dies macht ein funktionierendes, aber oft recht schlechtes Kreuzworträtsel. Es gab eine Reihe von Änderungen, die ich am obigen Grundrezept vorgenommen habe, um ein besseres Ergebnis zu erzielen.
Ich habe erst kürzlich meine eigene in Python geschrieben. Sie finden es hier: http://bryanhelmig.com/python-crossword-puzzle-generator/ . Es werden nicht die dichten Kreuzworträtsel im NYT-Stil erstellt, sondern die Kreuzworträtsel, die Sie möglicherweise im Puzzlespielbuch eines Kindes finden.
Im Gegensatz zu einigen Algorithmen, die ich dort herausgefunden habe und die eine zufällige Brute-Force-Methode zum Platzieren von Wörtern implementiert haben, wie einige vorgeschlagen haben, habe ich versucht, einen etwas intelligenteren Brute-Force-Ansatz bei der Wortplatzierung zu implementieren. Hier ist mein Prozess:
Am Ende haben Sie ein anständiges Kreuzworträtsel oder ein Wortsuchrätsel, da sie ungefähr gleich sind. Es läuft in der Regel recht gut, aber lassen Sie mich wissen, wenn Sie Verbesserungsvorschläge haben. Größere Gitter laufen exponentiell langsamer. größere Wortlisten linear. Größere Wortlisten haben auch eine viel höhere Chance auf bessere Wortplatzierungsnummern.
array.sort(key=f)
ist stabil, was (zum Beispiel) bedeutet, dass durch einfaches Sortieren einer alphabetischen Wortliste nach Länge alle 8-Buchstaben-Wörter alphabetisch sortiert bleiben.
Ich habe vor ungefähr zehn Jahren tatsächlich ein Programm zur Generierung von Kreuzworträtseln geschrieben (es war kryptisch, aber für normale Kreuzworträtsel gelten dieselben Regeln).
Es hatte eine Liste von Wörtern (und zugehörigen Hinweisen) in einer Datei gespeichert, sortiert nach absteigender Verwendung bis heute (so dass weniger verwendete Wörter oben in der Datei standen). Eine Vorlage, im Grunde eine Bitmaske, die die schwarzen und freien Quadrate darstellt, wurde zufällig aus einem vom Client bereitgestellten Pool ausgewählt.
Dann wurde für jedes nicht vollständige Wort im Puzzle (im Grunde genommen das erste leere Quadrat finden und prüfen, ob das rechte (quer zum Wort) oder das darunter liegende (das untere Wort) ebenfalls leer ist) eine Suche durchgeführt Die Datei sucht nach dem ersten passenden Wort und berücksichtigt dabei die Buchstaben, die bereits in diesem Wort enthalten sind. Wenn es kein passendes Wort gab, haben Sie das ganze Wort als unvollständig markiert und sind weitergegangen.
Am Ende wären einige unvollständige Wörter, die der Compiler ausfüllen müsste (und das Wort und einen Hinweis zur Datei hinzufügen, falls gewünscht). Wenn sie keine Ideen haben, können sie das Kreuzworträtsel manuell bearbeiten, um Einschränkungen zu ändern, oder einfach eine vollständige Neuerstellung anfordern.
Sobald die Wort- / Hinweisdatei eine bestimmte Größe erreicht hatte (und für diesen Client täglich 50 bis 100 Hinweise hinzugefügt wurden), gab es selten mehr als zwei oder drei manuelle Korrekturen, die für jedes Kreuzworträtsel durchgeführt werden mussten .
Dieser Algorithmus erstellt in 60 Sekunden 50 dichte Kreuzworträtsel mit 6 x 9 Pfeilen . Es verwendet eine Wortdatenbank (mit Wort + Tipps) und eine Board-Datenbank (mit vorkonfigurierten Boards).
1) Search for all starting cells (the ones with an arrow), store their size and directions
2) Loop through all starting cells
2.1) Search a word
2.1.1) Check if it was not already used
2.1.2) Check if it fits
2.2) Add the word to the board
3) Check if all cells were filled
Eine größere Wortdatenbank verkürzt die Generierungszeit erheblich und einige Boards sind schwerer zu füllen! Größere Bretter benötigen mehr Zeit, um richtig gefüllt zu werden!
Beispiel:
Vorkonfigurierte 6x9-Karte:
(# bedeutet eine Spitze in einer Zelle,% bedeutet zwei Spitzen in einer Zelle, Pfeile nicht gezeigt)
# - # # - % # - #
- - - - - - - - -
# - - - - - # - -
% - - # - # - - -
% - - - - - % - -
- - - - - - - - -
Generiertes 6x9 Board:
# C # # P % # O #
S A T E L L I T E
# N I N E S # T A
% A B # A # G A S
% D E N S E % W E
C A T H E D R A L
Tipps [Zeile, Spalte]:
[1,0] SATELLITE: Used for weather forecast
[5,0] CATHEDRAL: The principal church of a city
[0,1] CANADA: Country on USA's northern border
[0,4] PLEASE: A polite way to ask things
[0,7] OTTAWA: Canada's capital
[1,2] TIBET: Dalai Lama's region
[1,8] EASEL: A tripod used to put a painting
[2,1] NINES: Dressed up to (?)
[4,1] DENSE: Thick; impenetrable
[3,6] GAS: Type of fuel
[1,5] LS: Lori Singer, american actress
[2,7] TA: Teaching assistant (abbr.)
[3,1] AB: A blood type
[4,3] NH: New Hampshire (abbr.)
[4,5] ED: (?) Harris, american actor
[4,7] WE: The first person of plural (Grammar)
Obwohl dies eine ältere Frage ist, werde ich versuchen, eine Antwort zu finden, die auf ähnlichen Arbeiten basiert, die ich durchgeführt habe.
Es gibt viele Ansätze zur Lösung von Einschränkungsproblemen (die im Allgemeinen in der NPC-Komplexitätsklasse liegen).
Dies hängt mit der kombinatorischen Optimierung und der Einschränkungsprogrammierung zusammen. In diesem Fall sind die Einschränkungen die Geometrie des Gitters und die Anforderung, dass Wörter eindeutig sind usw.
Randomisierungs- / Annealing-Ansätze können ebenfalls funktionieren (wenn auch innerhalb der richtigen Einstellung).
Effiziente Einfachheit könnte die ultimative Weisheit sein!
Die Anforderungen waren für einen mehr oder weniger vollständigen Kreuzworträtsel-Compiler und einen (visuellen WYSIWYG-) Builder.
Abgesehen vom WYSIWYG-Builder-Teil lautete die Compiler-Gliederung wie folgt:
Laden Sie die verfügbaren Wortlisten (sortiert nach Wortlänge, dh 2,3, .., 20).
Suchen Sie die Wortschlitze (dh Gitterwörter) im vom Benutzer erstellten Gitter (z. B. Wort bei x, y mit der Länge L, horizontal oder vertikal) (Komplexität O (N)).
Berechnen Sie die Schnittpunkte der Gitterwörter (die gefüllt werden müssen) (Komplexität O (N ^ 2))
Berechnen Sie die Schnittpunkte der Wörter in den Wortlisten mit den verschiedenen Buchstaben des verwendeten Alphabets (dies ermöglicht die Suche nach übereinstimmenden Wörtern mithilfe einer Vorlage, z. B. einer von cwc verwendeten Sik-Cambon-These ) (Komplexität O (WL * AL)).
Mit den Schritten .3 und .4 können Sie diese Aufgabe ausführen:
ein. Die Schnittpunkte der Gitterwörter mit sich selbst ermöglichen es, eine "Vorlage" für den Versuch zu erstellen, Übereinstimmungen in der zugehörigen Wortliste der verfügbaren Wörter für dieses Gitterwort zu finden (indem die Buchstaben anderer sich überschneidender Wörter mit diesem Wort verwendet werden, die bereits zu einem bestimmten Zeitpunkt gefüllt sind Schritt des Algorithmus)
b. Die Schnittpunkte der Wörter in einer Wortliste mit dem Alphabet ermöglichen es, passende (Kandidaten-) Wörter zu finden, die einer bestimmten "Vorlage" entsprechen (z. B. "A" an erster Stelle und "B" an dritter Stelle usw.)
Mit diesen implementierten Datenstrukturen wurde folgender Algorithmus verwendet:
HINWEIS: Wenn das Raster und die Wortdatenbank konstant sind, können die vorherigen Schritte nur einmal ausgeführt werden.
Der erste Schritt des Algorithmus besteht darin, einen leeren Wortschlitz (Gitterwort) zufällig auszuwählen und ihn mit einem Kandidatenwort aus der zugehörigen Wortliste zu füllen (die Zufallsgenerierung ermöglicht es, bei aufeinanderfolgenden Ausführungen des Algorithmus unterschiedliche Lösungen zu erzeugen) (Komplexität O (1) oder O () N))
Berechnen Sie für jeden noch leeren Wortschlitz (der Schnittpunkte mit bereits gefüllten Wortschlitzen aufweist) ein Einschränkungsverhältnis (dies kann variieren, etw ist einfach die Anzahl der verfügbaren Lösungen in diesem Schritt) und sortieren Sie die leeren Wortschlitze nach diesem Verhältnis (Komplexität O (NlogN) ) oder O (N))
Durchlaufen Sie die leeren Wortfelder, die im vorherigen Schritt berechnet wurden, und versuchen Sie für jedes einzelne eine Reihe von Stornierungslösungen (stellen Sie sicher, dass die "Lichtbogenkonsistenz erhalten bleibt", dh das Raster hat nach diesem Schritt eine Lösung, wenn dieses Wort verwendet wird) und sortieren Sie sie nach maximale Verfügbarkeit für den nächsten Schritt (dh der nächste Schritt hat eine maximal mögliche Lösung, wenn dieses Wort zu diesem Zeitpunkt an diesem Ort verwendet wird usw.) (Komplexität O (N * MaxCandidatesUsed))
Füllen Sie dieses Wort aus (markieren Sie es als gefüllt und fahren Sie mit Schritt 2 fort).
Wenn kein Wort gefunden wird, das die Kriterien von Schritt .3 erfüllt, versuchen Sie, zu einer anderen Kandidatenlösung eines vorherigen Schritts zurückzukehren (Kriterien können hier variieren) (Komplexität O (N)).
Wenn ein Backtrack gefunden wurde, verwenden Sie die Alternative und setzen Sie optional bereits gefüllte Wörter zurück, die möglicherweise zurückgesetzt werden müssen (markieren Sie sie erneut als nicht gefüllt) (Komplexität O (N)).
Wenn kein Backtrack gefunden wird, kann keine Lösung gefunden werden (zumindest mit dieser Konfiguration, dem anfänglichen Startwert usw.)
Andernfalls haben Sie eine Lösung, wenn alle Wordlots gefüllt sind
Dieser Algorithmus führt einen zufälligen konsistenten Durchlauf des Lösungsbaums des Problems durch. Wenn es irgendwann eine Sackgasse gibt, wird ein vorheriger Knoten zurückverfolgt und eine andere Route verfolgt. Bis entweder eine gefundene Lösung oder die Anzahl der Kandidaten für die verschiedenen Knoten erschöpft ist.
Der Konsistenzteil stellt sicher, dass eine gefundene Lösung tatsächlich eine Lösung ist, und der zufällige Teil ermöglicht es, unterschiedliche Lösungen in unterschiedlichen Ausführungen zu erzeugen und im Durchschnitt auch eine bessere Leistung zu erzielen.
PS. All dies (und andere) wurden in reinem JavaScript (mit paralleler Verarbeitung und WYSIWYG) implementiert
PS2. Der Algorithmus kann leicht parallelisiert werden, um mehr als eine (unterschiedliche) Lösung gleichzeitig zu erzeugen
Hoffe das hilft
Warum nicht einfach einen zufälligen probabilistischen Ansatz verwenden? Beginnen Sie mit einem Wort und wählen Sie dann wiederholt ein zufälliges Wort aus und versuchen Sie, es in den aktuellen Status des Puzzles zu integrieren, ohne die Einschränkungen für die Größe usw. zu brechen. Wenn Sie versagen, beginnen Sie einfach von vorne.
Sie werden überrascht sein, wie oft ein solcher Monte-Carlo-Ansatz funktioniert.
Hier ist ein JavaScript-Code, der auf der Antwort von Nickf und dem Python-Code von Bryan basiert. Posten Sie es einfach für den Fall, dass jemand anderes es in js benötigt.
function board(cols, rows) { //instantiator object for making gameboards
this.cols = cols;
this.rows = rows;
var activeWordList = []; //keeps array of words actually placed in board
var acrossCount = 0;
var downCount = 0;
var grid = new Array(cols); //create 2 dimensional array for letter grid
for (var i = 0; i < rows; i++) {
grid[i] = new Array(rows);
}
for (var x = 0; x < cols; x++) {
for (var y = 0; y < rows; y++) {
grid[x][y] = {};
grid[x][y].targetChar = EMPTYCHAR; //target character, hidden
grid[x][y].indexDisplay = ''; //used to display index number of word start
grid[x][y].value = '-'; //actual current letter shown on board
}
}
function suggestCoords(word) { //search for potential cross placement locations
var c = '';
coordCount = [];
coordCount = 0;
for (i = 0; i < word.length; i++) { //cycle through each character of the word
for (x = 0; x < GRID_HEIGHT; x++) {
for (y = 0; y < GRID_WIDTH; y++) {
c = word[i];
if (grid[x][y].targetChar == c) { //check for letter match in cell
if (x - i + 1> 0 && x - i + word.length-1 < GRID_HEIGHT) { //would fit vertically?
coordList[coordCount] = {};
coordList[coordCount].x = x - i;
coordList[coordCount].y = y;
coordList[coordCount].score = 0;
coordList[coordCount].vertical = true;
coordCount++;
}
if (y - i + 1 > 0 && y - i + word.length-1 < GRID_WIDTH) { //would fit horizontally?
coordList[coordCount] = {};
coordList[coordCount].x = x;
coordList[coordCount].y = y - i;
coordList[coordCount].score = 0;
coordList[coordCount].vertical = false;
coordCount++;
}
}
}
}
}
}
function checkFitScore(word, x, y, vertical) {
var fitScore = 1; //default is 1, 2+ has crosses, 0 is invalid due to collision
if (vertical) { //vertical checking
for (i = 0; i < word.length; i++) {
if (i == 0 && x > 0) { //check for empty space preceeding first character of word if not on edge
if (grid[x - 1][y].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
} else if (i == word.length && x < GRID_HEIGHT) { //check for empty space after last character of word if not on edge
if (grid[x+i+1][y].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (x + i < GRID_HEIGHT) {
if (grid[x + i][y].targetChar == word[i]) { //letter match - aka cross point
fitScore += 1;
} else if (grid[x + i][y].targetChar != EMPTYCHAR) { //letter doesn't match and it isn't empty so there is a collision
fitScore = 0;
break;
} else { //verify that there aren't letters on either side of placement if it isn't a crosspoint
if (y < GRID_WIDTH - 1) { //check right side if it isn't on the edge
if (grid[x + i][y + 1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (y > 0) { //check left side if it isn't on the edge
if (grid[x + i][y - 1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
}
}
}
} else { //horizontal checking
for (i = 0; i < word.length; i++) {
if (i == 0 && y > 0) { //check for empty space preceeding first character of word if not on edge
if (grid[x][y-1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
} else if (i == word.length - 1 && y + i < GRID_WIDTH -1) { //check for empty space after last character of word if not on edge
if (grid[x][y + i + 1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (y + i < GRID_WIDTH) {
if (grid[x][y + i].targetChar == word[i]) { //letter match - aka cross point
fitScore += 1;
} else if (grid[x][y + i].targetChar != EMPTYCHAR) { //letter doesn't match and it isn't empty so there is a collision
fitScore = 0;
break;
} else { //verify that there aren't letters on either side of placement if it isn't a crosspoint
if (x < GRID_HEIGHT) { //check top side if it isn't on the edge
if (grid[x + 1][y + i].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (x > 0) { //check bottom side if it isn't on the edge
if (grid[x - 1][y + i].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
}
}
}
}
return fitScore;
}
function placeWord(word, clue, x, y, vertical) { //places a new active word on the board
var wordPlaced = false;
if (vertical) {
if (word.length + x < GRID_HEIGHT) {
for (i = 0; i < word.length; i++) {
grid[x + i][y].targetChar = word[i];
}
wordPlaced = true;
}
} else {
if (word.length + y < GRID_WIDTH) {
for (i = 0; i < word.length; i++) {
grid[x][y + i].targetChar = word[i];
}
wordPlaced = true;
}
}
if (wordPlaced) {
var currentIndex = activeWordList.length;
activeWordList[currentIndex] = {};
activeWordList[currentIndex].word = word;
activeWordList[currentIndex].clue = clue;
activeWordList[currentIndex].x = x;
activeWordList[currentIndex].y = y;
activeWordList[currentIndex].vertical = vertical;
if (activeWordList[currentIndex].vertical) {
downCount++;
activeWordList[currentIndex].number = downCount;
} else {
acrossCount++;
activeWordList[currentIndex].number = acrossCount;
}
}
}
function isActiveWord(word) {
if (activeWordList.length > 0) {
for (var w = 0; w < activeWordList.length; w++) {
if (word == activeWordList[w].word) {
//console.log(word + ' in activeWordList');
return true;
}
}
}
return false;
}
this.displayGrid = function displayGrid() {
var rowStr = "";
for (var x = 0; x < cols; x++) {
for (var y = 0; y < rows; y++) {
rowStr += "<td>" + grid[x][y].targetChar + "</td>";
}
$('#tempTable').append("<tr>" + rowStr + "</tr>");
rowStr = "";
}
console.log('across ' + acrossCount);
console.log('down ' + downCount);
}
//for each word in the source array we test where it can fit on the board and then test those locations for validity against other already placed words
this.generateBoard = function generateBoard(seed = 0) {
var bestScoreIndex = 0;
var top = 0;
var fitScore = 0;
var startTime;
//manually place the longest word horizontally at 0,0, try others if the generated board is too weak
placeWord(wordArray[seed].word, wordArray[seed].displayWord, wordArray[seed].clue, 0, 0, false);
//attempt to fill the rest of the board
for (var iy = 0; iy < FIT_ATTEMPTS; iy++) { //usually 2 times is enough for max fill potential
for (var ix = 1; ix < wordArray.length; ix++) {
if (!isActiveWord(wordArray[ix].word)) { //only add if not already in the active word list
topScore = 0;
bestScoreIndex = 0;
suggestCoords(wordArray[ix].word); //fills coordList and coordCount
coordList = shuffleArray(coordList); //adds some randomization
if (coordList[0]) {
for (c = 0; c < coordList.length; c++) { //get the best fit score from the list of possible valid coordinates
fitScore = checkFitScore(wordArray[ix].word, coordList[c].x, coordList[c].y, coordList[c].vertical);
if (fitScore > topScore) {
topScore = fitScore;
bestScoreIndex = c;
}
}
}
if (topScore > 1) { //only place a word if it has a fitscore of 2 or higher
placeWord(wordArray[ix].word, wordArray[ix].clue, coordList[bestScoreIndex].x, coordList[bestScoreIndex].y, coordList[bestScoreIndex].vertical);
}
}
}
}
if(activeWordList.length < wordArray.length/2) { //regenerate board if if less than half the words were placed
seed++;
generateBoard(seed);
}
}
}
function seedBoard() {
gameboard = new board(GRID_WIDTH, GRID_HEIGHT);
gameboard.generateBoard();
gameboard.displayGrid();
}
Ich würde zwei Zahlen generieren: Length und Scrabble Score. Angenommen, eine niedrige Scrabble-Punktzahl bedeutet, dass das Beitreten einfacher ist (niedrige Punktzahl = viele gebräuchliche Buchstaben). Sortieren Sie die Liste nach absteigender Länge und aufsteigender Scrabble-Punktzahl.
Als nächstes gehen Sie einfach die Liste durch. Wenn sich das Wort nicht mit einem vorhandenen Wort kreuzt (überprüfen Sie jedes Wort anhand seiner Länge bzw. Scrabble-Punktzahl), stellen Sie es in die Warteschlange und überprüfen Sie das nächste Wort.
Spülen und wiederholen, und dies sollte ein Kreuzworträtsel erzeugen.
Natürlich bin ich mir ziemlich sicher, dass dies O (n!) Ist und es nicht garantiert ist, dass das Kreuzworträtsel für Sie vervollständigt wird, aber vielleicht kann es jemand verbessern.
Ich habe über dieses Problem nachgedacht. Meiner Meinung nach können Sie nicht hoffen, dass Ihre begrenzte Wortliste ausreicht, um ein wirklich dichtes Kreuzworträtsel zu erstellen. Daher möchten Sie möglicherweise ein Wörterbuch in eine "Trie" -Datenstruktur einfügen. Auf diese Weise können Sie leicht Wörter finden, die die verbleibenden Leerzeichen ausfüllen. In einem Versuch ist es ziemlich effizient, eine Durchquerung zu implementieren, die Ihnen beispielsweise alle Wörter der Form "c? T" gibt.
Mein allgemeiner Gedanke lautet also: Erstellen Sie einen relativ Brute-Force-Ansatz, wie hier beschrieben, um ein Kreuz mit niedriger Dichte zu erstellen, und füllen Sie die Lücken mit Wörterbuchwörtern.
Wenn jemand diesen Ansatz gewählt hat, lassen Sie es mich bitte wissen.
Ich habe mit der Kreuzworträtsel-Generator-Engine herumgespielt und fand dies am wichtigsten:
0.!/usr/bin/python
ein. allwords.sort(key=len, reverse=True)
b. Erstellen Sie ein Element / Objekt wie einen Cursor, der zur einfachen Orientierung um die Matrix herumgeht, es sei denn, Sie möchten später durch zufällige Auswahl iterieren.
das erste, nimm das erste Paar und platziere es quer und runter von 0,0; Speichern Sie den ersten als unseren aktuellen Kreuzworträtsel-Anführer.
Bewegen Sie den Cursor in diagonaler oder zufälliger Reihenfolge mit größerer diagonaler Wahrscheinlichkeit zur nächsten leeren Zelle
Durchlaufen Sie die Wörter wie und verwenden Sie die Länge des freien Speicherplatzes, um die maximale Wortlänge zu definieren:
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 )
Um das Wort mit dem freien Speicherplatz zu vergleichen, habe ich Folgendes verwendet:
w_space=['c','.','a','.','.','.'] # whereas dots are blank cells
# CONVERT MULTIPLE '.' INTO '.*' FOR REGEX
pattern = r''.join( [ x.letter for x in w_space ] )
pattern = pattern.strip('.') +'.*' if pattern[-1] == '.' else pattern
prog = re.compile( pattern, re.U | re.I )
if prog.match( w ) :
#
if prog.match( w ).group() == w :
#
return True
Ändern Sie nach jedem erfolgreich verwendeten Wort die Richtung. Schleife, während alle Zellen gefüllt sind ODER Ihnen die Wörter ausgehen ODER durch Iterationsbegrenzung, dann:
# CHANGE ALL WORDS LIST
inexOf1stWord = allwords.index( leading_w )
allwords = allwords[:inexOf1stWord+1][:] + allwords[inexOf1stWord+1:][:]
... und wieder neues Kreuzworträtsel wiederholen.
Machen Sie das Bewertungssystem durch einfaches Füllen und einige Schätzungsberechnungen. Geben Sie die Punktzahl für das aktuelle Kreuzworträtsel an und schränken Sie die spätere Auswahl ein, indem Sie es in die Liste der erstellten Kreuzworträtsel einfügen, wenn die Punktzahl von Ihrem Punktesystem erfüllt wird.
Nach der ersten Iterationssitzung iterieren Sie erneut aus der Liste der erstellten Kreuzworträtsel, um den Job zu beenden.
Durch die Verwendung von mehr Parametern kann die Geschwindigkeit um einen großen Faktor verbessert werden.
Ich würde einen Index jedes Buchstabens erhalten, der von jedem Wort verwendet wird, um mögliche Kreuze zu kennen. Dann würde ich das größte Wort wählen und es als Basis verwenden. Wählen Sie den nächsten großen aus und kreuzen Sie ihn. Spülen und wiederholen. Es ist wahrscheinlich ein NP-Problem.
Eine andere Idee ist die Erstellung eines genetischen Algorithmus, bei dem die Stärke-Metrik angibt, wie viele Wörter Sie in das Raster einfügen können.
Der schwierige Teil, den ich finde, ist, wenn man weiß, dass eine bestimmte Liste unmöglich gekreuzt werden kann.
Ich habe eine JavaScript / jQuery-Lösung für dieses Problem codiert:
Beispieldemo: http://www.earthfluent.com/crossword-puzzle-demo.html
Quellcode: https://github.com/HoldOffHunger/jquery-crossword-puzzle-generator
Die Absicht des von mir verwendeten Algorithmus:
Ich werde den Algorithmus beschreiben, den ich verwendet habe:
Gruppieren Sie die Wörter nach denen, die einen gemeinsamen Buchstaben haben.
Erstellen Sie aus diesen Gruppen Sätze einer neuen Datenstruktur ("Wortblöcke"), bei der es sich um ein primäres Wort (das alle anderen Wörter durchläuft) und dann um die anderen Wörter (die das primäre Wort durchlaufen) handelt.
Beginnen Sie das Kreuzworträtsel mit dem allerersten dieser Wortblöcke ganz oben links im Kreuzworträtsel.
Bewegen Sie sich für den Rest der Wortblöcke ausgehend von der Position ganz rechts unten des Kreuzworträtsels nach oben und links, bis keine freien Plätze mehr verfügbar sind. Wenn mehr leere Spalten nach oben als nach links vorhanden sind, bewegen Sie sich nach oben und umgekehrt.
var crosswords = generateCrosswordBlockSources(puzzlewords);
. Protokollieren Sie diesen Wert einfach in der Konsole. Vergiss nicht, es gibt einen "Cheat-Modus" im Spiel, in dem du einfach auf "Antwort anzeigen" klicken kannst, um den Wert sofort zu erhalten.
Dieser erscheint als Projekt im AI CS50-Kurs von Harvard. Die Idee ist, das Kreuzworträtsel-Generierungsproblem als ein Problem der Einschränkungszufriedenheit zu formulieren und es durch Zurückverfolgen mit verschiedenen Heuristiken zu lösen, um den Suchraum zu reduzieren.
Zunächst benötigen wir einige Eingabedateien:
`
###_####_#
____####_#
_##_#_____
_##_#_##_#
______####
#_###_####
#_##______
#_###_##_#
_____###_#
#_######_#
##_______#
`
Ein Eingabevokabular (Wortliste / Wörterbuch), aus dem die Kandidatenwörter ausgewählt werden (wie das folgende gezeigt).
a
abandon
ability
able
abortion
about
above
abroad
absence
absolute
absolutely
...
Nun ist der CSP wie folgt definiert und zu lösen:
Das Folgende zeigt die Ausgabe, die unter Verwendung einer Implementierung des CSP-Lösungsalgorithmus erhalten wurde:
`
███S████D█
MUCH████E█
E██A█AGENT
S██R█N██Y█
SUPPLY████
█N███O████
█I██INSIDE
█Q███E██A█
SUGAR███N█
█E██████C█
██OFFENSE█
`
Die folgende Animation zeigt die Schritte zum Zurückverfolgen:
Hier ist eine andere mit einer Wortliste in Bangla (Bengali):