Papan Boggle Penilaian Terbaik

16

Saya tertarik melihat jawaban untuk pertanyaan ini (sekarang sudah tidak ada) , tetapi belum pernah diperbaiki / diperbaiki.

Diberikan satu set dadu Boggle 6-sisi (konfigurasi dicuri dari pertanyaan ini ), tentukan dalam dua menit waktu pemrosesan konfigurasi papan mana yang akan memungkinkan untuk skor setinggi mungkin. (yaitu, dadu mana di lokasi mana dengan sisi atas memungkinkan untuk kumpulan kata skor terbanyak?)


OBJEKTIF

  • Kode Anda harus berjalan tidak lebih dari 2 menit (120 detik). Pada saat itu, ia harus secara otomatis berhenti berjalan dan mencetak hasilnya.

  • Skor tantangan final akan menjadi skor Boggle rata-rata 5 kali program.

    • Jika terjadi seri, pemenangnya akan algoritma mana saja yang menemukan lebih banyak kata.
    • Jika masih ada seri, pemenangnya adalah algoritma mana saja yang lebih panjang (8+) kata.

ATURAN / KENDALA

  • Ini adalah tantangan kode; panjang kode tidak relevan.

  • Silakan merujuk ke tautan ini untuk daftar kata (gunakan ISPELL "english.0"daftar - daftar SCOWL tidak memiliki beberapa kata yang cukup umum).

    • Daftar ini dapat dirujuk ke / diimpor / dibaca dalam kode Anda dengan cara apa pun yang Anda inginkan.
    • Hanya kata-kata yang cocok dengan regex yang ^([a-pr-z]|qu){3,16}$akan dihitung. (Hanya huruf kecil, 3-16 karakter, qu harus digunakan sebagai satu unit.)
  • Kata-kata dibentuk dengan menghubungkan huruf-huruf yang berdekatan (horizontal, vertikal, dan diagonal) untuk mengeja kata-kata dalam urutan yang benar, tanpa menggunakan die tunggal lebih dari sekali dalam satu kata tunggal.

    • Kata-kata harus 3 huruf atau lebih; kata yang lebih pendek tidak akan menghasilkan poin.
    • Surat duplikat dapat diterima, hanya saja tidak ada dadu.
    • Kata-kata yang merentang tepi / silang dari satu sisi papan ke yang lain tidak diperbolehkan.
  • Skor Boggle akhir ( bukan tantangan ) adalah total nilai poin untuk semua kata yang ditemukan.

    • Nilai poin yang ditetapkan untuk setiap kata didasarkan pada panjang kata. (Lihat di bawah)
    • Aturan Boggle normal akan mengurangi / mengurangi kata-kata yang ditemukan oleh pemain lain. Asumsikan di sini tidak ada pemain lain yang terlibat dan semua kata yang ditemukan diperhitungkan dalam skor total.
    • Namun, kata-kata yang ditemukan lebih dari satu kali di kotak yang sama hanya boleh dihitung satu kali.
  • Fungsi / program Anda harus MENCARI pengaturan yang optimal; hanya mengkodekan daftar yang telah ditentukan tidak akan berhasil.

  • Output Anda harus berupa kotak 4x4 dari papan permainan ideal Anda, daftar semua kata yang ditemukan untuk papan itu, dan skor Boggle untuk mencocokkan kata-kata itu.


KONFIGURASI MATI

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

MEJA SKOR BOGGLE STANDAR

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.

CONTOH OUTPUT

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

CUT
THE
LUCK
HEX
....

140 points

Jika diperlukan klarifikasi lebih lanjut, silakan tanyakan!

Gaffi
sumber
2
Saya lebih suka memiliki kamus yang disediakan untuk membakukan tujuan. Perhatikan juga bahwa ini bukan ide baru, karena pencarian google sederhana akan mengungkapkan :) Skor tertinggi yang saya lihat adalah 4527( 1414kata-kata total), ditemukan di sini: ai.stanford.edu/ ~ chuongdo
mellamokb
4
Apakah program ini diperlukan untuk mengakhiri abad ini?
Peter Taylor
1
@GlitchMr Dalam bahasa Inggris, Q biasanya hanya digunakan dengan akun U. Boggle untuk ini dengan meletakkan dua huruf pada dadu yang sama sebagai satu unit.
Gaffi
1
Spesifikasi daftar kata tidak jelas. Apakah Anda hanya menghitung kata-kata yang tercantum dalam bahasa Inggris.0 dalam huruf kecil? (Aturan permainan kata standar mengecualikan singkatan / inisialisasi dan kata benda yang tepat).
Peter Taylor
1
Saya sedang berpikir regex ^([a-pr-z]|qu){3,16}$(yang salah akan mengecualikan kata-kata 3 huruf dengan qu, tetapi tidak ada).
Peter Taylor

Jawaban:

9

C, rata-rata 500+ 1500 1750 poin

Ini adalah peningkatan yang relatif kecil dibandingkan versi 2 (lihat di bawah untuk catatan tentang versi sebelumnya). Ada dua bagian. Pertama: Alih-alih memilih papan secara acak dari kolam, program sekarang beralih ke setiap papan di kolam, menggunakan masing-masing secara bergiliran sebelum kembali ke atas kolam dan mengulangi. (Karena pool sedang dimodifikasi saat iterasi ini terjadi, masih akan ada papan yang dipilih dua kali berturut-turut, atau lebih buruk, tapi ini bukan masalah serius.) Perubahan kedua adalah bahwa program sekarang melacak ketika pool berubah , dan jika program berjalan terlalu lama tanpa memperbaiki konten kumpulan, itu menentukan bahwa pencarian telah "terhenti", mengosongkan kumpulan, dan memulai kembali dengan pencarian baru. Terus melakukan ini sampai dua menit habis.

Awalnya saya berpikir bahwa saya akan menggunakan semacam pencarian heuristik untuk melampaui kisaran 1500 poin. Komentar @ mellamokb tentang board 4527-point membuat saya berasumsi bahwa ada banyak ruang untuk perbaikan. Namun, kami menggunakan daftar kata yang relatif kecil. Papan 4527-point dinilai menggunakan YAWL, yang merupakan daftar kata yang paling inklusif di luar sana - bahkan lebih besar dari daftar kata resmi Scrabble AS. Dengan mengingat hal ini, saya memeriksa kembali papan-papan yang telah ditemukan oleh program saya dan memerhatikan bahwa ada set papan terbatas di atas sekitar 1700 atau lebih. Jadi misalnya, saya memiliki beberapa kali menjalankan yang telah menemukan papan skor 1726, tetapi selalu papan yang sama persis yang ditemukan (mengabaikan rotasi dan refleksi).

Sebagai tes lain, saya menjalankan program saya menggunakan YAWL sebagai kamus, dan ia menemukan papan 4527-point setelah sekitar selusin berjalan. Mengingat ini, saya berhipotesis bahwa program saya sudah berada di batas atas ruang pencarian, dan karena itu penulisan ulang yang saya rencanakan akan memperkenalkan kompleksitas ekstra untuk keuntungan yang sangat sedikit.

Berikut adalah daftar lima papan skor tertinggi yang ditemukan program saya menggunakan english.0daftar kata:

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

Keyakinan saya adalah bahwa "papan grep" 1772 (sebagaimana saya sering menyebutnya), dengan 531 kata, adalah papan skor tertinggi yang mungkin dengan daftar kata ini. Lebih dari 50% dari dua menit program saya berakhir dengan board ini. Saya juga membiarkan program saya berjalan semalaman tanpa menemukan sesuatu yang lebih baik. Jadi jika ada papan skor yang lebih tinggi, kemungkinan harus memiliki beberapa aspek yang mengalahkan teknik pencarian program. Papan di mana setiap perubahan kecil yang mungkin pada tata letak menyebabkan penurunan besar dalam skor total, misalnya, mungkin tidak pernah ditemukan oleh program saya. Firasat saya adalah bahwa papan seperti itu sangat tidak mungkin ada.

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

Catatan untuk versi 2 (9 Juni)

Inilah salah satu cara untuk menggunakan versi awal kode saya sebagai titik awal. Perubahan pada versi ini terdiri dari kurang dari 100 baris, tetapi tiga kali lipat skor game rata-rata.

Dalam versi ini, program memiliki "kumpulan" kandidat, yang terdiri dari N papan skor tertinggi yang telah dihasilkan oleh program sejauh ini. Setiap kali papan baru dibuat, ditambahkan ke kolam dan papan skor terendah di kolam itu dihapus (yang mungkin merupakan papan yang baru saja ditambahkan, jika nilainya lebih rendah dari yang sudah ada). Kelompok ini awalnya diisi dengan papan yang dibuat secara acak, setelah itu ukurannya konstan sepanjang program dijalankan.

Lingkaran utama program terdiri dari memilih papan acak dari kumpulan dan mengubahnya, menentukan skor papan baru ini dan kemudian memasukkannya ke dalam kumpulan (jika skornya cukup baik). Dengan cara ini, program ini terus menyempurnakan papan skor tinggi. Kegiatan utama adalah membuat peningkatan bertahap, bertahap, tetapi ukuran kelompok ini juga memungkinkan program menemukan peningkatan multi-langkah yang sementara membuat skor dewan lebih buruk sebelum dapat membuatnya lebih baik.

Biasanya program ini menemukan maksimum lokal yang baik agak cepat, setelah itu mungkin ada maksimum yang lebih baik terlalu jauh untuk ditemukan. Jadi sekali lagi ada gunanya menjalankan program lebih dari 10 detik. Ini dapat ditingkatkan dengan misalnya membuat program mendeteksi situasi ini dan memulai pencarian baru dengan kumpulan kandidat baru. Namun, ini hanya akan menambah sedikit kenaikan. Teknik pencarian heuristik yang tepat kemungkinan akan menjadi jalan eksplorasi yang lebih baik.

(Catatan: Saya melihat bahwa versi ini menghasilkan sekitar 5k papan / detik. Karena versi pertama biasanya 20k papan / detik, saya awalnya khawatir. Namun setelah membuat profil, saya menemukan bahwa waktu ekstra dihabiskan untuk mengelola daftar kata. Dengan kata lain, itu sepenuhnya karena program menemukan lebih banyak kata per papan. Sehubungan dengan ini, saya mempertimbangkan untuk mengubah kode untuk mengelola daftar kata, tetapi mengingat bahwa program ini hanya menggunakan 10 dari 120 jatah yang dialokasikan, seperti optimasi akan sangat prematur.)

Catatan untuk versi 1 (2 Juni)

Ini adalah solusi yang sangat, sangat sederhana. Semua itu menghasilkan papan acak, dan kemudian setelah 10 detik output yang satu dengan skor tertinggi. (Saya default ke 10 detik karena tambahan 110 detik yang diizinkan oleh spesifikasi masalah biasanya tidak meningkatkan solusi akhir yang cukup layak untuk ditunggu.) Jadi itu sangat bodoh. Namun, ia memiliki semua infrastruktur untuk membuat titik awal yang baik untuk pencarian yang lebih cerdas, dan jika ada yang ingin memanfaatkannya sebelum batas waktu, saya mendorong mereka untuk melakukannya.

Program dimulai dengan membaca kamus ke dalam struktur pohon. (Bentuknya tidak seoptimal mungkin, tetapi lebih dari cukup untuk keperluan ini.) Setelah beberapa inisialisasi dasar lainnya, ia kemudian mulai membuat papan dan mencetaknya. Program memeriksa sekitar 20rb papan per detik pada mesin saya, dan setelah sekitar 200rb papan pendekatan acak mulai berjalan kering.

Karena hanya satu papan yang benar-benar dievaluasi pada waktu tertentu, data penilaian disimpan dalam variabel global. Ini memungkinkan saya untuk meminimalkan jumlah data konstan yang harus dilewati sebagai argumen untuk fungsi rekursif. (Saya yakin ini akan memberi beberapa orang gatal-gatal, dan kepada mereka saya minta maaf.) Daftar kata disimpan sebagai pohon pencarian biner. Setiap kata yang ditemukan harus dicari di daftar kata, sehingga kata-kata duplikat tidak dihitung dua kali. Namun, daftar kata hanya diperlukan selama proses evaulasi, sehingga dihapus setelah skor ditemukan. Dengan demikian, pada akhir program, papan yang dipilih harus dicetak lagi, sehingga daftar kata dapat dicetak.

Fakta menyenangkan: Skor rata-rata untuk papan Boggle yang dibuat secara acak, seperti yang dicetak oleh english.0, adalah 61,7 poin.

kotak roti
sumber
Jelas, saya perlu meningkatkan efisiensi saya sendiri. :-)
Gaffi
Pendekatan genetik saya mendapat sekitar 700-800 poin yang menghasilkan sekitar 200 ribu papan, jadi Anda jelas melakukan sesuatu yang jauh lebih baik daripada saya dalam cara Anda menghasilkan generasi berikutnya.
Peter Taylor
Struktur pohon saya sendiri, yang telah diimplementasikan hanya untuk daftar kata utama sejauh ini, sementara itu berfungsi dan memungkinkan saya untuk memvalidasi papan, itu merobohkan memori sistem saya (secara aktif tertinggal sampai pada titik yang membutuhkan banyak waktu untuk memaksa proses untuk mengakhiri lebih awal). Ini pasti salahku sendiri, tapi aku sedang berusaha! Sunting: Sudah diperbaiki! ;-)
Gaffi
@ PeterTaylor Saya berpikir untuk mencoba algoritma genetika, tapi saya tidak bisa memikirkan mekanisme yang masuk akal untuk menggabungkan dua papan. Bagaimana kabarmu? Apakah Anda memilih induk secara acak untuk setiap slot di papan tulis?
kotak roti
Saya membagi status papan menjadi permutasi dadu dan wajah-wajah yang ditampilkan pada dadu. Untuk permutasi crossover saya menggunakan "order crossover 1" dari cs.colostate.edu/~genitor/1995/permutations.pdf dan untuk crossover wajah saya melakukan yang jelas. Tapi saya punya ide untuk pendekatan yang sama sekali berbeda yang perlu saya temukan waktu untuk mengimplementasikannya.
Peter Taylor
3

VBA (rata-rata saat ini berkisar 80-110 poin, belum selesai)

Inilah proses kerja saya, tetapi jauh dari yang terbaik; skor terbaik absolut saya ditemukan di papan mana pun setelah banyak tes berjalan sekitar 120. Masih perlu ada pembersihan umum yang lebih baik dan saya yakin ada lebih banyak efisiensi yang bisa diperoleh di sejumlah tempat.

  • 2012.05.09:
    • Posting asli
  • 2012.05.10 - 2012.05.18:
    • Peningkatan algoritma penilaian
    • Memperbaiki logika pathfinding
  • 2012.06.07 - 2012.06.12 :
    • Batas kata yang dikurangi menjadi 6 dari 8. Memungkinkan papan lebih banyak dengan kata yang lebih kecil. Tampaknya telah membuat sedikit peningkatan dalam skor rata-rata. (10-15 papan diperiksa per dijalankan vs. 1 hingga 2)
    • Mengikuti saran kotak roti, saya telah membuat struktur pohon untuk menampung daftar kata. Ini mempercepat pemeriksaan back-end dari kata-kata di papan tulis secara signifikan.
    • Saya bermain dengan mengubah ukuran kata maksimum (kecepatan vs skor) dan saya belum memutuskan apakah 5 atau 6 adalah pilihan yang lebih baik untuk saya. 6 hasil dalam 100-120 total papan diperiksa, sementara 5 hasil dalam 500-1000 (keduanya masih jauh di bawah contoh lain yang disediakan sejauh ini).
    • Masalah : Setelah banyak berjalan berturut-turut, proses mulai melambat, sehingga masih ada beberapa memori yang harus dikelola.

Ini mungkin terlihat mengerikan bagi sebagian dari Anda, tetapi seperti yang saya katakan, WIP. Saya sangat terbuka terhadap kritik yang membangun ! Maaf untuk tubuh yang sangat panjang ...


Modul Kelas Dadu :

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

Modul Kelas Pohon :

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

Modul Kelas 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

Modul Utama :

Option Explicit

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

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

    StartTime = Now()

    strBoard = vbNullString
    strWords = vbNullString
    iScore = 0

    ReadWordsFileTree

    DiceSetup = Split(conAllSides, ";")

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

    Do While WithinTimeLimit

        Shuffle

        strBoardTemp = vbNullString
        strWordsTemp = vbNullString
        iScoreTemp = 0

        FindWords

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

        End If

    Loop

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

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

End Sub

Sub ShuffleBoard()
Dim i As Integer

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

End Sub

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

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

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

End Sub

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

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

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

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

End Sub

Function GetScoreTree() As Integer
Dim TempScore As Integer

    If Not WithinTimeLimit Then Exit Function

    GetScoreTree = 0

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

End Function

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

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

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

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

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

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

        Next k
    Next l

End Sub

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

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

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

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

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

        Next k
    Next i

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

End Sub

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

    j = 0

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

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

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

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

End Function

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

    If Not WithinTimeLimit Then Exit Function

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

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

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

End Function

Function WithinTimeLimit() As Boolean
    StopTime = Now()
    WithinTimeLimit = (Round(CDbl(((StopTime - StartTime) - Int(StopTime - StartTime)) * 86400), 0) < 120)
End Function
Gaffi
sumber
2
Saya belum melihat melalui kode, tetapi 50 poin sangat rendah. Saya telah memainkan papan yang dibuat secara acak dengan skor lebih dari 1000 (menggunakan SOWPODS - daftar kata yang disediakan mungkin kurang luas). Anda mungkin ingin memeriksa kesalahan tanda!
Peter Taylor
@PeterTaylor Terima kasih atas sarannya. Saya tahu bahwa skornya terlalu rendah, dan saya tahu bahwa sebagian masalahnya terletak pada kenyataan bahwa saya dapat melihat kata-kata yang terlewatkan ...
Gaffi
@PeterTaylor Juga, sebagai catatan, saya terus memposting kemajuan saya, daripada menunggu produk akhir saya, karena belum ada orang lain yang menusuknya. Saya ingin menjaga pertanyaan tetap hidup sampai itu terjadi.
Gaffi
Saya juga harus mencatat bahwa ini tidak dijalankan pada mesin tercepat di luar sana, jadi itu juga tidak membantu.
Gaffi
1
@ Gaffi 10 detik untuk menghitung skor? Itu terlalu lama 9,999 detik. Anda harus memikirkan kembali kode Anda. Jika Anda menolak untuk mengubah daftar kata Anda menjadi pohon, maka setidaknya lakukan ini: Buatlah daftar (hashtable, apa pun) dari semua awalan dua huruf. Kemudian ketika Anda mulai mengikuti jalur di papan tulis, jika dua huruf pertama tidak ada dalam daftar, lewati seluruh subtree dari jalur yang mungkin. Sekali lagi, membangun pohon lengkap adalah yang terbaik, tetapi daftar awalan dua huruf akan membantu dan sangat murah untuk membuatnya.
kotak roti
2

Cepat lihat ukuran ruang pencarian.

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

Mengurangi untuk mengecualikan pengulangan pada setiap dadu.

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

@breadbox menyimpan kamus sebagai pemeriksaan Hash Table O (1).

EDIT

Dewan Terbaik (Saya telah menyaksikan sejauh ini)

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

Score: 830
Words: 229
SLEETIEST  MANTELETS
MANTEELS  MANTELET  MATELESS
MANTEEL  MANTELS  TESTEES  BETISES  OBTESTS  OBESEST
SLEETS  SLEEST  TESTIS  TESTES  TSETSE  MANTES  MANTEL  TESTAE  TESTEE
STEELS  STELES  BETELS  BESETS  BESITS  BETISE  BODGES  BESEES  EISELS
GESTES  GEISTS  OBTEST
LEANT  LEATS  LEETS  LEESE  LESES  LESTS  LESBO  ANTES  NATES  SLEET  SETAE
SEATS  STIES  STEEL  STETS  STEAN  STEAM  STELE  SELES  TAELS  TEELS  TESTS
TESTE  TELES  TETES  MATES  TESTA  TEATS  SEELS  SITES  BEETS  BETEL  BETES
BESET  BESTS  BESIT  BEATS  BODGE  BESEE  DOGES  EISEL  GESTS  GESTE  GESSE
GEITS  GEIST  OBESE
LEAN  LEAT  LEAM  LEET  LEES  LETS  LEST  LESS  EATS  EELS  ELSE  ETNA  ESES
ESTS  ESSE  ANTE  ANTS  ATES  AMBO  NATS  SLEE  SEEL  SETA  SETS  SESE  SEAN
SEAT  SEAM  SELE  STIE  STET  SEES  TAEL  TAES  TEEL  TEES  TEST  TEAM  TELE
TELS  TETS  TETE  MATE  MATS  MAES  TIES  TEAT  TEGS  SELS  SEGO  SITS  SITE
BEET  BEES  BETA  BETE  BETS  BEST  BEAN  BEAT  BEAM  BELS  BOGS  BEGO  BEGS
DOGE  DOGS  DOBS  GOBS  GEST  GEIT  GETS  OBES
LEA  LEE  LET  LES  EAN  EAT  EEL  ELS  ETA  EST  ESS  ANT  ATE  NAT  NAE  NAM
SEE  SET  SEA  SEL  TAN  TAE  TAM  TEE  TES  TEA  TEL  TET  MNA  MAN  MAT  MAE
TIE  TIS  TEG  SEG  SEI  SIT  BEE  BET  BEL  BOD  BOG  BEG  DOG  DOB  ITS  EGO
GOD  GOB  GET  OBS  OBE
EA  EE  EL  ET  ES  AN  AT  AE  AM  NA  ST  TA  TE  MA
TI  SI  BE  BO  DO  IT  IS  GO  OD  OB
Adam Speight
sumber
Dapatkan saya mesin dengan RAM sebanyak itu dan kami akan bicara.
kotak roti
Anda perlu membagi permutasi dadu dengan 8 untuk memperhitungkan simetri alun-alun. Juga, bagaimana Anda mendapatkan (4 ^ 4) (5 ^ 6) (6 ^ 5)? Saya membuatnya (4 ^ 3) (5 ^ 7) (6 ^ 6), dengan total ruang lebih dari 2 ^ 79.
Peter Taylor
@ Peter Taylor: Anda benar. Saya harus menghapus satu ke banyak, ketika melakukan wajah-wajah unik. Saya pikir kita bisa sepakat ada 83 wajah unik, (tidak termasuk pengulangan di seluruh mati). Pilih 16 tanpa pengulangan. '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 'Kira-kira: 1,082 x (10 ^ 30) ==> ~ 2 ^ 100 Apa pernah itu, ini adalah angka yang besar.
Adam Speight
2
@AdamSpeight Awalnya saya berasumsi bahwa komentar Anda tentang menyimpan kamus sebagai hashtable hanya lelucon, jadi saya pada dasarnya mengabaikannya. Permintaan maaf saya. Respons yang tepat adalah: Sebenarnya, hashtable adalah struktur data yang buruk untuk masalah ini. Itu hanya dapat menjawab pertanyaan "apakah X kata yang valid?", Jadi Anda harus membangun semua string yang mungkin untuk menemukan kata-kata itu. Sebuah DAWG memungkinkan Anda bertanya "apakah X awalan dari kata yang valid? Dan jika demikian, huruf apa yang dapat mengikutinya?" Ini memungkinkan Anda untuk memangkas ruang pencarian ke sebagian kecil dari ukuran totalnya.
kotak roti
Hashtable mengerikan karena mencegah Anda menyisihkan fragmen kata yang tidak akan pernah menjadi kata lengkap (dicttree.ceiling (fragment) .startWith (fragment)). Meskipun boggle board apa pun yang diberikan memiliki jutaan kata potensial, Anda dapat membuang sebagian besar dari mereka setelah 2-3 huruf dirangkai menjadi satu. Tree traversal lebih lambat daripada pencarian yang hashtable, tetapi tree memungkinkan Anda untuk menghindari 99+ persen pekerjaan melalui backtracking.
Jim W
1

Entri saya ada di sini di Dream.In.Code ~ 30ms per pencarian papan (pada mesin 2 inti, harus lebih cepat dengan lebih banyak core)

Adam Speight
sumber
Masih mencari di dalamnya, tetapi tautan pertama Anda pada halaman tersebut tidak ada :di dalamnya http://. ;-)
Gaffi
Sangat bagus. Saya akan mencoba mencuri itu untuk diri saya sebagai pengalaman belajar. .NETuntuk VBAtidak terlalu sulit.
Gaffi
Maukah Anda memperbarui jawaban untuk menyertakan skor rata-rata saat menjalankan daftar ISPELL (bukan SOWPODS)? Itu adalah bagian dari tantangan, dan saya tertarik untuk melihat bagaimana hasil Anda dibandingkan dengan kotak roti.
Gaffi