Boggle Board con il miglior punteggio


16

Ero interessato a vedere le risposte a questa domanda (ora defunta) , ma non è mai stato corretto / migliorato.

Dato un set di dadi Boggle a 6 facce (configurazione rubata da questa domanda ), determinare in due minuti di tempo di elaborazione quale configurazione della scheda consentirà il punteggio più alto possibile. (ovvero quali dadi in quale posizione con quale lato verso l'alto consente il più grande pool di parole con punteggio?)


OBBIETTIVO

  • Il codice deve essere eseguito per non più di 2 minuti (120 secondi). A quel punto, dovrebbe interrompere automaticamente l'esecuzione e stampare i risultati.

  • Il punteggio finale della sfida sarà il punteggio medio Boggle di 5 esecuzioni del programma.

    • In caso di pareggio, il vincitore sarà qualunque algoritmo abbia trovato più parole.
    • Nel caso in cui vi sia ancora un pareggio, il vincitore sarà qualunque algoritmo trovato parole più lunghe (8+) .

REGOLE / VINCOLI

  • Questa è una sfida al codice; la lunghezza del codice è irrilevante.

  • Fare riferimento a questo collegamento per un elenco di parole (elenco di utilizzo ISPELL "english.0": l'elenco di SCOWL non contiene alcune parole piuttosto comuni).

    • Questo elenco può essere riferito / importato / letto nel tuo codice come desideri.
    • ^([a-pr-z]|qu){3,16}$Verranno conteggiate solo le parole corrispondenti al regex . (Come unità devono essere utilizzate solo lettere minuscole, 3-16 caratteri, qu).
  • Le parole si formano collegando le lettere adiacenti (orizzontale, verticale e diagonale) per compitare le parole nell'ordine corretto, senza usare un singolo dado più di una volta in una sola parola.

    • Le parole devono contenere almeno 3 lettere; parole più brevi non guadagneranno punti.
    • Le lettere duplicate sono accettabili, ma non i dadi.
    • Non sono consentite parole che attraversano i bordi / attraversano da un lato del tabellone all'altro.
  • Il punteggio finale di Boggle ( non sfida ) è il totale dei valori dei punti per tutte le parole trovate.

    • Il valore del punto assegnato per ogni parola si basa sulla lunghezza della parola. (vedi sotto)
    • Le normali regole Boggle deducono / scartano le parole trovate da un altro giocatore. Supponi che qui non siano coinvolti altri giocatori e tutte le parole trovate contano per il punteggio totale.
    • Tuttavia, le parole trovate più di una volta nella stessa griglia devono essere contate una sola volta.
  • La tua funzione / programma deve TROVARE la disposizione ottimale; semplicemente codificare un elenco predeterminato non lo farà.

  • L'output dovrebbe essere una griglia 4x4 del tabellone di gioco ideale, un elenco di tutte le parole trovate per quel tabellone e il punteggio Boggle per abbinare quelle parole.


CONFIGURAZIONE DI DIE

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

TABELLA DI PUNTEGGIO STANDARD 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.

USCITA DI ESEMPIO

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

CUT
THE
LUCK
HEX
....

140 points

Se sono necessari ulteriori chiarimenti, si prega di chiedere!


2
Preferirei avere un dizionario fornito per standardizzare l'obiettivo. Nota anche che questa non è una nuova idea, come rivelerà una semplice ricerca su Google :) Il punteggio più alto che ho visto è 4527( 1414parole in totale), trovato qui: ai.stanford.edu/~chuongdo/boggle/index.html
mellamokb,

4
Il programma è necessario per terminare questo secolo?
Peter Taylor,

1
@GlitchMr In inglese, in genere Q viene usato solo con account U. Boggle mettendo le due lettere sullo stesso dado di un'unità.
Gaffi,

1
La specifica dell'elenco di parole non è chiara. Stai contando solo le parole elencate in inglese.0 in minuscolo? (Le regole del gioco di parole standard escludono abbreviazioni / inizialismi e nomi propri).
Peter Taylor,

1
Stavo pensando a regex ^([a-pr-z]|qu){3,16}$(che escluderebbe erroneamente le parole di 3 lettere con qu, ma non ce ne sono).
Peter Taylor,

Risposte:


9

C, con una media di 500+ 1500 1750 punti

Questo è un miglioramento relativamente minore rispetto alla versione 2 (vedi sotto per le note sulle versioni precedenti). Ci sono due parti. Primo: invece di selezionare casualmente le schede dal pool, il programma ora scorre su tutte le board del pool, usandole a turno prima di tornare in cima al pool e ripetere. (Poiché il pool viene modificato mentre si verifica questa iterazione, ci saranno comunque board che vengono scelte due volte di seguito, o peggio, ma questo non è un problema serio.) La seconda modifica è che il programma ora tiene traccia quando il pool cambia e se il programma dura troppo a lungo senza migliorare il contenuto del pool, determina che la ricerca si è "bloccata", svuota il pool e ricomincia da capo con una nuova ricerca. Continua a farlo fino a quando non sono trascorsi i due minuti.

Inizialmente avevo pensato che avrei impiegato una sorta di ricerca euristica per andare oltre il raggio di 1500 punti. Il commento di @ mellamokb su una tavola da 4527 punti mi ha portato a ritenere che ci fosse un ampio margine di miglioramento. Tuttavia, stiamo usando un elenco di parole relativamente piccolo. La tavola da 4527 punti segnava usando YAWL, che è la lista di parole più inclusiva là fuori - è persino più grande della lista di parole ufficiale degli Scrabble degli Stati Uniti. Con questo in mente, ho riesaminato le schede trovate dal mio programma e ho notato che sembrava esserci un numero limitato di schede oltre il 1700 circa. Quindi, per esempio, ho avuto più tiri che avevano scoperto una tavola segnando 1726, ma era sempre la stessa identica tavola che veniva trovata (ignorando rotazioni e riflessioni).

Come altro test, ho eseguito il mio programma usando YAWL come dizionario e ho trovato la scheda a 4527 punti dopo circa una dozzina di esecuzioni. Detto questo, sto ipotizzando che il mio programma sia già al limite superiore dello spazio di ricerca, e quindi la riscrittura che stavo pianificando introdurrebbe ulteriore complessità per un guadagno molto piccolo.

Ecco la mia lista delle cinque schede con il punteggio più alto che il mio programma ha trovato usando la lista di english.0parole:

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

La mia convinzione è che la "scheda grep" del 1772 (come ho preso per chiamarla), con 531 parole, è la scheda con il punteggio più alto possibile con questo elenco di parole. Oltre il 50% della durata di due minuti del mio programma termina con questa scheda. Ho anche lasciato il mio programma in esecuzione durante la notte senza trovare nulla di meglio. Quindi, se esiste una tavola con punteggi più alti, probabilmente dovrebbe avere qualche aspetto che sconfigge la tecnica di ricerca del programma. Una scheda in cui ogni possibile piccola modifica al layout provoca un enorme calo del punteggio totale, ad esempio, potrebbe non essere mai scoperto dal mio programma. La mia impressione è che è molto improbabile che esista una tavola del genere.

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

Note per la versione 2 (9 giugno)

Ecco un modo per utilizzare la versione iniziale del mio codice come punto di partenza. Le modifiche a questa versione consistono in meno di 100 righe, ma triplicano il punteggio medio del gioco.

In questa versione, il programma mantiene un "pool" di candidati, composto dalle N commissioni con il punteggio più alto che il programma ha generato finora. Ogni volta che viene generata una nuova scheda, questa viene aggiunta al pool e viene rimossa la scheda con il punteggio più basso nel pool (che potrebbe benissimo essere la board appena aggiunta, se il suo punteggio è inferiore a quello che è già presente). Il pool viene inizialmente riempito con schede generate casualmente, dopo di che mantiene una dimensione costante durante l'esecuzione del programma.

Il ciclo principale del programma consiste nel selezionare una tavola a caso dal pool e modificarla, determinando il punteggio di questa nuova tavola e poi inserendola nel pool (se segna abbastanza bene). In questo modo il programma perfeziona continuamente le schede ad alto punteggio. L'attività principale è apportare miglioramenti graduali e incrementali, ma la dimensione del pool consente anche al programma di trovare miglioramenti in più passaggi che peggiorano temporaneamente il punteggio di una scheda prima che possa migliorarla.

In genere questo programma trova un buon massimo locale piuttosto rapidamente, dopo il quale presumibilmente un massimo migliore è troppo distante per essere trovato. E quindi, ancora una volta, non ha senso eseguire il programma per più di 10 secondi. Ciò potrebbe essere migliorato, ad esempio facendo in modo che il programma rilevi questa situazione e inizi una nuova ricerca con un nuovo pool di candidati. Tuttavia, ciò comporterebbe solo un aumento marginale. Una corretta tecnica di ricerca euristica sarebbe probabilmente una migliore via di esplorazione.

(Nota a margine: ho visto che questa versione generava circa 5k board / sec. Dato che la prima versione in genere faceva 20k board / sec, inizialmente ero preoccupato. Alla profilazione, tuttavia, ho scoperto che il tempo extra è stato impiegato nella gestione dell'elenco di parole. In altre parole, è stato interamente dovuto al fatto che il programma ha trovato molte più parole per scheda. Alla luce di ciò, ho considerato di cambiare il codice per gestire l'elenco di parole, ma dato che questo programma utilizza solo 10 dei 120 secondi assegnati, ad esempio un'ottimizzazione sarebbe molto prematura.)

Note per la versione 1 (2 giugno)

Questa è una soluzione molto, molto semplice. Tutto ciò che genera schede casuali, e dopo 10 secondi emette quello con il punteggio più alto. (Ho impostato automaticamente 10 secondi perché i 110 secondi extra consentiti dalla specifica del problema in genere non migliorano la soluzione finale trovata abbastanza per cui vale la pena aspettare.) Quindi è estremamente stupida. Tuttavia, ha tutte le infrastrutture per creare un buon punto di partenza per una ricerca più intelligente e, se qualcuno desidera utilizzarlo prima della scadenza, li incoraggio a farlo.

Il programma inizia leggendo il dizionario in una struttura ad albero. (Il modulo non è così ottimizzato come potrebbe essere, ma è più che sufficiente per questi scopi.) Dopo qualche altra inizializzazione di base, inizia quindi a generare schede e assegnarle un punteggio. Il programma esamina circa 20.000 schede al secondo sulla mia macchina e dopo circa 200.000 schede l'approccio casuale inizia a funzionare a secco.

Dal momento che solo una scheda viene attualmente valutata in un dato momento, i dati del punteggio vengono archiviati in variabili globali. Ciò mi consente di ridurre al minimo la quantità di dati costanti che devono essere passati come argomenti alle funzioni ricorsive. (Sono sicuro che questo darà ad alcuni alveari, e a loro mi scuso.) La lista di parole è memorizzata come un albero di ricerca binario. Ogni parola trovata deve essere cercata nell'elenco delle parole, in modo che le parole duplicate non vengano conteggiate due volte. L'elenco di parole è necessario solo durante il processo di evaulazione, quindi viene scartato dopo aver trovato il punteggio. Pertanto, alla fine del programma, la tavola scelta deve essere nuovamente classificata, in modo che l'elenco di parole possa essere stampato.

Curiosità: il punteggio medio per una tavola Boggle generata casualmente, come segnato da english.0, è di 61,7 punti.


Ovviamente, devo migliorare la mia efficienza. :-)
Gaffi,

Il mio approccio genetico ottiene circa 700-800 punti generando circa 200k schede, quindi stai chiaramente facendo qualcosa di molto meglio di me nel modo in cui produci la prossima generazione.
Peter Taylor,

La mia struttura ad albero, finora implementata solo per l'elenco di parole principale, mentre funziona e mi consente di convalidare le schede, impantana la memoria del mio sistema (attivamente rallenta al punto che ci vuole un tempo considerevole per forzare il processo per terminare presto). Questa è sicuramente colpa mia, ma ci sto lavorando! Modifica: già risolto! ;-)
Gaffi,

@PeterTaylor Ho pensato di provare un algoritmo genetico, ma non riuscivo a pensare a un meccanismo plausibile per combinare due schede. Come lo stai facendo? Stai selezionando il genitore in modo casuale per ogni slot sulla scheda?
breadbox

Ho diviso lo stato del tabellone nella permutazione dei dadi e nelle facce mostrate sui dadi. Per il crossover di permutazione uso "ordina crossover 1" da cs.colostate.edu/~genitor/1995/permutations.pdf e per il crossover facciale faccio l'ovvio. Ma ho un'idea per un approccio totalmente diverso che devo trovare il tempo di attuare.
Peter Taylor,

3

VBA (media attualmente compresa tra 80 e 110 punti, incompiuta)

Ecco il mio processo di lavoro, ma è tutt'altro che il migliore possibile; il mio miglior punteggio assoluto trovato su qualsiasi tavola dopo molte prove è di circa 120. C'è ancora bisogno di una migliore pulizia generale e sono sicuro che ci sono più efficienze da guadagnare in un certo numero di posti.

  • 2012/05/09:
    • Pubblicazione originale
  • 2012.05.10 - 2012.05.18:
    • Migliorato l'algoritmo di punteggio
    • Migliorata la logica del pathfinding
  • 2012.06.07 - 2012.06.12 :
    • Limite di parole ridotto a 6 da 8. Consente più schede con parole più piccole. Sembra aver fatto un leggero miglioramento nel punteggio medio. (10-15 schede verificate per sequenza rispetto a 1 a 2)
    • Seguendo il suggerimento di breadbox, ho creato una struttura ad albero per ospitare l'elenco di parole. Ciò accelera notevolmente il controllo back-end delle parole su una scheda.
    • Ho giocato cambiando la dimensione massima delle parole (velocità vs. punteggio) e non ho ancora deciso se 5 o 6 sono le opzioni migliori per me. 6 risultati in 100-120 schede totali controllate, mentre 5 risultati in 500-1000 (entrambi i quali sono ancora molto al di sotto degli altri esempi forniti finora).
    • Problema : dopo molte esecuzioni successive, il processo inizia a rallentare, quindi c'è ancora della memoria da gestire.

Questo probabilmente sembra orribile per alcuni di voi, ma come ho detto, WIP. Sono molto aperto alle critiche costruttive ! Scusa per il corpo molto lungo ...


Modulo classe dadi :

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

Modulo di classe dell'albero :

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

Modulo 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

Modulo principale :

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
Non ho controllato il codice, ma 50 punti sono ridicolmente bassi. Ho giocato a schede generate casualmente con punteggi superiori a 1000 (usando SOWPODS - l'elenco delle parole fornito potrebbe essere meno esteso). Potresti voler controllare un errore di segno!
Peter Taylor,

@PeterTaylor Grazie per il suggerimento. So che il punteggio è troppo basso e so che parte del problema sta nel fatto che posso vedere le parole ovvie che mi mancano ...
Gaffi,

@PeterTaylor Inoltre, per la cronaca, continuo a pubblicare i miei progressi, piuttosto che aspettare il mio prodotto finale, dal momento che nessun altro lo ha ancora preso. Vorrei mantenere la domanda in qualche modo viva fino a quando ciò accadrà.
Gaffi,

Dovrei anche notare che questo non viene eseguito sulla macchina più veloce là fuori, quindi anche questo non aiuta.
Gaffi,

1
@Gaffi 10 secondi per calcolare il punteggio? Sono 9999 secondi di troppo. Devi ripensare il tuo codice. Se rifiuti di trasformare la tua lista di parole in un albero, almeno fallo: Crea un elenco (hashtable, qualunque cosa) di tutti i prefissi di due lettere. Quindi quando inizi a seguire un percorso sulla lavagna, se le prime due lettere non sono nell'elenco, salta l'intera sottostruttura di possibili percorsi. Ancora una volta, la costruzione dell'intero albero è la cosa migliore, ma l'elenco dei prefissi di due lettere aiuterà ed è molto economico da realizzare.
breadbox

2

Occhiata veloce alle dimensioni dello spazio di ricerca.

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

Riducendo per escludere la ripetizione su ogni dado.

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

@breadbox memorizza il dizionario come controllo Hash Table O (1).

MODIFICARE

Miglior consiglio (ho assistito finora)

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

Portami una macchina con così tanta RAM e ne parliamo.
breadbox

Devi dividere le permutazioni dei dadi per 8 per tenere conto delle simmetrie del quadrato. Inoltre, come si ottiene (4 ^ 4) (5 ^ 6) (6 ^ 5)? Lo faccio (4 ^ 3) (5 ^ 7) (6 ^ 6), per uno spazio di ricerca totale di poco più di 2 ^ 79.
Peter Taylor,

@Peter Taylor: hai ragione. Devo aver cancellato uno a molti, quando fanno le facce uniche. Penso che possiamo essere d'accordo sul fatto che ci siano 83 volti unici (escluse le ripetizioni attraverso i dadi). Scegli qualsiasi 16 senza ripetizioni. '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 'Circa: 1.082 x (10 ^ 30) ==> ~ 2 ^ 100 Cosa mai lo è, è un gran numero.
Adam Speight,

2
@AdamSpeight Inizialmente pensavo che il tuo commento sull'archiviazione del dizionario come hashtable fosse solo uno scherzo, e quindi l'ho praticamente ignorato. Mie scuse. Una risposta adeguata sarebbe: In realtà, una tabella hash è una struttura di dati scadente per questo problema. Può solo rispondere alla domanda "X è una parola valida?", Quindi devi costruire tutte le stringhe possibili per trovare le parole. Un DAWG ti permette di chiedere "X è un prefisso di una parola valida? In tal caso, quali lettere possono seguirla?" Ciò ti consente di potare lo spazio di ricerca su una minuscola frazione della sua dimensione totale.
breadbox

Hashtable è terribile in quanto ti impedisce di eliminare i frammenti di parole che non diventeranno mai parole complete (dicttree.ceiling (frammento). StarttsWith (frammento)). Sebbene una determinata boggle board abbia molti milioni di parole potenziali, puoi buttarne via una gran parte dopo che 2-3 lettere sono state messe insieme. L'attraversamento degli alberi è più lento della ricerca con hash, ma l'albero consente di evitare il 99 percento del lavoro attraverso il backtracking.
Jim W,

1

La mia voce è qui su Dream.In.Code ~ 30ms per una ricerca della scheda (su una macchina a 2 core, dovrebbe essere più veloce con più core)


Lo sto ancora esaminando, ma al tuo primo link su quella pagina manca il :in http://. ;-)
Gaffi,

Molto bella. Proverò a rubarmelo come esperienza di apprendimento. .NETa VBAnon è troppo difficile.
Gaffi,

Ti dispiacerebbe aggiornare la risposta per includere il tuo punteggio medio quando esegui l'elenco ISPELL (non SOWPODS)? Questo fa parte della sfida e sono interessato a vedere come i tuoi risultati si confrontano con quelli di breadbox.
Gaffi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.