Boggle Board mit der besten Punktzahl


16

Ich war daran interessiert, die Antworten auf diese (jetzt nicht mehr existierende) Frage zu sehen , aber sie wurde nie korrigiert / verbessert.

Bestimmen Sie bei einem Satz 6-seitiger Boggle- Würfel (Konfiguration aus dieser Frage gestohlen ) in zwei Minuten Verarbeitungszeit, welche Board-Konfiguration die höchstmögliche Punktzahl ermöglicht. (dh welche Würfel an welcher Stelle mit welcher Seite nach oben ermöglichen den größten Wertungswortpool?)


ZIELSETZUNG

  • Ihr Code sollte nicht länger als 2 Minuten (120 Sekunden) ausgeführt werden. Zu diesem Zeitpunkt sollte die Ausführung automatisch unterbrochen und die Ergebnisse gedruckt werden.

  • Die endgültige Herausforderungspunktzahl ist die durchschnittliche Boggle-Punktzahl von 5 Programmläufen.

    • Bei einem Gleichstand gewinnt der Algorithmus, der mehr Wörter gefunden hat.
    • Falls es immer noch einen Gleichstand gibt, gewinnt der Algorithmus, der mehr lange (8+) Wörter gefunden hat.

REGELN / EINSCHRÄNKUNGEN

  • Dies ist eine Code-Herausforderung. Codelänge ist irrelevant.

  • Unter diesem Link finden Sie eine Wortliste (verwenden Sie die ISPELL "english.0"Liste - in der SCOWL-Liste fehlen einige häufig verwendete Wörter).

    • Diese Auflistung kann nach Belieben in Ihrem Code referenziert / importiert / gelesen werden.
    • Es werden nur Wörter gezählt, die mit dem regulären Ausdruck übereinstimmen ^([a-pr-z]|qu){3,16}$. (Nur Kleinbuchstaben mit 3 bis 16 Zeichen, qu, dürfen als Einheit verwendet werden.)
  • Wörter werden durch Verknüpfen benachbarter Buchstaben (horizontal, vertikal und diagonal) gebildet, um Wörter in der richtigen Reihenfolge zu buchstabieren, ohne einen einzelnen Würfel mehr als einmal in einem einzelnen Wort zu verwenden.

    • Wörter müssen 3 Buchstaben oder länger sein; Kürzere Wörter bringen keine Punkte.
    • Doppelbuchstaben sind zulässig, nur keine Würfel.
    • Wörter, die sich über die Kanten erstrecken, sind nicht erlaubt.
  • Die endgültige Punktzahl für Boggle ( keine Herausforderung ) ist die Summe der Punktwerte für alle gefundenen Wörter.

    • Der für jedes Wort zugewiesene Punktwert basiert auf der Länge des Wortes. (siehe unten)
    • Normale Boggle-Regeln würden Wörter, die von einem anderen Spieler gefunden wurden, abziehen / rabattieren. Angenommen, es sind keine anderen Spieler beteiligt, und alle gefundenen Wörter werden für die Gesamtpunktzahl berücksichtigt.
    • Wörter, die mehr als einmal im selben Raster gefunden wurden, sollten jedoch nur einmal gezählt werden.
  • Ihre Funktion / Programm muss FIND die optimale Anordnung; Eine vorgegebene Liste einfach hart zu codieren, reicht nicht aus.

  • Ihre Ausgabe sollte ein 4x4-Raster Ihres idealen Spielbretts sein, eine Liste aller gefundenen Wörter für dieses Brett und die Boggle-Punktzahl, die mit diesen Wörtern übereinstimmt.


DIE KONFIGURATION

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

STANDARD BOGGLE SCORING TABELLE

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.

BEISPIEL AUSGABE

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

CUT
THE
LUCK
HEX
....

140 points

Wenn weitere Abklärungen erforderlich sind, fragen Sie bitte!


2
Ich würde es vorziehen, ein Wörterbuch zur Verfügung zu haben, um das Ziel zu standardisieren. Beachten Sie auch , dass dies nicht eine neue Idee ist, als eine einfache Google - Suche wird zeigen :) Die höchste Punktzahl Ich habe gesehen ist 4527( 1414Gesamt Wörter), finden Sie hier: ai.stanford.edu/~chuongdo/boggle/index.html
mellamokb

4
Ist das Programm erforderlich, um dieses Jahrhundert zu beenden?
Peter Taylor

1
@GlitchMr In Englisch wird Q normalerweise nur bei U verwendet. Boggle berücksichtigt dies, indem die beiden Buchstaben als eine Einheit auf den gleichen Würfel gelegt werden.
Gaffi

1
Die Wortlistenspezifikation ist unklar. Zählen Sie nur die Wörter, die in Englisch.0 in Kleinbuchstaben aufgeführt sind? (Standard-Wortspielregeln schließen Abkürzungen / Initialismen und Eigennamen aus).
Peter Taylor

1
Ich dachte an Regex ^([a-pr-z]|qu){3,16}$(was fälschlicherweise Wörter mit drei Buchstaben mit qu ausschließen würde, aber es gibt keine).
Peter Taylor

Antworten:


9

C, im Durchschnitt mehr als 1500 1750 Punkte

Dies ist eine relativ geringfügige Verbesserung gegenüber Version 2 (siehe unten für Hinweise zu früheren Versionen). Es gibt zwei Teile. Erstens: Anstatt Bretter zufällig aus dem Pool auszuwählen, durchläuft das Programm nun alle Bretter im Pool und verwendet sie nacheinander, bevor es zum oberen Rand des Pools zurückkehrt und sie wiederholt. (Da der Pool während dieser Iteration geändert wird, gibt es immer noch Boards, die zweimal hintereinander oder noch schlimmer ausgewählt werden. Dies ist jedoch kein ernstes Problem.) Die zweite Änderung besteht darin, dass das Programm jetzt nachverfolgt, wann sich der Pool ändert , und wenn das Programm zu lange läuft, ohne den Poolinhalt zu verbessern, wird festgestellt, dass die Suche "angehalten" hat, der Pool geleert wird und eine neue Suche gestartet wird. Dies wird fortgesetzt, bis die zwei Minuten abgelaufen sind.

Ich hatte ursprünglich gedacht, dass ich eine Art heuristische Suche durchführen würde, um über den 1500-Punkte-Bereich hinauszukommen. @ mellamokbs Kommentar zu einem 4527-Punkte-Board hat mich zu der Annahme veranlasst, dass es viel Raum für Verbesserungen gibt. Wir verwenden jedoch eine relativ kleine Wortliste. Das 4527-Punkte-Board bewertete mit YAWL, der umfassendsten Wortliste auf dem Markt - sie ist sogar größer als die offizielle US-Scrabble-Wortliste. In diesem Sinne überprüfte ich die Boards, die mein Programm gefunden hatte, erneut und stellte fest, dass es eine begrenzte Anzahl von Boards über 1700 zu geben schien. So hatte ich zum Beispiel mehrere Läufe, bei denen ein Board gefunden wurde, das 1726 Punkte erzielte, aber es wurde immer genau dasselbe Board gefunden (ohne Berücksichtigung von Rotationen und Reflexionen).

Als weiteren Test habe ich mein Programm mit YAWL als Wörterbuch ausgeführt und nach etwa einem Dutzend Durchläufen die 4527-Punkte-Karte gefunden. Angesichts dessen gehe ich davon aus, dass sich mein Programm bereits an der oberen Grenze des Suchbereichs befindet und daher die geplante Neufassung zusätzliche Komplexität mit sehr geringem Gewinn einbringen würde.

Hier ist meine Liste der fünf am english.0besten bewerteten Boards, die mein Programm anhand der Wortliste gefunden hat:

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

Meiner Meinung nach ist das "Grep-Board" von 1772 (wie ich es nenne) mit 531 Wörtern das Board mit der höchsten Punktzahl, das mit dieser Wortliste möglich ist. Über 50% der zweiminütigen Läufe meines Programms enden mit diesem Board. Ich habe mein Programm auch über Nacht laufen lassen, ohne dass es etwas Besseres findet. Wenn es also ein Board mit höherer Punktzahl gibt, muss es wahrscheinlich einen Aspekt haben, der die Suchtechnik des Programms zunichte macht. Ein Board, bei dem jede mögliche kleine Änderung des Layouts zum Beispiel zu einem großen Rückgang der Gesamtpunktzahl führt, könnte von meinem Programm möglicherweise nie entdeckt werden. Meiner Meinung nach ist es sehr unwahrscheinlich, dass ein solches Board existiert.

#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;
}

Anmerkungen zu Version 2 (9. Juni)

Hier ist eine Möglichkeit, die ursprüngliche Version meines Codes als Ausgangspunkt zu verwenden. Die Änderungen an dieser Version bestehen aus weniger als 100 Zeilen, haben jedoch die durchschnittliche Spielpunktzahl verdreifacht.

In dieser Version verwaltet das Programm einen "Pool" von Kandidaten, bestehend aus den N Boards mit der höchsten Punktzahl, die das Programm bisher generiert hat. Jedes Mal, wenn ein neues Board erstellt wird, wird es dem Pool hinzugefügt und das Board mit der niedrigsten Punktzahl aus dem Pool entfernt (dies kann sehr gut das Board sein, das gerade hinzugefügt wurde, wenn seine Punktzahl niedriger ist als das, was bereits vorhanden ist). Der Pool wird anfänglich mit zufällig generierten Boards gefüllt, wonach er während des gesamten Programmablaufs eine konstante Größe behält.

Die Hauptschleife des Programms besteht darin, eine zufällige Karte aus dem Pool auszuwählen und zu ändern, die Punktzahl dieser neuen Karte zu bestimmen und sie dann in den Pool zu legen (wenn sie gut genug punktet). Auf diese Weise werden die Highscoring-Boards ständig weiterentwickelt. Die Hauptaktivität besteht darin, schrittweise, inkrementelle Verbesserungen vorzunehmen. Die Größe des Pools ermöglicht es dem Programm jedoch auch, Verbesserungen in mehreren Schritten zu finden, die die Punktzahl eines Boards vorübergehend verschlechtern, bevor es sie verbessern kann.

Typischerweise findet dieses Programm ein gutes lokales Maximum ziemlich schnell, wonach vermutlich ein besseres Maximum zu weit entfernt ist, um gefunden zu werden. Und so hat es wieder wenig Sinn, das Programm länger als 10 Sekunden laufen zu lassen. Dies könnte verbessert werden, indem z. B. das Programm diese Situation erkennt und eine neue Suche mit einem neuen Kandidatenpool startet. Dies würde jedoch nur einen geringfügigen Anstieg bedeuten. Eine geeignete heuristische Suchtechnik wäre wahrscheinlich eine bessere Möglichkeit zur Erkundung.

(Randnotiz: Ich sah, dass diese Version ungefähr 5.000 Boards / Sek. Erzeugte. Da die erste Version normalerweise 20.000 Boards / Sek. Erzeugte, war ich anfangs besorgt. Als ich ein Profil erstellte, stellte ich jedoch fest, dass die zusätzliche Zeit für die Verwaltung der Wortliste aufgewendet wurde. Mit anderen Worten, es war ganz darauf zurückzuführen, dass das Programm so viel mehr Wörter pro Board gefunden hat. Aus diesem Grund habe ich überlegt, den Code zu ändern, um die Wortliste zu verwalten, aber da dieses Programm nur 10 seiner zugewiesenen 120 Sekunden verwendet, z Eine Optimierung wäre sehr verfrüht.)

Anmerkungen zu Version 1 (2. Juni)

Dies ist eine sehr, sehr einfache Lösung. Alles, was es macht, sind zufällige Bretter, und nach 10 Sekunden gibt es das mit der höchsten Punktzahl aus. (Ich habe standardmäßig 10 Sekunden verwendet, da die zusätzlichen 110 Sekunden, die in der Problemspezifikation angegeben sind, in der Regel nicht die endgültige Lösung verbessern, auf die es sich zu warten lohnt.) Es ist also extrem dumm. Es verfügt jedoch über die gesamte Infrastruktur, um einen guten Ausgangspunkt für eine intelligentere Suche zu schaffen, und wenn jemand es vor Ablauf der Frist nutzen möchte, ermutige ich ihn, dies zu tun.

Das Programm beginnt mit dem Einlesen des Wörterbuchs in eine Baumstruktur. (Das Formular ist nicht ganz so optimiert wie es sein könnte, aber es ist mehr als gut genug für diese Zwecke.) Nach einer weiteren grundlegenden Initialisierung werden Bretter generiert und bewertet. Das Programm untersucht ungefähr 20.000 Boards pro Sekunde auf meiner Maschine, und nach ungefähr 200.000 Boards beginnt der Zufallsansatz, trocken zu laufen.

Da zu jedem Zeitpunkt nur eine Karte tatsächlich ausgewertet wird, werden die Scoring-Daten in globalen Variablen gespeichert. Dadurch kann ich die Menge der konstanten Daten minimieren, die als Argumente an die rekursiven Funktionen übergeben werden müssen. (Ich bin sicher, dass dies einigen Leuten Bienenstöcke geben wird, und ich entschuldige mich bei ihnen.) Die Wortliste wird als binärer Suchbaum gespeichert. Jedes gefundene Wort muss in der Wortliste nachgeschlagen werden, damit doppelte Wörter nicht doppelt gezählt werden. Die Wortliste wird jedoch nur während des Auswertungsprozesses benötigt, sodass sie gelöscht wird, nachdem die Punktzahl gefunden wurde. Am Ende des Programms muss die gewählte Tafel also noch einmal gewertet werden, damit die Wortliste ausgedruckt werden kann.

Unterhaltsame Tatsache: Die durchschnittliche Punktzahl für ein zufällig generiertes Boggle-Board english.0beträgt 61,7 Punkte.


Natürlich muss ich meine eigene Effizienz verbessern. :-)
Gaffi

Mein genetischer Ansatz erreicht ungefähr 700-800 Punkte und generiert ungefähr 200.000 Boards. Sie machen also bei der Produktion der nächsten Generation eindeutig etwas Besseres als ich.
Peter Taylor

Meine eigene Baumstruktur wurde bisher nur für die Master-Wortliste implementiert. Sie funktioniert zwar und ermöglicht es mir, Boards zu validieren, sie blockiert jedoch meinen Systemspeicher (sie bleibt aktiv so lange zurück, bis der Prozess erzwungen wird) vorzeitig kündigen). Das ist sicherlich meine eigene Schuld, aber ich arbeite daran! Edit: Bereits behoben! ;-)
Gaffi

@PeterTaylor Ich dachte darüber nach, einen genetischen Algorithmus auszuprobieren, konnte mir aber keinen plausiblen Mechanismus für die Kombination zweier Boards vorstellen. Wie machst du das Wählen Sie die übergeordneten Elemente zufällig für jeden Steckplatz auf der Platine aus?
Brotkasten

Ich habe den Brettstatus in die Permutation der Würfel und die auf den Würfeln angezeigten Gesichter aufgeteilt. Für Permutations-Crossover verwende ich "Order Crossover 1" von cs.colostate.edu/~genitor/1995/permutations.pdf und für Face Crossover mache ich das Offensichtliche. Aber ich habe eine Idee für einen völlig anderen Ansatz, für dessen Umsetzung ich Zeit finden muss.
Peter Taylor

3

VBA (Durchschnitt derzeit im Bereich von 80-110 Punkten, unvollendet)

Hier ist mein Arbeitsprozess, aber er ist alles andere als der bestmögliche. Mein absoluter Bestwert, den ich nach vielen Testläufen auf einem Board gefunden habe, liegt bei etwa 120. Es muss noch etwas besser aufgeräumt werden, und ich bin mir sicher, dass es an mehreren Stellen mehr Effizienz gibt.

  • 2012.05.09:
    • Ursprüngliche Buchung
  • 2012.05.10 - 2012.05.18:
    • Der Bewertungsalgorithmus wurde verbessert
    • Verbesserte die Pfadfindungslogik
  • 2012.06.07 - 2012.06.12 :
    • Reduziertes Wortlimit von 8 auf 6. Ermöglicht mehr Bretter mit kleineren Wörtern. Die durchschnittliche Punktzahl scheint sich leicht verbessert zu haben. (10-15 oder so überprüfte Bretter pro Lauf gegen 1 bis 2)
    • Nach dem Vorschlag von breadbox habe ich eine Baumstruktur für die Wortliste erstellt. Dies beschleunigt die Back-End-Überprüfung der Wörter auf einer Tafel erheblich.
    • Ich habe mit der Änderung der maximalen Wortgröße (Geschwindigkeit vs. Punktzahl) gespielt und noch nicht entschieden, ob 5 oder 6 eine bessere Option für mich ist. 6 führt zu 100-120 überprüften Brettern insgesamt, während 5 zu 500-1000 Ergebnissen führen (beide liegen immer noch weit unter den anderen bisher bereitgestellten Beispielen).
    • Problem : Nach vielen aufeinanderfolgenden Durchläufen wird der Prozess langsamer, sodass immer noch Speicherplatz zur Verfügung steht.

Dies sieht für einige von Ihnen wahrscheinlich schrecklich aus, aber wie gesagt, WIP. Ich bin sehr offen für konstruktive Kritik! Entschuldigung für den sehr langen Körper ...


Würfelklassen-Modul :

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

Baumklassenmodul :

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

TreeNode-Klassenmodul :

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

Hauptmodul :

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

2
Ich habe den Code nicht durchgesehen, aber 50 Punkte sind lächerlich niedrig. Ich habe zufällig generierte Bretter mit Punktzahlen über 1000 gespielt (unter Verwendung von SOWPODS - die gelieferte Wortliste ist möglicherweise weniger umfangreich). Vielleicht möchten Sie nach einem Vorzeichenfehler suchen!
Peter Taylor

@ PeterTaylor Danke für den Vorschlag. Ich weiß, dass die Punktzahl viel zu niedrig ist, und ich weiß, dass ein Teil des Problems in der Tatsache liegt, dass ich sehe, dass offensichtliche Worte übersehen werden ...
Gaffi,

@PeterTaylor Außerdem gebe ich regelmäßig meine Fortschritte bekannt, anstatt auf mein Endprodukt zu warten, da es noch niemand anders versucht hat. Ich möchte die Frage etwas am Leben erhalten, bis dies geschieht.
Gaffi

Ich sollte auch beachten, dass dies nicht auf der schnellsten Maschine da draußen ausgeführt wird, also hilft das auch nicht.
Gaffi

1
@Gaffi 10 Sekunden, um die Punktzahl zu berechnen? Das sind 9.999 Sekunden zu lang. Sie müssen Ihren Code überdenken. Wenn Sie sich weigern, Ihre Wortliste in einen Baum zu verwandeln, tun Sie dies zumindest: Erstellen Sie eine Liste (Hash-Tabelle, was auch immer) aller aus zwei Buchstaben bestehenden Präfixe. Wenn Sie dann beginnen, einem Pfad auf der Tafel zu folgen, überspringen Sie den gesamten Teilbaum der möglichen Pfade, wenn die ersten beiden Buchstaben nicht in der Liste enthalten sind. Auch hier ist es am besten, den vollständigen Baum zu erstellen, aber die aus zwei Buchstaben bestehende Präfixliste hilft und ist sehr billig zu erstellen.
Brotkasten

2

Schneller Blick auf die Größe des Suchraums.

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

Reduzierung, um die Wiederholung auf jedem Würfel auszuschließen.

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

@breadbox speichert das Wörterbuch als Hash Table O (1) -Prüfung.

BEARBEITEN

Bestes Board (das ich bisher gesehen habe)

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

Besorgen Sie mir eine Maschine mit so viel RAM, und wir werden uns unterhalten.
Brotkasten

Sie müssen die Würfel-Permutationen durch 8 teilen, um die Symmetrien des Quadrats zu berücksichtigen. Wie kommt man auch zu (4 ^ 4) (5 ^ 6) (6 ^ 5)? Ich mache es (4 ^ 3) (5 ^ 7) (6 ^ 6), für einen gesamten Suchraum von etwas mehr als 2 ^ 79.
Peter Taylor

@ Peter Taylor: Du hast recht. Ich muss eins zu viele gelöscht haben, wenn die einzigartigen Gesichter tun. Ich denke, wir können uns darauf einigen, dass es 83 einzigartige Gesichter gibt (ohne Wiederholungen über den Würfel). Wählen Sie eine 16 ohne Wiederholungen. '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 'Ungefähr: 1.082 x (10 ^ 30) ==> ~ 2 ^ 100 Was Wie auch immer, es ist eine große Zahl.
Adam Speight

2
@AdamSpeight Ich bin ursprünglich davon ausgegangen, dass Ihr Kommentar zum Speichern des Wörterbuchs als Hash-Tabelle nur ein Scherz war, und habe ihn daher im Grunde ignoriert. Entschuldigen Sie. Eine angemessene Antwort wäre: Tatsächlich ist eine Hash-Tabelle eine miese Datenstruktur für dieses Problem. Es kann nur die Frage "Ist X ein gültiges Wort?" Beantworten, sodass Sie alle möglichen Zeichenfolgen erstellen müssen, um die Wörter zu finden. In einer DAWG können Sie fragen: " Ist X ein Präfix eines gültigen Wortes? Wenn ja, welche Buchstaben können darauf folgen?" Auf diese Weise können Sie den Suchbereich auf einen winzigen Bruchteil seiner Gesamtgröße beschränken.
Brotkasten

Hashtable ist schrecklich, da es verhindert, dass Sie Wortfragmente aussortieren, die niemals zu vollständigen Wörtern werden (dicttree.ceiling (Fragment) .startsWith (Fragment)). Obwohl jedes Boggle Board viele Millionen potenzieller Wörter enthält, können Sie einen großen Teil davon wegwerfen, nachdem 2-3 Buchstaben aneinander gereiht wurden. Das Durchlaufen von Bäumen ist langsamer als das Nachschlagen von Hashtabellen. Mit dem Baum können Sie jedoch mehr als 99 Prozent der Arbeit durch Zurückverfolgen umgehen.
Jim W

1

Mein Eintrag ist hier auf Dream.In.Code ~ 30ms pro Board-Suche (auf einer 2-Core-Maschine sollte mit mehr Kernen schneller sein)


Suchen Sie immer noch danach, aber bei Ihrem ersten Link auf dieser Seite fehlt der :in http://. ;-)
Gaffi

Sehr schön. Ich werde versuchen, das als Lernerfahrung für mich selbst zu stehlen. .NETzu VBAist nicht zu schwer.
Gaffi,

Würde es Ihnen etwas ausmachen, die Antwort so zu aktualisieren, dass sie Ihre durchschnittliche Punktzahl beim Ausführen der ISPELL-Liste enthält (nicht SOWPODS)? Das ist Teil der Herausforderung, und ich bin gespannt, wie sich Ihre Ergebnisse mit denen von Brotkästen vergleichen lassen.
Gaffi
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.