web-dev-qa-db-fra.com

Algorithme pour générer un mot croisé

Avec une liste de mots, comment voudriez-vous les organiser dans une grille de mots croisés?

Cela ne devrait pas être comme un "bon" jeu de mots croisés qui soit symétrique ou quelque chose du genre: en gros, il suffit de donner une position de départ et une direction pour chaque mot.

Y aurait-il des exemples Java disponibles?

110
nickf

J'ai proposé une solution qui n'est probablement pas la plus efficace, mais elle fonctionne assez bien. Fondamentalement:

  1. Trier tous les mots par longueur, par ordre décroissant.
  2. Prenez le premier mot et placez-le au tableau.
  3. Prenez le prochain mot.
  4. Effectuez une recherche parmi tous les mots déjà inscrits au tableau et voyez s’il existe des intersections possibles (lettres communes) avec ce mot.
  5. S'il existe un emplacement possible pour ce mot, parcourez tous les mots du tableau et vérifiez si le nouveau mot interfère.
  6. Si cette Parole ne casse pas le tableau, placez-la là et passez à l'étape 3, sinon, continuez à chercher un lieu (étape 4).
  7. Continuez cette boucle jusqu'à ce que tous les mots soient placés ou incapables d'être placés.

Cela fait des mots croisés de travail, mais souvent assez pauvres. J'ai apporté plusieurs modifications à la recette de base ci-dessus pour obtenir un meilleur résultat.

  • À la fin de la génération de mots croisés, attribuez à celui-ci un score basé sur le nombre de mots placés (le plus grand est le mieux), sur la taille du tableau (le plus petit possible) et sur le rapport hauteur/largeur (le plus proche). à 1 le mieux). Générez un certain nombre de mots croisés, puis comparez leurs scores et choisissez le meilleur.
    • Au lieu d'exécuter un nombre arbitraire d'itérations, j'ai décidé de créer autant de mots croisés que possible dans un laps de temps arbitraire. Si vous avez seulement une petite liste de mots, vous obtiendrez des dizaines de mots croisés possibles en 5 secondes. Un mot croisé plus grand peut seulement être choisi parmi 5-6 possibilités.
  • Lorsque vous placez un nouveau mot, au lieu de le placer immédiatement après avoir trouvé un emplacement acceptable, attribuez à celui-ci un score en fonction de son augmentation de la taille de la grille et du nombre d'intersections (idéalement, vous voudriez que chaque mot soit traversé par 2-3 autres mots). Gardez une trace de toutes les positions et de leurs scores, puis choisissez le meilleur.
52
nickf

J'ai récemment écrit le mien en Python. Vous pouvez le trouver ici: http://bryanhelmig.com/python-crossword-puzzle-generator/ . Il ne crée pas les mots croisés denses de style NYT, mais le style de mots croisés que vous pourriez trouver dans le livre de puzzle d'un enfant.

Contrairement à quelques algorithmes que j'ai trouvés là-bas qui implémentaient une méthode aléatoire de placement de mots comme certains l'ont suggéré, j'ai essayé d'implémenter une approche légèrement plus intelligente de force brute lors du placement de Word. Voici mon processus:

  1. Créez une grille de n'importe quelle taille et une liste de mots.
  2. Mélangez la liste de mots, puis triez les mots par ordre le plus long au plus court.
  3. Placez le premier et le plus long mot en haut à gauche, 1,1 (vertical ou horizontal).
  4. Déplacez-vous sur le prochain mot, passez en boucle sur chaque lettre du mot et chaque cellule de la grille à la recherche de correspondances entre lettres.
  5. Lorsqu'une correspondance est trouvée, ajoutez simplement cette position à une liste de coordonnées suggérée pour ce mot.
  6. Parcourez la liste de coordonnées suggérée et "notez" le placement de Word en fonction du nombre de mots croisés. Des scores de 0 indiquent soit un mauvais placement (adjacent aux mots existants), soit l'absence de croix de Word.
  7. Retour à l'étape 4 jusqu'à ce que la liste de mots soit épuisée. Deuxième passage facultatif.
  8. Nous devrions maintenant avoir des mots croisés, mais la qualité peut être aléatoire, en raison de certains placements aléatoires. Donc, nous tamponnons ces mots croisés et revenons à l’étape 2. Si le mot de passe suivant contient plus de mots sur le tableau, il remplace le mot de passe dans la mémoire tampon. C'est limité dans le temps (trouvez le meilleur mot croisé en x secondes).

À la fin, vous avez des mots croisés ou des mots cachés, car ils sont à peu près les mêmes. Cela fonctionne plutôt bien, mais laissez-moi savoir si vous avez des suggestions d'amélioration. Les plus grandes grilles fonctionnent de manière exponentielle plus lente; les plus grandes listes de mots linéairement. De plus grandes listes de mots ont également beaucoup plus de chances d'obtenir de meilleurs numéros de placement de mots.

18
Bryan

En fait, j’ai écrit un programme de génération de mots croisés il ya environ dix ans (c’était cryptique, mais les mêmes règles s’appliqueraient aux mots croisés normaux).

Il contenait une liste de mots (et d’indices associés) stockés dans un fichier, triés par ordre décroissant d’utilisation jusqu’à présent (de sorte que les mots moins utilisés se trouvaient en haut du fichier). Un modèle, essentiellement un masque de bits représentant les carrés noirs et libres, a été choisi au hasard dans un pool fourni par le client.

Ensuite, pour chaque mot non complet du puzzle (trouvez le premier carré vide et voyez si celui qui se trouve à droite (mot croisé) ou celui du dessous (mot caché) est également vide), une recherche a été effectuée: le fichier à la recherche du premier mot qui convient, en tenant compte des lettres déjà présentes dans ce mot. S'il n'y avait pas de mot qui puisse tenir, vous venez de marquer le mot comme incomplet et de passer à autre chose.

À la fin, il y aurait quelques mots incomplets que le compilateur devrait remplir (et ajouter le mot et un indice au fichier si vous le souhaitez). S'ils ne pouvaient pas trouver d'idées aucune, ils pourraient modifier le mot croisé manuellement pour modifier les contraintes ou simplement demander une régénération totale.

Une fois que le fichier Word/indice a atteint une certaine taille (et qu'il ajoutait 50 à 100 indices par jour pour ce client), il était rarement nécessaire de corriger plus de deux ou trois corrections manuelles pour chaque mot croisé. .

17
paxdiablo

Cet algorithme crée 50 denses 6x9 mots croisés sur les flèches en 60 secondes. Il utilise une base de données Word (avec Word + astuces) et une base de données de cartes (avec des cartes préconfigurées).

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

Une plus grande base de données Word réduit considérablement le temps de génération et certains types de tableaux sont plus difficiles à remplir! Les plus gros tableaux demandent plus de temps pour être remplis correctement!


Exemple:

Carte 6x9 pré-configurée:

(# signifie un bout dans une cellule,% signifie deux bouts dans une cellule, flèches non représentées)

# - # # - % # - # 
- - - - - - - - - 
# - - - - - # - - 
% - - # - # - - - 
% - - - - - % - - 
- - - - - - - - - 

Tableau 6x9 généré:

# 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 

Astuces [ligne, colonne]:

[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)
14
thiagolr

Pourquoi ne pas simplement utiliser une approche probabiliste aléatoire pour commencer. Commencez avec un mot, puis choisissez de manière répétée un mot aléatoire et essayez de l'adapter à l'état actuel du puzzle sans rompre les contraintes de taille, etc. Si vous échouez, recommencez à nouveau.

Vous serez surpris de la fréquence à laquelle une approche de Monte Carlo comme celle-ci fonctionne.

9
Yogi

Bien qu’il s’agisse d’une question plus ancienne, je tenterai de répondre à un travail similaire que j’ai effectué.

Il existe de nombreuses approches pour résoudre les problèmes de contraintes (qui sont généralement dans NPC)).

Ceci est lié à l'optimisation combinatoire et à la programmation par contraintes. Dans ce cas, les contraintes sont la géométrie de la grille et l'exigence que les mots soient uniques, etc.

Les approches de randomisation/recuit peuvent également fonctionner (bien que dans le cadre approprié).

Une simplicité efficace pourrait bien être la sagesse ultime!

Les exigences concernaient un compilateur de mots croisés plus ou moins complet et un constructeur (visuel WYSIWYG).

Laissant de côté la partie constructeur WYSIWYG, le contour du compilateur était le suivant:

  1. Charger les listes de mots disponibles (triés par longueur de mot, soit 2,3, .., 20)

  2. Recherchez les emplacements de mots (c.-à-d. Mots de grille) sur la grille construite par l'utilisateur (par exemple, mot à x, y de longueur L, horizontal ou vertical) (complexité O(N))

  3. Calcule les points d'intersection des mots de la grille (à renseigner) (complexité O (N ^ 2))

  4. Calculez les intersections des mots dans les listes de mots avec les différentes lettres de l'alphabet utilisé (cela permet de rechercher des mots correspondants à l'aide d'un modèle, par exemple. thèse de Sik Cambon telle qu'utilisée par cwc ) (complexité O ( WL * AL))

Les étapes .3 et .4 permettent d’effectuer cette tâche:

une. Les intersections des mots de la grille avec eux-mêmes permettent de créer un "modèle" pour essayer de trouver des correspondances dans la liste de mots associée pour les mots disponibles pour ce mot de grille (en utilisant les lettres des autres mots qui se croisent avec ce mot qui sont déjà remplies à un certain moment). pas de l'algorithme)

b. Les intersections des mots d'une liste de mots avec l'alphabet permettent de trouver des mots correspondants (candidats) correspondant à un "modèle" donné (par exemple, "A" à la 1ère place et "B" à la 3ème place, etc.).

Donc, avec ces structures de données implémentées, l'algorithme utilisé était qc comme ceci:

REMARQUE: si la grille et la base de données de mots sont constantes, les étapes précédentes ne peuvent être effectuées qu'une fois.

  1. La première étape de l’algorithme consiste à sélectionner au hasard un mot vide (grille de mots) vide et à le remplir avec un mot candidat de la liste de mots correspondante (la randomisation permet de produire différentes solutions lors de l’exécution consécutive de l’algorithme) (complexité O(1) ou O(N))

  2. Pour chaque emplacement de mot encore vide (ayant des intersections avec des mots déjà remplis), calculez un rapport de contrainte (cela peut varier, sth est simple le nombre de solutions disponibles à ce stade) et triez les mots vides par ce rapport (complexité O(NlogN) ou O(N)))

  3. Parcourez les mots vides calculés à l'étape précédente et essayez pour chacun d'entre eux un certain nombre de solutions d'annulation (en vous assurant que la "cohérence d'arc est conservée", c'est-à-dire que la grille a une solution après cette étape si ce mot est utilisé) et triez-les en fonction de disponibilité maximale pour l'étape suivante (c.-à-d. que l'étape suivante comporte un nombre maximal de solutions possibles si ce mot est utilisé à ce moment-là, etc.) (complexité O (N * MaxCandidatesUsed))

  4. Remplissez cette parole (marquez-la comme remplie et passez à l'étape 2)

  5. Si aucun mot trouvé ne satisfait aux critères de l'étape .3, essayez de revenir à une autre solution candidate d'une étape précédente (les critères peuvent varier ici) (complexité O(N))).

  6. Si vous trouvez un retour arrière, utilisez l’alternative et réinitialisez éventuellement les mots déjà renseignés qui pourraient nécessiter une réinitialisation (marquez-les comme remplis à nouveau) (complexité O(N))

  7. Si aucun retour en arrière n'a été trouvé, aucune solution ne peut être trouvée (au moins avec cette configuration, graine initiale, etc.)

  8. Sinon, lorsque tous les mots sont remplis, vous avez une solution.

Cet algorithme effectue une marche cohérente aléatoire de l'arborescence de solutions du problème. Si à un moment donné il y a une impasse, il retourne à un nœud précédent et suit un autre chemin. Jusqu'à ce qu'une solution soit trouvée ou que le nombre de candidats pour les différents nœuds ne soit épuisé.

La partie cohérence s'assure qu'une solution trouvée est bien une solution et la partie aléatoire permet de produire différentes solutions dans différentes exécutions et, en moyenne, d'obtenir de meilleures performances.

PS tout cela (et d'autres) ont été implémentés en JavaScript pur (avec traitement parallèle et WYSIWYG)

PS2. L'algorithme peut être facilement mis en parallèle afin de produire plus d'une solution (différente) à la fois.

J'espère que cela t'aides

9
Nikos M.

Voici du code javascript basé sur la réponse de nickf et le code de Bryan python. Publiez-le simplement au cas où quelqu'un d'autre en aurait besoin dans js.

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();
}
6
FascistDonut

Je générerais deux nombres: Longueur et Scrabble. Supposons qu'un faible score au Scrabble signifie qu'il est plus facile de rejoindre le groupe (scores faibles = beaucoup de lettres communes). Triez la liste par longueur en ordre décroissant et le Scrabble en ordre croissant.

Ensuite, il suffit de descendre la liste. Si le mot ne se croise pas avec un mot existant (vérifiez chaque mot par sa longueur et son score au Scrabble, respectivement), mettez-le dans la file d'attente et vérifiez le mot suivant.

Rincer et répéter, et cela devrait générer un mot croisé.

Bien sûr, je suis à peu près sûr que c'est O (n!) Et que les mots croisés ne sont pas garantis pour vous, mais peut-être que quelqu'un pourra les améliorer.

5
Eric

J'ai réfléchi à ce problème. Mon sentiment est que pour créer des mots croisés vraiment denses, vous ne pouvez pas espérer que votre liste de mots limitée suffira. Par conséquent, vous voudrez peut-être prendre un dictionnaire et le placer dans une structure de données "trie". Cela vous permettra de trouver facilement des mots qui remplissent les espaces laissés. Dans un trie, il est assez efficace de mettre en œuvre une traversée qui, par exemple, vous donne tous les mots de la forme "c? T".

Donc, ma pensée générale est la suivante: créez une sorte d’approche de force relativement brute, comme certains l’ont décrit ici, pour créer une croix de faible densité, et complétez les blancs avec les mots du dictionnaire.

Si quelqu'un d'autre a adopté cette approche, s'il vous plaît faites le moi savoir.

3
Jake

Je jouais avec le moteur du générateur de mots croisés, et j'ai trouvé cela le plus important:

0. !/usr/bin/python

  1. une. allwords.sort(key=len, reverse=True)

    b. comme le curseur, créez un élément/objet qui contournera la matrice pour faciliter l’orientation, à moins que vous ne vouliez itérer par choix aléatoire plus tard.

  2. le premier, prenez la première paire et placez-les dans la colonne 0,0; stockez le premier en tant que notre mot de passe actuel 'leader'.

  3. déplace le curseur en diagonale ou aléatoire avec une probabilité diagonale plus grande vers la cellule vide suivante

  4. itérer sur les mots comme et utiliser la longueur de l'espace libre pour définir la longueur maximale du mot: 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 )

  5. pour comparer Word à l’espace libre que j’ai utilisé, à savoir:

    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
    
  6. après chaque utilisation réussie de Word, changez de direction. Boucle pendant que toutes les cellules sont remplies OR vous manquez de mots OR par limite d'itérations, puis:

# CHANGE ALL WORDS LIST inexOf1stWord = allwords.index( leading_w ) allwords = allwords[:inexOf1stWord+1][:] + allwords[inexOf1stWord+1:][:]

... et réitérer de nouveaux mots croisés.

  1. Faites le système de notation par la facilité de remplissage, et quelques calculs d'estimation. Donnez le score pour les mots croisés actuels et restreignez le choix ultérieur en l'ajoutant à la liste des mots croisés créés si le score est satisfait par votre système de notation.

  2. Après la première session d’itération, effectuez une nouvelle itération dans la liste des mots croisés créés pour terminer le travail.

En utilisant plus de paramètres, la vitesse peut être considérablement améliorée.

3
Alex

Je voudrais obtenir un index de chaque lettre utilisée par chaque mot pour connaître les croix possibles. Ensuite, je choisirais le plus grand mot et l'utiliserais comme base. Sélectionnez le prochain grand et traversez-le. Rincer et répéter. C'est probablement un problème NP.

Une autre idée est de créer un algorithme génétique où la métrique de force est le nombre de mots que vous pouvez mettre dans la grille.

La partie difficile que je trouve est quand il est impossible de traverser une certaine liste.

2
eipipuz

J'ai codé une solution 100% jQuery à ce problème.

Exemple de démonstration: http://www.earthfluent.com/crossword-puzzle-demo.html

Code source: https://github.com/HoldOffHunger/jquery-crossword-puzzle-generator

L'intention de l'algorithme que j'ai utilisé:

  1. Minimisez autant que possible le nombre de carrés inutilisables dans la grille.
  2. Ayez autant de mots mélangés que possible.
  3. Calculer dans un temps extrêmement rapide.

Je vais décrire l'algorithme que j'ai utilisé:

  1. Regroupez les mots en fonction de ceux qui partagent une lettre commune.

  2. À partir de ces groupes, créez des ensembles d'une nouvelle structure de données ("Blocs de mots"), qui est un mot principal (qui traverse tous les autres mots), puis les autres mots (qui traversent le mot principal).

  3. Commencez les mots croisés avec le tout premier de ces blocs Word dans la position tout à fait à gauche du mots croisés.

  4. Pour le reste des blocs Word, en partant de la position la plus à droite du mot croisé, déplacez-vous vers le haut et vers la gauche, jusqu'à ce qu'il n'y ait plus de places disponibles à remplir. S'il y a plus de colonnes vides vers le haut que vers la gauche, déplacez-vous vers le haut et inversement.

1
HoldOffHunger