Tableau Boggle avec meilleur score

16

Je voulais voir les réponses à cette question (aujourd'hui disparue) , mais elle n'a jamais été corrigée / améliorée.

Étant donné un ensemble de dés Boggle à 6 faces (configuration volée à cette question ), déterminez en deux minutes de temps de traitement quelle configuration de carte permettra le score le plus élevé possible. (c.-à-d. quels dés dans quel endroit avec quel côté vers le haut permet le plus grand pool de mots de score?)


OBJECTIF

  • Votre code ne devrait pas durer plus de 2 minutes (120 secondes). À ce moment, il devrait s'arrêter automatiquement et imprimer les résultats.

  • Le score final du défi sera le score Boggle moyen de 5 exécutions du programme.

    • En cas d'égalité, le gagnant sera celui qui aura trouvé le plus de mots.
    • S'il y a toujours égalité, le gagnant sera celui qui aura trouvé le plus de mots longs (8+) .

RÈGLES / CONTRAINTES

  • Il s'agit d'un défi de code; la longueur du code n'est pas pertinente.

  • Veuillez vous référer à ce lien pour une liste de mots (utilisez la ISPELL "english.0"liste - il manque des mots assez communs à la liste SCOWL).

    • Cette liste peut être référencée / importée / lue dans votre code comme vous le souhaitez.
    • Seuls les mots correspondant à l'expression régulière ^([a-pr-z]|qu){3,16}$seront comptés. (Seules les lettres minuscules, 3-16 caractères, qu doivent être utilisées comme une unité.)
  • Les mots sont formés en reliant des lettres adjacentes (horizontales, verticales et diagonales) pour épeler les mots dans le bon ordre, sans utiliser un seul dé plus d'une fois dans un seul mot.

    • Les mots doivent contenir 3 lettres ou plus; des mots plus courts ne rapporteront aucun point.
    • Les lettres en double sont acceptables, mais pas les dés.
    • Les mots qui s'étendent sur les bords / se croisent d'un côté de la planche à l'autre ne sont pas autorisés.
  • Le score final Boggle ( pas de défi ) est le total des valeurs de points pour tous les mots trouvés.

    • La valeur en points attribuée à chaque mot est basée sur la longueur du mot. (voir ci-dessous)
    • Les règles Boggle normales déduiraient / remettraient les mots trouvés par un autre joueur. Supposons ici qu'aucun autre joueur n'est impliqué et que tous les mots trouvés comptent pour le score total.
    • Cependant, les mots trouvés plus d'une fois dans la même grille ne doivent être comptés qu'une seule fois.
  • Votre fonction / programme doit TROUVER l'arrangement optimal; simplement coder en dur une liste prédéterminée ne fera pas l'affaire.

  • Votre sortie doit être une grille 4x4 de votre plateau de jeu idéal, une liste de tous les mots trouvés pour ce plateau et le score Boggle correspondant à ces mots.


CONFIGURATION DE LA MATRICE

A  A  E  E  G  N
E  L  R  T  T  Y
A  O  O  T  T  W
A  B  B  J  O  O
E  H  R  T  V  W
C  I  M  O  T  U
D  I  S  T  T  Y
E  I  O  S  S  T
D  E  L  R  V  Y
A  C  H  O  P  S
H  I  M  N  Qu U
E  E  I  N  S  U
E  E  G  H  N  W
A  F  F  K  P  S
H  L  N  N  R  Z
D  E  I  L  R  X

TABLEAU DE NOTATION STANDARD DE BOGGLE

Word length => Points
<= 2 - 0 pts
   3 - 1  
   4 - 1  
   5 - 2  
   6 - 3  
   7 - 5
>= 8 - 11 pts
*Words using the "Qu" die will count the full 2 letters for their word, not just the 1 die.

EXEMPLE DE SORTIE

A  L  O  J  
V  U  T  S  
L  C  H  E  
G  K  R  X

CUT
THE
LUCK
HEX
....

140 points

Si des éclaircissements supplémentaires sont nécessaires, veuillez demander!

Gaffi
la source
2
Je préférerais avoir un dictionnaire pour normaliser l'objectif. Notez également que ce n'est pas une nouvelle idée, comme une simple recherche Google le révélera :) Le score le plus élevé que j'ai vu est 4527( 1414nombre total de mots), trouvé ici: ai.stanford.edu/~chuongdo/boggle/index.html
mellamokb
4
Le programme est-il nécessaire pour mettre fin à ce siècle?
Peter Taylor
1
@GlitchMr En anglais, Q n'est généralement utilisé qu'avec U. Boggle explique cela en plaçant les deux lettres sur le même dé comme une seule unité.
Gaffi
1
La spécification de la liste de mots n'est pas claire. Comptez-vous uniquement les mots répertoriés en anglais.0 en minuscules? (Les règles de jeu de mots standard excluent les abréviations / initialismes et les noms propres).
Peter Taylor
1
Je pensais à regex ^([a-pr-z]|qu){3,16}$(qui exclurait à tort les mots de 3 lettres avec qu, mais il n'y en a pas).
Peter Taylor

Réponses:

9

C, en moyenne 500+ 1500 1750 points

Il s'agit d'une amélioration relativement mineure par rapport à la version 2 (voir ci-dessous pour les notes sur les versions précédentes). Il y a deux parties. Premièrement: au lieu de sélectionner des tableaux au hasard dans le pool, le programme itère maintenant sur chaque tableau du pool, en les utilisant à tour de rôle avant de retourner en haut du pool et de répéter. (Étant donné que le pool est en cours de modification pendant cette itération, il y aura toujours des cartes qui seront choisies deux fois de suite, ou pire, mais ce n'est pas un problème grave.) Le deuxième changement est que le programme suit maintenant lorsque le pool change et si le programme dure trop longtemps sans améliorer le contenu du pool, il détermine que la recherche est "bloquée", vide le pool et recommence avec une nouvelle recherche. Il continue de le faire jusqu'à ce que les deux minutes soient écoulées.

J'avais initialement pensé que j'utiliserais une sorte de recherche heuristique pour dépasser la plage des 1500 points. Le commentaire de @ mellamokb à propos d'un tableau de 4527 points m'a amené à supposer qu'il y avait beaucoup de place pour l'amélioration. Cependant, nous utilisons une liste de mots relativement petite. Le tableau de 4527 points marquait en utilisant YAWL, qui est la liste de mots la plus inclusive - elle est encore plus grande que la liste de mots officielle du Scrabble américain. Dans cet esprit, j'ai réexaminé les planches que mon programme avait trouvées et j'ai remarqué qu'il semblait y avoir un ensemble limité de planches au-dessus de 1700 environ. Ainsi, par exemple, j'ai eu plusieurs pistes qui avaient découvert une planche marquant 1726, mais c'était toujours exactement la même planche que celle trouvée (en ignorant les rotations et les réflexions).

Comme autre test, j'ai exécuté mon programme en utilisant YAWL comme dictionnaire, et il a trouvé la carte à 4527 points après environ une douzaine d'exécutions. Compte tenu de cela, je fais l'hypothèse que mon programme est déjà à la limite supérieure de l'espace de recherche, et donc la réécriture que je prévoyais introduirait une complexité supplémentaire pour très peu de gain.

Voici ma liste des cinq tableaux les mieux notés que mon programme a trouvés en utilisant la liste de english.0mots:

1735 :  D C L P  E I A E  R N T R  S E G S
1738 :  B E L S  R A D G  T I N E  S E R S
1747 :  D C L P  E I A E  N T R D  G S E R
1766 :  M P L S  S A I E  N T R N  D E S G
1772:   G R E P  T N A L  E S I T  D R E S

Ma conviction est que le 1772 "grep board" (comme je l'ai pris pour l'appeler), avec 531 mots, est le tableau le plus performant possible avec cette liste de mots. Plus de 50% des parcours de deux minutes de mon programme se terminent avec ce forum. J'ai également laissé mon programme fonctionner pendant la nuit sans qu'il ne trouve rien de mieux. Donc, s'il existe un tableau avec un score plus élevé, il devrait probablement avoir un aspect qui vainc la technique de recherche du programme. Un tableau dans lequel chaque petite modification possible de la mise en page provoque une énorme baisse du score total, par exemple, pourrait ne jamais être découvert par mon programme. Mon intuition est qu'il est très peu probable qu'un tel conseil existe.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#define WORDLISTFILE "./english.0"

#define XSIZE 4
#define YSIZE 4
#define BOARDSIZE (XSIZE * YSIZE)
#define DIEFACES 6
#define WORDBUFSIZE 256
#define MAXPOOLSIZE 32
#define STALLPOINT 64
#define RUNTIME 120

/* Generate a random int from 0 to N-1.
 */
#define random(N)  ((int)(((double)(N) * rand()) / (RAND_MAX + 1.0)))

static char const dice[BOARDSIZE][DIEFACES] = {
    "aaeegn", "elrtty", "aoottw", "abbjoo",
    "ehrtvw", "cimotu", "distty", "eiosst",
    "delrvy", "achops", "himnqu", "eeinsu",
    "eeghnw", "affkps", "hlnnrz", "deilrx"
};

/* The dictionary is represented in memory as a tree. The tree is
 * represented by its arcs; the nodes are implicit. All of the arcs
 * emanating from a single node are stored as a linked list in
 * alphabetical order.
 */
typedef struct {
    int letter:8;   /* the letter this arc is labelled with */
    int arc:24;     /* the node this arc points to (i.e. its first arc) */
    int next:24;    /* the next sibling arc emanating from this node */
    int final:1;    /* true if this arc is the end of a valid word */
} treearc;

/* Each of the slots that make up the playing board is represented
 * by the die it contains.
 */
typedef struct {
    unsigned char die;      /* which die is in this slot */
    unsigned char face;     /* which face of the die is showing */
} slot;

/* The following information defines a game.
 */
typedef struct {
    slot board[BOARDSIZE];  /* the contents of the board */
    int score;              /* how many points the board is worth */
} game;

/* The wordlist is stored as a binary search tree.
 */
typedef struct {
    int item: 24;   /* the identifier of a word in the list */
    int left: 16;   /* the branch with smaller identifiers */
    int right: 16;  /* the branch with larger identifiers */
} listnode;

/* The dictionary.
 */
static treearc *dictionary;
static int heapalloc;
static int heapsize;

/* Every slot's immediate neighbors.
 */
static int neighbors[BOARDSIZE][9];

/* The wordlist, used while scoring a board.
 */
static listnode *wordlist;
static int listalloc;
static int listsize;
static int xcursor;

/* The game that is currently being examined.
 */
static game G;

/* The highest-scoring game seen so far.
 */
static game bestgame;

/* Variables to time the program and display stats.
 */
static time_t start;
static int boardcount;
static int allscores;

/* The pool contains the N highest-scoring games seen so far.
 */
static game pool[MAXPOOLSIZE];
static int poolsize;
static int cutoffscore;
static int stallcounter;

/* Some buffers shared by recursive functions.
 */
static char wordbuf[WORDBUFSIZE];
static char gridbuf[BOARDSIZE];

/*
 * The dictionary is stored as a tree. It is created during
 * initialization and remains unmodified afterwards. When moving
 * through the tree, the program tracks the arc that points to the
 * current node. (The first arc in the heap is a dummy that points to
 * the root node, which otherwise would have no arc.)
 */

static void initdictionary(void)
{
    heapalloc = 256;
    dictionary = malloc(256 * sizeof *dictionary);
    heapsize = 1;
    dictionary->arc = 0;
    dictionary->letter = 0;
    dictionary->next = 0;
    dictionary->final = 0;
}

static int addarc(int arc, char ch)
{
    int prev, a;

    prev = arc;
    a = dictionary[arc].arc;
    for (;;) {
        if (dictionary[a].letter == ch)
            return a;
        if (!dictionary[a].letter || dictionary[a].letter > ch)
            break;
        prev = a;
        a = dictionary[a].next;
    }
    if (heapsize >= heapalloc) {
        heapalloc *= 2;
        dictionary = realloc(dictionary, heapalloc * sizeof *dictionary);
    }
    a = heapsize++;
    dictionary[a].letter = ch;
    dictionary[a].final = 0;
    dictionary[a].arc = 0;
    if (prev == arc) {
        dictionary[a].next = dictionary[prev].arc;
        dictionary[prev].arc = a;
    } else {
        dictionary[a].next = dictionary[prev].next;
        dictionary[prev].next = a;
    }
    return a;
}

static int validateword(char *word)
{
    int i;

    for (i = 0 ; word[i] != '\0' && word[i] != '\n' ; ++i)
        if (word[i] < 'a' || word[i] > 'z')
            return 0;
    if (word[i] == '\n')
        word[i] = '\0';
    if (i < 3)
        return 0;
    for ( ; *word ; ++word, --i) {
        if (*word == 'q') {
            if (word[1] != 'u')
                return 0;
            memmove(word + 1, word + 2, --i);
        }
    }
    return 1;
}

static void createdictionary(char const *filename)
{
    FILE *fp;
    int arc, i;

    initdictionary();
    fp = fopen(filename, "r");
    while (fgets(wordbuf, sizeof wordbuf, fp)) {
        if (!validateword(wordbuf))
            continue;
        arc = 0;
        for (i = 0 ; wordbuf[i] ; ++i)
            arc = addarc(arc, wordbuf[i]);
        dictionary[arc].final = 1;
    }
    fclose(fp);
}

/*
 * The wordlist is stored as a binary search tree. It is only added
 * to, searched, and erased. Instead of storing the actual word, it
 * only retains the word's final arc in the dictionary. Thus, the
 * dictionary needs to be walked in order to print out the wordlist.
 */

static void initwordlist(void)
{
    listalloc = 16;
    wordlist = malloc(listalloc * sizeof *wordlist);
    listsize = 0;
}

static int iswordinlist(int word)
{
    int node, n;

    n = 0;
    for (;;) {
        node = n;
        if (wordlist[node].item == word)
            return 1;
        if (wordlist[node].item > word)
            n = wordlist[node].left;
        else
            n = wordlist[node].right;
        if (!n)
            return 0;
    }
}

static int insertword(int word)
{
    int node, n;

    if (!listsize) {
        wordlist->item = word;
        wordlist->left = 0;
        wordlist->right = 0;
        ++listsize;
        return 1;
    }

    n = 0;
    for (;;) {
        node = n;
        if (wordlist[node].item == word)
            return 0;
        if (wordlist[node].item > word)
            n = wordlist[node].left;
        else
            n = wordlist[node].right;
        if (!n)
            break;
    }

    if (listsize >= listalloc) {
        listalloc *= 2;
        wordlist = realloc(wordlist, listalloc * sizeof *wordlist);
    }
    n = listsize++;
    wordlist[n].item = word;
    wordlist[n].left = 0;
    wordlist[n].right = 0;
    if (wordlist[node].item > word)
        wordlist[node].left = n;
    else
        wordlist[node].right = n;
    return 1;
}

static void clearwordlist(void)
{
    listsize = 0;
    G.score = 0;
}


static void scoreword(char const *word)
{
    int const scoring[] = { 0, 0, 0, 1, 1, 2, 3, 5 };
    int n, u;

    for (n = u = 0 ; word[n] ; ++n)
        if (word[n] == 'q')
            ++u;
    n += u;
    G.score += n > 7 ? 11 : scoring[n];
}

static void addwordtolist(char const *word, int id)
{
    if (insertword(id))
        scoreword(word);
}

static void _printwords(int arc, int len)
{
    int a;

    while (arc) {
        a = len + 1;
        wordbuf[len] = dictionary[arc].letter;
        if (wordbuf[len] == 'q')
            wordbuf[a++] = 'u';
        if (dictionary[arc].final) {
            if (iswordinlist(arc)) {
                wordbuf[a] = '\0';
                if (xcursor == 4) {
                    printf("%s\n", wordbuf);
                    xcursor = 0;
                } else {
                    printf("%-16s", wordbuf);
                    ++xcursor;
                }
            }
        }
        _printwords(dictionary[arc].arc, a);
        arc = dictionary[arc].next;
    }
}

static void printwordlist(void)
{
    xcursor = 0;
    _printwords(1, 0);
    if (xcursor)
        putchar('\n');
}

/*
 * The board is stored as an array of oriented dice. To score a game,
 * the program looks at each slot on the board in turn, and tries to
 * find a path along the dictionary tree that matches the letters on
 * adjacent dice.
 */

static void initneighbors(void)
{
    int i, j, n;

    for (i = 0 ; i < BOARDSIZE ; ++i) {
        n = 0;
        for (j = 0 ; j < BOARDSIZE ; ++j)
            if (i != j && abs(i / XSIZE - j / XSIZE) <= 1
                       && abs(i % XSIZE - j % XSIZE) <= 1)
                neighbors[i][n++] = j;
        neighbors[i][n] = -1;
    }
}

static void printboard(void)
{
    int i;

    for (i = 0 ; i < BOARDSIZE ; ++i) {
        printf(" %c", toupper(dice[G.board[i].die][G.board[i].face]));
        if (i % XSIZE == XSIZE - 1)
            putchar('\n');
    }
}

static void _findwords(int pos, int arc, int len)
{
    int ch, i, p;

    for (;;) {
        ch = dictionary[arc].letter;
        if (ch == gridbuf[pos])
            break;
        if (ch > gridbuf[pos] || !dictionary[arc].next)
            return;
        arc = dictionary[arc].next;
    }
    wordbuf[len++] = ch;
    if (dictionary[arc].final) {
        wordbuf[len] = '\0';
        addwordtolist(wordbuf, arc);
    }
    gridbuf[pos] = '.';
    for (i = 0 ; (p = neighbors[pos][i]) >= 0 ; ++i)
        if (gridbuf[p] != '.')
            _findwords(p, dictionary[arc].arc, len);
    gridbuf[pos] = ch;
}

static void findwordsingrid(void)
{
    int i;

    clearwordlist();
    for (i = 0 ; i < BOARDSIZE ; ++i)
        gridbuf[i] = dice[G.board[i].die][G.board[i].face];
    for (i = 0 ; i < BOARDSIZE ; ++i)
        _findwords(i, 1, 0);
}

static void shuffleboard(void)
{
    int die[BOARDSIZE];
    int i, n;

    for (i = 0 ; i < BOARDSIZE ; ++i)
        die[i] = i;
    for (i = BOARDSIZE ; i-- ; ) {
        n = random(i);
        G.board[i].die = die[n];
        G.board[i].face = random(DIEFACES);
        die[n] = die[i];
    }
}

/*
 * The pool contains the N highest-scoring games found so far. (This
 * would typically be done using a priority queue, but it represents
 * far too little of the runtime. Brute force is just as good and
 * simpler.) Note that the pool will only ever contain one board with
 * a particular score: This is a cheap way to discourage the pool from
 * filling up with almost-identical high-scoring boards.
 */

static void addgametopool(void)
{
    int i;

    if (G.score < cutoffscore)
        return;
    for (i = 0 ; i < poolsize ; ++i) {
        if (G.score == pool[i].score) {
            pool[i] = G;
            return;
        }
        if (G.score > pool[i].score)
            break;
    }
    if (poolsize < MAXPOOLSIZE)
        ++poolsize;
    if (i < poolsize) {
        memmove(pool + i + 1, pool + i, (poolsize - i - 1) * sizeof *pool);
        pool[i] = G;
    }
    cutoffscore = pool[poolsize - 1].score;
    stallcounter = 0;
}

static void selectpoolmember(int n)
{
    G = pool[n];
}

static void emptypool(void)
{
    poolsize = 0;
    cutoffscore = 0;
    stallcounter = 0;
}

/*
 * The program examines as many boards as it can in the given time,
 * and retains the one with the highest score. If the program is out
 * of time, then it reports the best-seen game and immediately exits.
 */

static void report(void)
{
    findwordsingrid();
    printboard();
    printwordlist();
    printf("score = %d\n", G.score);
    fprintf(stderr, "// score: %d points (%d words)\n", G.score, listsize);
    fprintf(stderr, "// %d boards examined\n", boardcount);
    fprintf(stderr, "// avg score: %.1f\n", (double)allscores / boardcount);
    fprintf(stderr, "// runtime: %ld s\n", time(0) - start);
}

static void scoreboard(void)
{
    findwordsingrid();
    ++boardcount;
    allscores += G.score;
    addgametopool();
    if (bestgame.score < G.score) {
        bestgame = G;
        fprintf(stderr, "// %ld s: board %d scoring %d\n",
                time(0) - start, boardcount, G.score);
    }

    if (time(0) - start >= RUNTIME) {
        G = bestgame;
        report();
        exit(0);
    }
}

static void restartpool(void)
{
    emptypool();
    while (poolsize < MAXPOOLSIZE) {
        shuffleboard();
        scoreboard();
    }
}

/*
 * Making small modifications to a board.
 */

static void turndie(void)
{
    int i, j;

    i = random(BOARDSIZE);
    j = random(DIEFACES - 1) + 1;
    G.board[i].face = (G.board[i].face + j) % DIEFACES;
}

static void swapdice(void)
{
    slot t;
    int p, q;

    p = random(BOARDSIZE);
    q = random(BOARDSIZE - 1);
    if (q >= p)
        ++q;
    t = G.board[p];
    G.board[p] = G.board[q];
    G.board[q] = t;
}

/*
 *
 */

int main(void)
{
    int i;

    start = time(0);
    srand((unsigned int)start);

    createdictionary(WORDLISTFILE);
    initwordlist();
    initneighbors();

    restartpool();
    for (;;) {
        for (i = 0 ; i < poolsize ; ++i) {
            selectpoolmember(i);
            turndie();
            scoreboard();
            selectpoolmember(i);
            swapdice();
            scoreboard();
        }
        ++stallcounter;
        if (stallcounter >= STALLPOINT) {
            fprintf(stderr, "// stalled; restarting search\n");
            restartpool();
        }
    }

    return 0;
}

Notes pour la version 2 (9 juin)

Voici une façon d'utiliser la version initiale de mon code comme point de départ. Les modifications apportées à cette version se composent de moins de 100 lignes, mais ont triplé le score de jeu moyen.

Dans cette version, le programme maintient un "pool" de candidats, composé des N tableaux les mieux notés que le programme a généré jusqu'à présent. Chaque fois qu'un nouveau tableau est généré, il est ajouté au pool et le tableau le moins bien noté dans le pool est supprimé (qui peut très bien être le tableau qui vient d'être ajouté, si son score est inférieur à celui qui existe déjà). Le pool est initialement rempli de cartes générées aléatoirement, après quoi il conserve une taille constante tout au long du programme.

La boucle principale du programme consiste à sélectionner un tableau aléatoire dans le pool et à le modifier, à déterminer le score de ce nouveau tableau, puis à le placer dans le pool (s'il réussit assez bien). De cette façon, le programme perfectionne continuellement les tableaux à score élevé. L'activité principale consiste à apporter des améliorations progressives et incrémentielles, mais la taille du pool permet également au programme de trouver des améliorations en plusieurs étapes qui aggravent temporairement le score d'un conseil avant de pouvoir l'améliorer.

Généralement, ce programme trouve un bon maximum local assez rapidement, après quoi un meilleur maximum est probablement trop éloigné pour être trouvé. Et donc, encore une fois, il est inutile d'exécuter le programme pendant plus de 10 secondes. Cela pourrait être amélioré, par exemple en demandant au programme de détecter cette situation et de lancer une nouvelle recherche avec un nouveau groupe de candidats. Cependant, cela ne représenterait qu'une augmentation marginale. Une technique de recherche heuristique appropriée serait probablement une meilleure voie d'exploration.

(Note latérale: j'ai vu que cette version générait environ 5 000 tableaux / seconde. Comme la première version faisait généralement 20 000 tableaux / seconde, j'étais initialement inquiet. Cependant, lors du profilage, j'ai découvert que le temps supplémentaire était consacré à la gestion de la liste de mots. En d'autres termes, cela est entièrement dû au fait que le programme a trouvé beaucoup plus de mots par tableau. À la lumière de cela, j'ai envisagé de changer le code pour gérer la liste de mots, mais étant donné que ce programme n'utilise que 10 des 120 secondes allouées, telles que une optimisation serait très prématurée.)

Notes pour la version 1 (2 juin)

Il s'agit d'une solution très, très simple. Tout ce qu'il fait, il génère des tableaux aléatoires, puis après 10 secondes, il sort celui avec le score le plus élevé. (Je suis passé par défaut à 10 secondes car les 110 secondes supplémentaires autorisées par la spécification du problème n'améliorent généralement pas la solution finale trouvée suffisamment pour mériter d'être attendue.) C'est donc extrêmement stupide. Cependant, il dispose de toutes les infrastructures nécessaires pour constituer un bon point de départ pour une recherche plus intelligente, et si quelqu'un souhaite en faire usage avant la date limite, je les encourage à le faire.

Le programme commence par lire le dictionnaire dans une arborescence. (Le formulaire n'est pas aussi optimisé qu'il pourrait l'être, mais il est plus que suffisant pour ces fins.) Après une autre initialisation de base, il commence alors à générer des tableaux et à les noter. Le programme examine environ 20 000 planches par seconde sur ma machine, et après environ 200 000 planches, l'approche aléatoire commence à tourner à sec.

Puisqu'une seule carte est réellement évaluée à un moment donné, les données de notation sont stockées dans des variables globales. Cela me permet de minimiser la quantité de données constantes qui doivent être transmises comme arguments aux fonctions récursives. (Je suis sûr que cela donnera des ruches à certaines personnes, et je m'excuse pour elles.) La liste de mots est stockée sous forme d'arbre de recherche binaire. Chaque mot trouvé doit être recherché dans la liste de mots, afin que les mots en double ne soient pas comptés deux fois. La liste de mots n'est nécessaire que pendant le processus d'évaluation, elle est donc supprimée une fois la partition trouvée. Ainsi, à la fin du programme, le tableau choisi doit être noté à nouveau, afin que la liste de mots puisse être imprimée.

Fait amusant: Le score moyen pour un tableau Boggle généré de manière aléatoire, tel que marqué par english.0, est de 61,7 points.

boite à pain
la source
De toute évidence, je dois améliorer ma propre efficacité. :-)
Gaffi
Mon approche génétique obtient environ 700 à 800 points, générant environ 200 000 cartes, donc vous faites clairement quelque chose de bien mieux que moi dans la façon dont vous produisez la prochaine génération.
Peter Taylor
Ma propre structure arborescente, n'ayant été mise en œuvre que pour la liste de mots principale jusqu'à présent, alors qu'elle fonctionne et me permet de valider les cartes, elle réduit la mémoire de mon système (ralentit activement au point qu'il faut beaucoup de temps pour forcer le processus pour résilier plus tôt). C'est sûrement ma faute, mais j'y travaille! Edit: Déjà corrigé! ;-)
Gaffi
@PeterTaylor J'ai pensé à essayer un algorithme génétique, mais je ne pouvais pas penser à un mécanisme plausible pour combiner deux cartes. Comment tu fais ça? Sélectionnez-vous le parent au hasard pour chaque emplacement sur la carte?
boîte à pain le
J'ai divisé l'état du plateau en permutation des dés et les visages apparaissant sur les dés. Pour le crossover de permutation, j'utilise le "crossover de commande 1" de cs.colostate.edu/~genitor/1995/permutations.pdf et pour le crossover de visage, je fais l'évidence. Mais j'ai une idée d'une approche totalement différente dont j'ai besoin pour trouver le temps de la mettre en œuvre.
Peter Taylor
3

VBA (moyenne actuellement comprise entre 80 et 110 points, inachevé)

Voici mon processus de travail, mais c'est loin d'être le meilleur possible; mon meilleur score absolu trouvé sur n'importe quelle planche après de nombreux tests est d'environ 120. Il doit encore y avoir un meilleur nettoyage général et je suis sûr qu'il y a plus d'efficacité à gagner à plusieurs endroits.

  • 09.05.2012:
    • Publication d'origine
  • 2012.05.10 - 2012.05.18:
    • Amélioration de l'algorithme de notation
    • Amélioration de la logique d'orientation
  • 2012.06.07 - 2012.06.12 :
    • Limite de mots réduite à 6 au lieu de 8. Permet plus de tableaux avec des mots plus petits. Semble avoir légèrement amélioré le score moyen. (10 à 15 planches vérifiées par série contre 1 à 2)
    • Suite à la suggestion de la boîte à pain, j'ai créé une arborescence pour héberger la liste de mots. Cela accélère considérablement la vérification back-end des mots sur un tableau.
    • J'ai joué avec le changement de la taille maximale des mots (vitesse vs score) et je n'ai pas encore décidé si 5 ou 6 est une meilleure option pour moi. 6 résultats dans 100-120 tableaux totaux vérifiés, tandis que 5 résultats dans 500-1000 (les deux étant encore bien en deçà des autres exemples fournis jusqu'à présent).
    • Problème : après plusieurs exécutions successives, le processus commence à ralentir, il reste donc de la mémoire à gérer.

Cela semble probablement horrible pour certains d'entre vous, mais comme je l'ai dit, WIP. Je suis très ouvert aux critiques constructives ! Désolé pour le corps très long ...


Module de classe de dés :

Option Explicit

Private Sides() As String

Sub NewDie(NewLetters As String)
    Sides = Split(NewLetters, ",")
End Sub

Property Get Side(i As Integer)
    Side = Sides(i)
End Property

Module de classe d'arbre :

Option Explicit

Private zzroot As TreeNode


Sub AddtoTree(ByVal TreeWord As Variant)
Dim i As Integer
Dim TempNode As TreeNode

    Set TempNode = TraverseTree(TreeWord, zzroot)
    SetNode TreeWord, TempNode

End Sub

Private Function SetNode(ByVal Value As Variant, parent As TreeNode) As TreeNode
Dim ValChar As String
    If Len(Value) > 0 Then
        ValChar = Left(Value, 1)
        Select Case Asc(ValChar) - 96
            Case 1:
                Set parent.Node01 = AddNode(ValChar, parent.Node01)
                Set SetNode = parent.Node01
            Case 2:
                Set parent.Node02 = AddNode(ValChar, parent.Node02)
                Set SetNode = parent.Node02
            ' ... - Reduced to limit size of answer.
            Case 26:
                Set parent.Node26 = AddNode(ValChar, parent.Node26)
                Set SetNode = parent.Node26
            Case Else:
                Set SetNode = Nothing
        End Select

        Set SetNode = SetNode(Right(Value, Len(Value) - 1), SetNode)
    Else
        Set parent.Node27 = AddNode(True, parent.Node27)
        Set SetNode = parent.Node27
    End If
End Function

Function AddNode(ByVal Value As Variant, NewNode As TreeNode) As TreeNode
    If NewNode Is Nothing Then
        Set AddNode = New TreeNode
        AddNode.Value = Value
    Else
        Set AddNode = NewNode
    End If
End Function
Function TraverseTree(TreeWord As Variant, parent As TreeNode) As TreeNode
Dim Node As TreeNode
Dim ValChar As String
    If Len(TreeWord) > 0 Then
        ValChar = Left(TreeWord, 1)

        Select Case Asc(ValChar) - 96
            Case 1:
                Set Node = parent.Node01
            Case 2:
                Set Node = parent.Node02
            ' ... - Reduced to limit size of answer.
            Case 26:
                Set Node = parent.Node26
            Case Else:
                Set Node = Nothing
        End Select

        If Not Node Is Nothing Then
            Set TraverseTree = TraverseTree(Right(TreeWord, Len(TreeWord) - 1), Node)
            If Not TraverseTree Is Nothing Then
                Set TraverseTree = parent
            End If
        Else
            Set TraverseTree = parent
        End If
    Else
        If parent.Node27.Value Then
            Set TraverseTree = parent
        Else
            Set TraverseTree = Nothing
        End If
    End If
End Function

Function WordScore(TreeWord As Variant, Step As Integer, Optional parent As TreeNode = Nothing) As Integer
Dim Node As TreeNode
Dim ValChar As String
    If parent Is Nothing Then Set parent = zzroot
    If Len(TreeWord) > 0 Then
        ValChar = Left(TreeWord, 1)

        Select Case Asc(ValChar) - 96
            Case 1:
                Set Node = parent.Node01
            Case 2:
                Set Node = parent.Node02
            ' ... - Reduced to limit size of answer.
            Case 26:
                Set Node = parent.Node26
            Case Else:
                Set Node = Nothing
        End Select

        If Not Node Is Nothing Then
            WordScore = WordScore(Right(TreeWord, Len(TreeWord) - 1), Step + 1, Node)
        End If
    Else
        If parent.Node27 Is Nothing Then
            WordScore = 0
        Else
            WordScore = Step
        End If
    End If
End Function

Function ValidWord(TreeWord As Variant, Optional parent As TreeNode = Nothing) As Integer
Dim Node As TreeNode
Dim ValChar As String
    If parent Is Nothing Then Set parent = zzroot
    If Len(TreeWord) > 0 Then
        ValChar = Left(TreeWord, 1)

        Select Case Asc(ValChar) - 96
            Case 1:
                Set Node = parent.Node01
            Case 2:
                Set Node = parent.Node02
            ' ... - Reduced to limit size of answer.
            Case 26:
                Set Node = parent.Node26
            Case Else:
                Set Node = Nothing
        End Select

        If Not Node Is Nothing Then
            ValidWord = ValidWord(Right(TreeWord, Len(TreeWord) - 1), Node)
        Else
            ValidWord = False
        End If
    Else
        If parent.Node27 Is Nothing Then
            ValidWord = False
        Else
            ValidWord = True
        End If
    End If
End Function

Private Sub Class_Initialize()
    Set zzroot = New TreeNode
End Sub

Private Sub Class_Terminate()
    Set zzroot = Nothing
End Sub

Module de classe TreeNode :

Option Explicit

Public Value As Variant
Public Node01 As TreeNode
Public Node02 As TreeNode
' ... - Reduced to limit size of answer.
Public Node26 As TreeNode
Public Node27 As TreeNode

Module principal :

Option Explicit

Const conAllSides As String = ";a,a,e,e,g,n;e,l,r,t,t,y;a,o,o,t,t,w;a,b,b,j,o,o;e,h,r,t,v,w;c,i,m,o,t,u;d,i,s,t,t,y;e,i,o,s,s,t;d,e,l,r,v,y;a,c,h,o,p,s;h,i,m,n,qu,u;e,e,i,n,s,u;e,e,g,h,n,w;a,f,f,k,p,s;h,l,n,n,r,z;d,e,i,l,r,x;"
Dim strBoard As String, strBoardTemp As String, strWords As String, strWordsTemp As String
Dim CheckWordSub As String
Dim iScore As Integer, iScoreTemp As Integer
Dim Board(1 To 4, 1 To 4) As Integer
Dim AllDice(1 To 16) As Dice
Dim AllWordsTree As Tree
Dim AllWords As Scripting.Dictionary
Dim CurWords As Scripting.Dictionary
Dim FullWords As Scripting.Dictionary
Dim JunkWords As Scripting.Dictionary
Dim WordPrefixes As Scripting.Dictionary
Dim StartTime As Date, StopTime As Date
Const MAX_LENGTH As Integer = 5
Dim Points(3 To 8) As Integer

Sub Boggle()
Dim DiceSetup() As String
Dim i As Integer, j As Integer, k As Integer

    StartTime = Now()

    strBoard = vbNullString
    strWords = vbNullString
    iScore = 0

    ReadWordsFileTree

    DiceSetup = Split(conAllSides, ";")

    For i = 1 To 16
        Set AllDice(i) = New Dice
        AllDice(i).NewDie "," & DiceSetup(i)
    Next i

    Do While WithinTimeLimit

        Shuffle

        strBoardTemp = vbNullString
        strWordsTemp = vbNullString
        iScoreTemp = 0

        FindWords

        If iScoreTemp > iScore Or iScore = 0 Then
            iScore = iScoreTemp
            k = 1
            For i = 1 To 4
                For j = 1 To 4
                    strBoardTemp = strBoardTemp & AllDice(k).Side(Board(j, i)) & "  "
                    k = k + 1
                Next j
                strBoardTemp = strBoardTemp & vbNewLine
            Next i
            strBoard = strBoardTemp
            strWords = strWordsTemp

        End If

    Loop

    Debug.Print strBoard
    Debug.Print strWords
    Debug.Print iScore & " points"

    Set AllWordsTree = Nothing
    Set AllWords = Nothing
    Set CurWords = Nothing
    Set FullWords = Nothing
    Set JunkWords = Nothing
    Set WordPrefixes = Nothing

End Sub

Sub ShuffleBoard()
Dim i As Integer

    For i = 1 To 16
        If Not WithinTimeLimit Then Exit Sub
        Board(Int((i - 1) / 4) + 1, 4 - (i Mod 4)) = Int(6 * Rnd() + 1)
    Next i

End Sub

Sub Shuffle()
Dim n As Long
Dim Temp As Variant
Dim j As Long

    Randomize
    ShuffleBoard
    For n = 1 To 16
        If Not WithinTimeLimit Then Exit Sub
        j = CLng(((16 - n) * Rnd) + n)
        If n <> j Then
            Set Temp = AllDice(n)
            Set AllDice(n) = AllDice(j)
            Set AllDice(j) = Temp
        End If
    Next n

    Set FullWords = New Scripting.Dictionary
    Set CurWords = New Scripting.Dictionary
    Set JunkWords = New Scripting.Dictionary

End Sub

Sub ReadWordsFileTree()
Dim FSO As New FileSystemObject
Dim FS
Dim strTemp As Variant
Dim iLength As Integer
Dim StartTime As Date

    StartTime = Now()
    Set AllWordsTree = New Tree
    Set FS = FSO.OpenTextFile("P:\Personal\english.txt")

    Points(3) = 1
    Points(4) = 1
    Points(5) = 2
    Points(6) = 3
    Points(7) = 5
    Points(8) = 11

    Do Until FS.AtEndOfStream
        strTemp = FS.ReadLine
        If strTemp = LCase(strTemp) Then
            iLength = Len(strTemp)
            iLength = IIf(iLength > 8, 8, iLength)
            If InStr(strTemp, "'") < 1 And iLength > 2 Then
                AllWordsTree.AddtoTree strTemp
            End If
        End If
    Loop
    FS.Close

End Sub

Function GetScoreTree() As Integer
Dim TempScore As Integer

    If Not WithinTimeLimit Then Exit Function

    GetScoreTree = 0

    TempScore = AllWordsTree.WordScore(CheckWordSub, 0)
    Select Case TempScore
        Case Is < 3:
            GetScoreTree = 0
        Case Is > 8:
            GetScoreTree = 11
        Case Else:
            GetScoreTree = Points(TempScore)
    End Select

End Function

Sub SubWords(CheckWord As String)
Dim CheckWordScore As Integer
Dim k As Integer, l As Integer

    For l = 0 To Len(CheckWord) - 3
        For k = 1 To Len(CheckWord) - l
            If Not WithinTimeLimit Then Exit Sub

            CheckWordSub = Mid(CheckWord, k, Len(CheckWord) - ((k + l) - 1))

            If Len(CheckWordSub) >= 3 And Not CurWords.Exists(CheckWordSub) Then
                CheckWordScore = GetScoreTree

                If CheckWordScore > 0 Then
                    CurWords.Add CheckWordSub, CheckWordSub
                    iScoreTemp = iScoreTemp + CheckWordScore
                    strWordsTemp = strWordsTemp & CheckWordSub & vbNewLine
                End If

                If Left(CheckWordSub, 1) = "q" Then
                    k = k + 1
                End If
            End If

        Next k
    Next l

End Sub

Sub FindWords()
Dim CheckWord As String
Dim strBoardLine(1 To 16) As String
Dim Used(1 To 16) As Boolean
Dim i As Integer, j As Integer, k As Integer, l As Integer, m As Integer, n As Integer
Dim StartSquare As Integer
Dim FullCheck As Variant

    n = 1
    For l = 1 To 4
        For m = 1 To 4
            If Not WithinTimeLimit Then Exit Sub
            strBoardLine(n) = AllDice(n).Side(Board(m, l))
            n = n + 1
        Next m
    Next l

    For i = 1 To 16
        For k = 1 To 16

            If Not WithinTimeLimit Then Exit Sub
            If k Mod 2 = 0 Then
                For j = 1 To 16
                    Used(j) = False
                Next j

                Used(i) = True
                MakeWords strBoardLine, Used, i, k / 2, strBoardLine(i)
            End If

        Next k
    Next i

    For Each FullCheck In FullWords.Items
        SubWords CStr(FullCheck)
    Next FullCheck

End Sub

Function MakeWords(BoardLine() As String, Used() As Boolean, _
    Start As Integer, _
    Direction As Integer, CurString As String) As String
Dim i As Integer, j As Integer, k As Integer, l As Integer

    j = 0

    Select Case Direction
        Case 1:
            k = Start - 5
        Case 2:
            k = Start - 4
        Case 3:
            k = Start - 3
        Case 4:
            k = Start - 1
        Case 5:
            k = Start + 1
        Case 6:
            k = Start + 3
        Case 7:
            k = Start + 4
        Case 8:
            k = Start + 5
    End Select

    If k >= 1 And k <= 16 Then
        If Not WithinTimeLimit Then Exit Function

        If Not Used(k) Then
            If ValidSquare(Start, k) Then
                If Not (JunkWords.Exists(CurString & BoardLine(k))) And Not FullWords.Exists(CurString & BoardLine(k)) Then
                    Used(k) = True
                    For l = 1 To MAX_LENGTH
                        If Not WithinTimeLimit Then Exit Function
                        MakeWords = CurString & BoardLine(k)
                        If Not (JunkWords.Exists(MakeWords)) Then
                            JunkWords.Add MakeWords, MakeWords
                        End If
                        If Len(MakeWords) = MAX_LENGTH And Not FullWords.Exists(MakeWords) Then
                            FullWords.Add MakeWords, MakeWords
                        ElseIf Len(MakeWords) < MAX_LENGTH Then
                            MakeWords BoardLine, Used, k, l, MakeWords
                        End If
                    Next l
                    Used(k) = False
                End If
            End If
        End If
    End If

    If Len(MakeWords) = MAX_LENGTH And Not FullWords.Exists(MakeWords) Then
        FullWords.Add MakeWords, MakeWords
        Debug.Print "FULL - " & MakeWords
    End If

End Function

Function ValidSquare(StartSquare As Integer, EndSquare As Integer) As Boolean
Dim sx As Integer, sy As Integer, ex As Integer, ey As Integer

    If Not WithinTimeLimit Then Exit Function

    sx = (StartSquare - 1) Mod 4 + 1
    ex = (EndSquare - 1) Mod 4 + 1

    sy = Int((StartSquare - 1) / 4 + 1)
    ey = Int((EndSquare - 1) / 4 + 1)

    ValidSquare = (sx - 1 <= ex And sx + 1 >= ex) And (sy - 1 <= ey And sy + 1 >= ey) And StartSquare <> EndSquare

End Function

Function WithinTimeLimit() As Boolean
    StopTime = Now()
    WithinTimeLimit = (Round(CDbl(((StopTime - StartTime) - Int(StopTime - StartTime)) * 86400), 0) < 120)
End Function
Gaffi
la source
2
Je n'ai pas parcouru le code, mais 50 points est ridiculement bas. J'ai joué sur des tableaux générés aléatoirement avec des scores supérieurs à 1000 (en utilisant SOWPODS - la liste de mots fournie peut être moins étendue). Vous voudrez peut-être vérifier une erreur de signe!
Peter Taylor
@PeterTaylor Merci pour la suggestion. Je sais que ce score est beaucoup trop bas, et je sais qu'une partie du problème réside dans le fait que je peux voir des mots évidents manqués ...
Gaffi
@PeterTaylor Aussi, pour mémoire, je poste continuellement mes progrès, plutôt que d'attendre mon produit final, car personne d'autre ne l'a encore essayé. Je voudrais garder la question quelque peu vivante jusqu'à ce que cela se produise.
Gaffi
Je dois également noter que cela ne fonctionne pas sur la machine la plus rapide, donc cela n'aide pas non plus.
Gaffi
1
@Gaffi 10 secondes pour calculer le score? C'est 9,999 secondes de trop. Vous devez repenser votre code. Si vous refusez de transformer votre liste de mots en arborescence, faites au moins ceci: créez une liste (table de hachage, peu importe) de tous les préfixes à deux lettres. Ensuite, lorsque vous commencez à suivre un chemin sur le tableau, si les deux premières lettres ne figurent pas dans la liste, sautez ce sous-arbre entier des chemins possibles. Encore une fois, la construction de l'arborescence complète est préférable, mais la liste des préfixes à deux lettres sera utile et très peu coûteuse à réaliser.
boîte à pain le
2

Aperçu rapide de la taille de l'espace de recherche.

   16! => 20922789888000 Dice Permutations
(6^16) =>  2821109907456 Face Permutations
 59025489844657012604928000 Boggle Grids 

Réduire pour exclure la répétition sur chaque dé.

              16! => 20922789888000 Dice Permutations
(4^4)*(5^6)*(6^5) => 31104000000 Unique Face Permutations
   650782456676352000000000 Boggle Grids 

@breadbox stocke le dictionnaire en tant que vérification de la table de hachage O (1).

ÉDITER

Meilleure planche (j'en ai été témoin jusqu'à présent)

L  E  A  N
S  E  T  M
T  S  B  D
I  E  G  O

Score: 830
Words: 229
SLEETIEST  MANTELETS
MANTEELS  MANTELET  MATELESS
MANTEEL  MANTELS  TESTEES  BETISES  OBTESTS  OBESEST
SLEETS  SLEEST  TESTIS  TESTES  TSETSE  MANTES  MANTEL  TESTAE  TESTEE
STEELS  STELES  BETELS  BESETS  BESITS  BETISE  BODGES  BESEES  EISELS
GESTES  GEISTS  OBTEST
LEANT  LEATS  LEETS  LEESE  LESES  LESTS  LESBO  ANTES  NATES  SLEET  SETAE
SEATS  STIES  STEEL  STETS  STEAN  STEAM  STELE  SELES  TAELS  TEELS  TESTS
TESTE  TELES  TETES  MATES  TESTA  TEATS  SEELS  SITES  BEETS  BETEL  BETES
BESET  BESTS  BESIT  BEATS  BODGE  BESEE  DOGES  EISEL  GESTS  GESTE  GESSE
GEITS  GEIST  OBESE
LEAN  LEAT  LEAM  LEET  LEES  LETS  LEST  LESS  EATS  EELS  ELSE  ETNA  ESES
ESTS  ESSE  ANTE  ANTS  ATES  AMBO  NATS  SLEE  SEEL  SETA  SETS  SESE  SEAN
SEAT  SEAM  SELE  STIE  STET  SEES  TAEL  TAES  TEEL  TEES  TEST  TEAM  TELE
TELS  TETS  TETE  MATE  MATS  MAES  TIES  TEAT  TEGS  SELS  SEGO  SITS  SITE
BEET  BEES  BETA  BETE  BETS  BEST  BEAN  BEAT  BEAM  BELS  BOGS  BEGO  BEGS
DOGE  DOGS  DOBS  GOBS  GEST  GEIT  GETS  OBES
LEA  LEE  LET  LES  EAN  EAT  EEL  ELS  ETA  EST  ESS  ANT  ATE  NAT  NAE  NAM
SEE  SET  SEA  SEL  TAN  TAE  TAM  TEE  TES  TEA  TEL  TET  MNA  MAN  MAT  MAE
TIE  TIS  TEG  SEG  SEI  SIT  BEE  BET  BEL  BOD  BOG  BEG  DOG  DOB  ITS  EGO
GOD  GOB  GET  OBS  OBE
EA  EE  EL  ET  ES  AN  AT  AE  AM  NA  ST  TA  TE  MA
TI  SI  BE  BO  DO  IT  IS  GO  OD  OB
Adam Speight
la source
Obtenez-moi une machine avec autant de RAM et nous parlerons.
boîte à pain le
Vous devez diviser les permutations de dés par 8 pour tenir compte des symétries du carré. Aussi, comment obtenez-vous (4 ^ 4) (5 ^ 6) (6 ^ 5)? Je le fais (4 ^ 3) (5 ^ 7) (6 ^ 6), pour un espace de recherche total d'un peu plus de 2 ^ 79.
Peter Taylor
@Peter Taylor: Tu as raison. J'ai dû en supprimer un à plusieurs, quand font les visages uniques. Je pense que nous pouvons convenir qu'il y a 83 visages uniques, (à l'exclusion des répétitions à travers le dé). Choisissez n'importe quel 16 sans répétitions. '83 x 82 x 81 x 80 x 79 x 78 x 77 x 76 x 75 x 74 x 73 x 72 x 71 x 70 x 69 x 68 'Environ: 1,082 x (10 ^ 30) ==> ~ 2 ^ 100 que ce soit, c'est un grand nombre.
Adam Speight
2
@AdamSpeight J'ai initialement supposé que votre commentaire sur le stockage du dictionnaire sous forme de table de hachage n'était qu'une blague, et je l'ai donc fondamentalement ignoré. Mes excuses. Une réponse appropriée serait: En fait, une table de hachage est une structure de données moche pour ce problème. Il ne peut répondre qu'à la question "X est-il un mot valide?", Vous devez donc créer toutes les chaînes possibles afin de trouver les mots. Un DAWG vous permet de demander "X est-il un préfixe d'un mot valide? Et si oui, quelles lettres peuvent le suivre?" Cela vous permet de tailler l'espace de recherche à une toute petite fraction de sa taille totale.
boîte à pain le
La table de hachage est terrible car elle vous empêche de supprimer des fragments de mots qui ne deviendront jamais des mots complets (dicttree.ceiling (fragment) .startsWith (fragment)). Bien que n'importe quel tableau de boggle donné ait plusieurs millions de mots potentiels, vous pouvez en jeter une grande partie après que 2 à 3 lettres aient été assemblées. La traversée de l'arborescence est plus lente que la recherche de table de hachage, mais l'arborescence vous permet de contourner 99+ pour cent du travail grâce au retour en arrière.
Jim W
1

Mon entrée est ici sur Dream.In.Code ~ 30 ms par recherche de carte (sur une machine à 2 cœurs, devrait être plus rapide avec plus de cœurs)

Adam Speight
la source
Toujours à la recherche en elle, mais votre premier lien sur cette page ne contient pas les :en http://. ;-)
Gaffi
Très agréable. Je vais essayer de me voler ça comme une expérience d'apprentissage. .NETà VBAn'est pas trop difficile.
Gaffi
Pourriez-vous mettre à jour la réponse pour inclure votre score moyen lors de l'exécution de la liste ISPELL (pas SOWPODS)? Cela fait partie du défi, et je suis intéressé de voir comment vos résultats se comparent à ceux de la boîte à pain.
Gaffi