เกณฑ์การให้คะแนนที่ดีที่สุดเกรงกลัว


16

ฉันสนใจที่จะเห็นคำตอบของคำถาม(ตอนนี้หมดอายุ)แต่ก็ไม่เคยได้รับการแก้ไข / ปรับปรุง

เมื่อได้รับชุดลูกเต๋าเกยตื้น 6 ด้าน(การกำหนดค่าที่ถูกขโมยไปจากคำถามนี้ ) ให้กำหนดเวลาในการประมวลผลสองนาทีซึ่งการกำหนดค่าบอร์ดจะช่วยให้ได้คะแนนสูงสุดที่เป็นไปได้ (เช่นลูกเต๋าในตำแหน่งใดที่อนุญาตให้มีกลุ่มคำที่ใหญ่ที่สุดได้)


วัตถุประสงค์

  • รหัสของคุณควรทำงานไม่เกิน 2 นาที (120 วินาที) ในเวลานั้นควรหยุดทำงานโดยอัตโนมัติและพิมพ์ผลลัพธ์

  • คะแนนความท้าทายสุดท้ายจะเป็นคะแนน Boggle เฉลี่ย 5 โปรแกรม

    • ในกรณีที่เสมอกันผู้ชนะจะต้องพบว่าอัลกอริทึมใดมีคำมากกว่านี้
    • ในกรณีที่ยังคงมีคะแนนเสียงเท่ากันให้ผู้ชนะเลิศจะได้รับแล้วแต่ราคาใดขั้นตอนวิธีการที่พบมากขึ้น ยาว (8+)คำ

กฎ / ข้อ จำกัด

  • นี่คือความท้าทายรหัส; ความยาวรหัสไม่เกี่ยวข้อง

  • โปรดอ้างอิงลิงค์นี้สำหรับรายการคำ ( ISPELL "english.0"รายการใช้งาน- รายการ SCOWL ไม่มีคำทั่วไปที่น่าสนใจ)

    • รายชื่อนี้อาจถูกอ้างถึง / นำเข้า / อ่านในรหัสของคุณในแบบที่คุณต้องการ
    • เฉพาะคำที่ตรงกับ regex ^([a-pr-z]|qu){3,16}$เท่านั้นที่จะถูกนับ (ต้องใช้ตัวอักษรพิมพ์เล็ก, 3-16 ตัวอักษร, qu เท่านั้น)
  • คำที่ถูกสร้างขึ้นโดยการเชื่อมโยงตัวอักษรที่อยู่ติดกัน (แนวนอนแนวตั้งและแนวทแยงมุม) เพื่อสะกดคำตามลำดับที่ถูกต้องโดยไม่ต้องใช้ตัวเดียวตายมากกว่าหนึ่งครั้งในคำเดียว

    • คำต้องมี 3 ตัวอักษรหรือมากกว่านั้น คำที่สั้นกว่าจะไม่ได้รับคะแนน
    • ตัวอักษรที่ซ้ำกันนั้นเป็นที่ยอมรับได้ไม่ใช่แค่ลูกเต๋า
    • ไม่อนุญาตให้ใช้คำที่ขยายขอบ / ข้ามจากด้านหนึ่งของกระดานไปอีกด้านหนึ่ง
  • คะแนน Boggle สุดท้าย ( ไม่ใช่การท้าทาย ) คือผลรวมของค่าคะแนนสำหรับคำทั้งหมดที่พบ

    • ค่าคะแนนที่กำหนดสำหรับแต่ละคำขึ้นอยู่กับความยาวของคำ (ดูด้านล่าง)
    • กฎเกรงกลัวปกติจะหัก / ลดคำที่ผู้เล่นอื่นพบ สมมติว่าที่นี่ไม่มีผู้เล่นคนอื่นเข้ามาเกี่ยวข้องและคำที่พบทั้งหมดจะถูกนับรวมกับคะแนนรวม
    • อย่างไรก็ตามคำที่พบมากกว่าหนึ่งครั้งในกริดเดียวกันควรนับเพียงครั้งเดียว
  • ฟังก์ชั่น / โปรแกรมของคุณจะต้องหาการจัดเรียงที่ดีที่สุด; เพียงการเข้ารหัสอย่างยากลำบากรายการที่กำหนดไว้จะไม่ทำ

  • ผลลัพธ์ของคุณควรเป็นตาราง 4x4 ของบอร์ดเกมในอุดมคติของคุณรายการคำศัพท์ที่พบทั้งหมดสำหรับบอร์ดนั้นและคะแนนเกรงขามให้ตรงกับคำเหล่านั้น


กำหนดค่า 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

ตารางการให้คะแนนแบบ 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.

ตัวอย่างเอาต์พุต

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

CUT
THE
LUCK
HEX
....

140 points

หากต้องการคำชี้แจงเพิ่มเติมใด ๆ โปรดถาม!


2
ฉันต้องการให้พจนานุกรมที่จัดทำขึ้นเพื่อวัตถุประสงค์มาตรฐาน โปรดทราบว่านี่ไม่ใช่ความคิดใหม่เนื่องจากการค้นหาโดย Google แบบง่ายจะเปิดเผย :) คะแนนสูงสุดที่ฉันเคยเห็นคือ4527( 1414คำทั้งหมด) พบได้ที่นี่: ai.stanford.edu/~chuongdo/boggle/index.html
mellamokb

4
โปรแกรมจำเป็นต้องยุติศตวรรษนี้หรือไม่?
Peter Taylor

1
@GlitchMr โดยปกติแล้วภาษาอังกฤษ Q มักจะใช้กับ U. Boggle บัญชีนี้โดยการใส่ตัวอักษรสองตัวบน Die เดียวกันกับหน่วยเดียว
Gaffi

1
ข้อกำหนดรายการคำไม่ชัดเจน คุณนับเฉพาะคำที่อยู่ใน english.0 เป็นตัวพิมพ์เล็กหรือไม่? (กฎของเกมคำศัพท์มาตรฐานไม่รวมตัวย่อ / การเริ่มต้นและคำนามที่เหมาะสม)
Peter Taylor

1
ฉันคิดว่า regex ^([a-pr-z]|qu){3,16}$(ซึ่งจะไม่รวมคำ 3 ตัวอักษรที่มี qu อย่างไม่ถูกต้อง แต่ไม่มี)
Peter Taylor

คำตอบ:


9

C, เฉลี่ย500+ 1,500 1,750 คะแนน

นี่เป็นการปรับปรุงเล็กน้อยสำหรับรุ่น 2 (ดูด้านล่างสำหรับบันทึกย่อของรุ่นก่อนหน้า) มีสองส่วน ครั้งแรก: แทนที่จะเลือกบอร์ดแบบสุ่มจากพูลตอนนี้โปรแกรมวนซ้ำทุกบอร์ดในพูลโดยใช้แต่ละบอร์ดก่อนที่จะกลับไปที่ด้านบนสุดของพูลและทำซ้ำ (เนื่องจากพูลกำลังถูกแก้ไขในขณะที่การวนซ้ำนี้เกิดขึ้นจะยังคงมีบอร์ดที่ได้รับการเลือกสองครั้งในแถวหรือแย่กว่านั้น แต่นี่ไม่ใช่ปัญหาร้ายแรง) การเปลี่ยนแปลงครั้งที่สองคือโปรแกรมจะติดตามเมื่อเปลี่ยนพูล และถ้าโปรแกรมใช้เวลานานเกินไปโดยไม่ปรับปรุงเนื้อหาของพูลโปรแกรมจะพิจารณาว่าการค้นหามี "หยุด" ลบพูลและเริ่มต้นด้วยการค้นหาใหม่ มันยังคงทำเช่นนี้ต่อไปจนกว่าจะถึงสองนาที

ตอนแรกฉันคิดว่าฉันจะใช้การค้นหาแบบฮิวริสติกบางอย่างเพื่อให้ได้มากกว่า 1500 จุด @ ความคิดเห็นของ mellamokb เกี่ยวกับคณะกรรมการ 4527 จุดทำให้ฉันคิดว่ามันมีพื้นที่มากมายสำหรับการปรับปรุง อย่างไรก็ตามเราใช้รายการคำที่ค่อนข้างเล็ก กระดาน 4527 จุดนั้นให้คะแนนโดยใช้ YAWL ซึ่งเป็นรายการคำศัพท์ที่ครอบคลุมมากที่สุด - ยิ่งใหญ่กว่า Wordlist Scrabble ที่เป็นทางการของสหรัฐฯ ด้วยสิ่งนี้ในใจฉันจึงตรวจสอบกระดานของฉันอีกครั้งว่าโปรแกรมของฉันพบและสังเกตว่าดูเหมือนว่าจะมีบอร์ดที่ จำกัด จำนวนสูงกว่า 1,700 หรือมากกว่านั้น ตัวอย่างเช่นฉันมีการวิ่งหลายครั้งที่ค้นพบบอร์ดที่ให้คะแนน 1726 แต่มันก็เป็นบอร์ดเดียวกับที่พบเสมอ (ไม่สนใจการหมุนและการสะท้อนกลับ)

จากการทดสอบอีกครั้งฉันใช้โปรแกรมของฉันโดยใช้ YAWL เป็นพจนานุกรมและพบกระดาน 4527 จุดหลังจากนั้นประมาณหนึ่งโหล เมื่อพิจารณาถึงสิ่งนี้ฉันตั้งสมมติฐานว่าโปรแกรมของฉันมีขีด จำกัด สูงสุดของพื้นที่การค้นหาอยู่แล้วดังนั้นการเขียนใหม่ที่ฉันวางแผนไว้จะทำให้เกิดความซับซ้อนเป็นพิเศษเพื่อให้ได้กำไรน้อยมาก

นี่คือรายการกระดานคะแนนสูงสุดห้ารายการที่ฉันพบโดยใช้english.0wordlist:

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

ความเชื่อของฉันคือว่า "grep board" ในปี 1772 (อย่างที่ฉันเคยเรียกมันว่า) ด้วยความยาว 531 คำเป็นบอร์ดที่ให้คะแนนสูงสุดที่เป็นไปได้สำหรับ wordlist นี้ กว่า 50% ของโปรแกรมสองนาทีของฉันจบลงด้วยบอร์ดนี้ ฉันยังออกจากโปรแกรมของฉันทำงานข้ามคืนโดยไม่ได้หาอะไรที่ดีกว่า ดังนั้นหากมีบอร์ดที่ให้คะแนนสูงกว่าก็เป็นไปได้ว่าจะต้องมีบางแง่มุมที่เอาชนะเทคนิคการค้นหาของโปรแกรม บอร์ดที่มีการเปลี่ยนแปลงเลย์เอาต์เล็ก ๆ น้อย ๆ ที่เป็นไปได้จะทำให้คะแนนรวมลดลงอย่างมากตัวอย่างเช่นโปรแกรมของฉันอาจไม่ถูกค้นพบ ลางสังหรณ์ของฉันคือว่าคณะกรรมการดังกล่าวไม่น่าจะมีอยู่จริง

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

หมายเหตุสำหรับรุ่น 2 (9 มิถุนายน)

นี่คือวิธีหนึ่งในการใช้รหัสเริ่มต้นของฉันเป็นจุดกระโดด การเปลี่ยนแปลงของเวอร์ชั่นนี้มีน้อยกว่า 100 บรรทัด แต่เพิ่มคะแนนเกมเฉลี่ยสามเท่า

ในรุ่นนี้โปรแกรมรักษา "กลุ่ม" ของผู้สมัครซึ่งประกอบด้วยบอร์ดที่มีคะแนนสูงสุด N ซึ่งโปรแกรมได้สร้างขึ้น ทุกครั้งที่มีการสร้างบอร์ดใหม่มันจะถูกเพิ่มลงในพูลและบอร์ดที่มีคะแนนต่ำที่สุดในพูลนั้นจะถูกลบออก (ซึ่งอาจเป็นบอร์ดที่เพิ่งเพิ่มเข้าไปถ้าคะแนนต่ำกว่าที่มีอยู่แล้ว) กลุ่มแรกจะเต็มไปด้วยบอร์ดที่สร้างแบบสุ่มหลังจากนั้นจะมีขนาดคงที่ตลอดช่วงการทำงานของโปรแกรม

ลูปหลักของโปรแกรมประกอบด้วยการเลือกบอร์ดแบบสุ่มจากพูลและแก้ไขโดยการกำหนดคะแนนของบอร์ดใหม่จากนั้นใส่ลงในพูล (ถ้าคะแนนดีพอ) ในแบบนี้โปรแกรมจะทำการกลั่นกระดานคะแนนสูงอย่างต่อเนื่อง กิจกรรมหลักคือการปรับปรุงทีละขั้นตอนเพิ่มขึ้น แต่ขนาดของพูลยังอนุญาตให้โปรแกรมค้นหาการปรับปรุงหลายขั้นตอนที่ทำให้คะแนนของบอร์ดแย่ลงชั่วคราวก่อนจึงจะสามารถทำให้ดีขึ้นได้

โดยทั่วไปโปรแกรมนี้จะหาค่าสูงสุดในท้องถิ่นที่ค่อนข้างดีได้อย่างรวดเร็วหลังจากนั้นค่าสูงสุดที่ดีกว่านั้นอยู่ไกลเกินกว่าที่จะพบได้ ดังนั้นอีกครั้งมีจุดเล็กน้อยที่จะเรียกใช้โปรแกรมนานกว่า 10 วินาที สิ่งนี้อาจได้รับการปรับปรุงโดยการให้โปรแกรมตรวจสอบสถานการณ์นี้และเริ่มการค้นหาใหม่ด้วยกลุ่มผู้สมัครใหม่ อย่างไรก็ตามสิ่งนี้จะเป็นเพียงการเพิ่มขึ้นเล็กน้อย เทคนิคการค้นหาแบบฮิวริสติกที่เหมาะสมน่าจะเป็นหนทางที่ดีกว่าในการสำรวจ

(หมายเหตุด้านข้าง: ฉันเห็นว่ารุ่นนี้สร้างบอร์ดประมาณ 5k / วินาทีเนื่องจากโดยทั่วไปเวอร์ชันแรกทำบอร์ด 20k / วินาทีฉันก็กังวลในขั้นแรกเมื่อทำโปรไฟล์อย่างไรก็ตามฉันค้นพบว่าเวลาพิเศษใช้เวลาจัดการรายการคำ มันเป็นเพราะโปรแกรมค้นหาคำอื่น ๆ อีกมากมายต่อบอร์ดในแง่นี้ฉันคิดว่าการเปลี่ยนรหัสเพื่อจัดการ wordlist แต่เนื่องจากโปรแกรมนี้ใช้ 10 ใน 120 วินาทีที่กำหนดไว้เท่านั้น การเพิ่มประสิทธิภาพจะเร็วมาก)

หมายเหตุสำหรับรุ่น 1 (2 มิถุนายน)

นี่เป็นทางออกที่ง่ายมาก ๆ ทั้งหมดมันสร้างบอร์ดแบบสุ่มและหลังจากนั้น 10 วินาทีมันจะออกบอร์ดที่มีคะแนนสูงสุด (ฉันตั้งค่าเริ่มต้นเป็น 10 วินาทีเพราะปกติ 110 วินาทีที่ได้รับอนุญาตจากข้อกำหนดคุณสมบัติของปัญหาไม่ได้ปรับปรุงวิธีแก้ไขปัญหาขั้นสุดท้ายที่พบว่าคุ้มค่าที่จะรอ) ดังนั้นมันจึงโง่มาก อย่างไรก็ตามมีโครงสร้างพื้นฐานทั้งหมดเพื่อให้เป็นจุดเริ่มต้นที่ดีสำหรับการค้นหาที่ชาญฉลาดยิ่งขึ้นและหากใครต้องการใช้มันก่อนถึงกำหนดเวลาฉันขอแนะนำให้พวกเขาทำเช่นนั้น

โปรแกรมเริ่มต้นด้วยการอ่านพจนานุกรมลงในโครงสร้างแบบต้นไม้ (แบบฟอร์มนั้นยังไม่ได้รับการปรับให้เหมาะสมที่สุดเท่าที่จะเป็นไปได้ แต่มันก็ยังดีพอสำหรับจุดประสงค์เหล่านี้) หลังจากการเริ่มต้นพื้นฐานอื่น ๆ แล้วมันก็เริ่มสร้างบอร์ดและให้คะแนนพวกเขา โปรแกรมตรวจสอบบอร์ดประมาณ 20k ต่อวินาทีบนเครื่องของฉันและหลังจากบอร์ดประมาณ 200k การสุ่มเริ่มแห้ง

เนื่องจากมีเพียงบอร์ดเดียวเท่านั้นที่ถูกประเมินตามเวลาที่กำหนดข้อมูลการให้คะแนนจะถูกเก็บไว้ในตัวแปรทั่วโลก สิ่งนี้ช่วยให้ฉันลดจำนวนข้อมูลคงที่ที่จะต้องถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังฟังก์ชันเรียกซ้ำ (ฉันแน่ใจว่านี่จะให้บางคนลมพิษและฉันขอโทษพวกเขา) รายการคำจะถูกเก็บไว้เป็นต้นไม้ค้นหาแบบไบนารี ทุกคำที่พบจะต้องค้นหาในรายการคำเพื่อให้คำที่ซ้ำกันไม่ถูกนับสองครั้ง รายการคำศัพท์จำเป็นต้องใช้ในระหว่างกระบวนการ evaulation เท่านั้นดังนั้นมันจึงถูกทิ้งหลังจากที่พบคะแนน ดังนั้นเมื่อสิ้นสุดโปรแกรมคณะกรรมการที่ได้รับการคัดเลือกจะต้องทำคะแนนซ้ำอีกครั้งเพื่อให้สามารถพิมพ์รายการคำได้

ความจริงแล้วสนุก: คะแนนเฉลี่ยสำหรับกระดาน Boggle ที่สร้างแบบสุ่มซึ่งได้คะแนนenglish.0เท่ากับ 61.7 คะแนน


เห็นได้ชัดว่าฉันต้องปรับปรุงประสิทธิภาพของตัวเอง :-)
Gaffi

วิธีการทางพันธุกรรมของฉันได้รับประมาณ 700-800 คะแนนสร้างกระดานประมาณ 200k ดังนั้นคุณจึงทำสิ่งที่ดีกว่าฉันอย่างชัดเจนในแบบที่คุณผลิตรุ่นต่อไป
Peter Taylor

โครงสร้างต้นไม้ของฉันเองถูกนำไปใช้กับ wordlist หลักแล้วในขณะที่มันใช้งานได้และทำให้ฉันสามารถตรวจสอบบอร์ดได้มันทำให้หน่วยความจำระบบของฉันยุ่งเหยิงลงได้ (ล่าช้าไปจนถึงจุดที่ใช้เวลานานในการบังคับกระบวนการ เพื่อยุติก่อน) นี่เป็นความผิดของฉันเอง แต่ฉันกำลังทำงานอยู่! แก้ไข: แก้ไขแล้ว! ;-)
Gaffi

@ PeterTaylor ฉันคิดเกี่ยวกับการลองอัลกอริทึมทางพันธุกรรม แต่ฉันไม่สามารถนึกถึงกลไกที่เป็นไปได้สำหรับการรวมสองบอร์ดเข้าด้วยกัน คุณเป็นอย่างไรบ้าง คุณเลือกผู้ปกครองสุ่มสำหรับแต่ละช่องบนกระดานหรือไม่?
breadbox

ฉันแบ่งสถานะกระดานเป็นการเรียงสับเปลี่ยนของลูกเต๋าและใบหน้าแสดงบนลูกเต๋า สำหรับการเปลี่ยนแปลงแบบไขว้ฉันใช้ "order crossover 1" จากcs.colostate.edu/~genitor/1995/permutations.pdfและสำหรับ crossover face ฉันเห็นได้ชัด แต่ฉันมีความคิดสำหรับแนวทางที่แตกต่างอย่างสิ้นเชิงซึ่งฉันต้องหาเวลาที่จะใช้
Peter Taylor

3

VBA (ปัจจุบันเฉลี่ยอยู่ระหว่าง 80-110 จุดยังไม่เสร็จ)

นี่คือกระบวนการทำงานของฉัน แต่มันไกลเกินกว่าที่จะเป็นไปได้ คะแนนที่ดีที่สุดของฉันที่พบในกระดานใด ๆ หลังจากการทดสอบจำนวนมากอยู่ที่ประมาณ 120. ยังคงต้องมีการทำความสะอาดทั่วไปที่ดีขึ้นและฉันแน่ใจว่ามีประสิทธิภาพมากขึ้นที่จะได้รับในหลายสถานที่

  • 2012/05/09:
    • โพสต์ต้นฉบับ
  • 2012.05.10 - 2012.05.18:
    • ปรับปรุงอัลกอริทึมการให้คะแนน
    • ปรับปรุงตรรกะการค้นหาเส้นทาง
  • 2012.06.07 - 2012.06.12 :
    • จำกัด จำนวนคำที่ลดลงเหลือ 6 จาก 8 อนุญาตให้บอร์ดเพิ่มเติมที่มีคำน้อยกว่า ดูเหมือนว่าจะมีการปรับปรุงคะแนนเฉลี่ยเล็กน้อย (บอร์ด 10-15 หรือมากกว่านั้นได้รับการตรวจสอบต่อการวิ่งเทียบกับ 1 ถึง 2)
    • ตามคำแนะนำของ breadbox ฉันได้สร้างโครงสร้างต้นไม้เพื่อใช้เป็นที่เก็บคำศัพท์ นี่เป็นการเพิ่มความเร็วในการตรวจสอบคำท้ายกระดานอย่างรวดเร็ว
    • ฉันเล่นกับการเปลี่ยนขนาดคำสูงสุด (ความเร็วเทียบกับคะแนน) และฉันยังไม่ได้ตัดสินใจว่า 5 หรือ 6 เป็นตัวเลือกที่ดีกว่าสำหรับฉัน 6 ผลลัพธ์ใน 100-120 บอร์ดรวมที่ตรวจสอบแล้วในขณะที่ 5 ผลลัพธ์ใน 500-1000 (ซึ่งทั้งคู่ยังคงต่ำกว่าตัวอย่างอื่น ๆ ที่มีให้)
    • ปัญหา : หลังจากทำงานต่อเนื่องหลายครั้งกระบวนการจะเริ่มช้าลงดังนั้นจึงมีหน่วยความจำบางส่วนที่จะจัดการ

นี่อาจดูน่ากลัวสำหรับคุณบางคน แต่อย่างที่ฉันพูด WIP ฉันเปิดกว้างสำหรับการวิจารณ์ที่สร้างสรรค์ ! ขออภัยสำหรับร่างกายที่ยาวมาก ...


โมดูลระดับลูกเต๋า :

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

Tree Class Module :

Option Explicit

Private zzroot As TreeNode


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

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

End Sub

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

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

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

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

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

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

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

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

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

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

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

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

Private Sub Class_Terminate()
    Set zzroot = Nothing
End Sub

โมดูลคลาส TreeNode :

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

โมดูลหลัก :

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
ฉันไม่ได้ดูรหัส แต่ 50 คะแนนต่ำอย่างน่าขัน ฉันเล่นบอร์ดที่สร้างแบบสุ่มด้วยคะแนนมากกว่า 1,000 รายการ (ใช้ SOWPODS - รายการคำที่ให้อาจไม่ครอบคลุมมาก) คุณอาจต้องการตรวจสอบข้อผิดพลาดของสัญญาณ!
ปีเตอร์เทย์เลอร์

@ PeterTaylor ขอบคุณสำหรับคำแนะนำ ฉันรู้ว่าคะแนนที่ต่ำมากเกินไปและฉันรู้ว่าส่วนหนึ่งของปัญหาอยู่ในความจริงที่ฉันสามารถดูคำพูดที่ชัดเจนว่าถูกพลาด ...
Gaffi

@PeterTaylor นอกจากนี้สำหรับบันทึกฉันยังคงโพสต์ความคืบหน้าของฉันอย่างต่อเนื่องมากกว่าที่จะรอผลิตภัณฑ์ขั้นสุดท้ายของฉันเพราะยังไม่มีใครถูกแทงที่มัน ฉันต้องการให้คำถามมีชีวิตอยู่บ้างจนกว่าจะเกิดขึ้น
Gaffi

ฉันควรทราบด้วยว่านี่ไม่ได้ถูกเรียกใช้บนเครื่องที่เร็วที่สุดที่นั่นดังนั้นจึงไม่ช่วยเช่นกัน
Gaffi

1
@Gaffi 10 วินาทีในการคำนวณคะแนน? นั่นคือ 9.999 วินาทีนานเกินไป คุณต้องคิดใหม่รหัสของคุณ หากคุณปฏิเสธที่จะเปลี่ยน wordlist ของคุณเป็น tree ให้ทำอย่างน้อยทำรายการ (hashtable อะไรก็ตาม) ของคำนำหน้าตัวอักษรสองตัวทั้งหมด จากนั้นเมื่อคุณเริ่มติดตามเส้นทางบนกระดานหากตัวอักษรสองตัวแรกไม่อยู่ในรายการให้ข้ามทรีย่อยของเส้นทางที่เป็นไปได้ทั้งหมด อีกครั้งการสร้างต้นไม้เต็มจะดีที่สุด แต่รายการคำนำหน้าสองตัวอักษรจะช่วยและมีราคาถูกมากที่จะทำให้
breadbox

2

ดูขนาดของพื้นที่การค้นหาอย่างรวดเร็ว

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

ลดการแยกการทำซ้ำในแต่ละตาย

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

@breadbox จัดเก็บพจนานุกรมเป็นการตรวจสอบ Hash Table O (1)

แก้ไข

สุดยอดคณะ (ฉันเคยเห็น)

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

รับเครื่องที่มี RAM มากแล้วเราจะคุยกัน
breadbox

คุณต้องแบ่งการเรียงสับเปลี่ยนลูกเต๋าด้วย 8 เพื่อดูสัดส่วนของสแควร์ นอกจากนี้คุณจะได้รับ (4 ^ 4) (5 ^ 6) (6 ^ 5) ได้อย่างไร? ฉันทำมัน (4 ^ 3) (5 ^ 7) (6 ^ 6) สำหรับพื้นที่ searc รวมที่มากกว่า 2 ^ 79
Peter Taylor

@ Peter Taylor: คุณพูดถูก ฉันต้องลบหนึ่งต่อหลายเมื่อทำใบหน้าที่ไม่ซ้ำใคร ฉันคิดว่าเราสามารถตกลงกันได้ว่ามี 83 ใบหน้าที่ไม่ซ้ำกัน (ยกเว้นการทำซ้ำข้ามตาย) เลือก 16 โดยไม่ต้องทำซ้ำ '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 71 x 70 x 69 x 68 'ประมาณ: 1.082 x (10 ^ 30) ==> ~ 2 ^ 100 อะไร เคยเป็นมันเป็นจำนวนมาก
Adam Speight

2
@ AdamSpeight ตอนแรกฉันคิดว่าความคิดเห็นของคุณเกี่ยวกับการจัดเก็บพจนานุกรมเป็น hashtable เป็นเพียงเรื่องตลกและดังนั้นโดยทั่วไปฉันจึงไม่สนใจมัน ขอโทษด้วย. การตอบสนองที่เหมาะสมจะ: จริง hashtable เป็นโครงสร้างข้อมูลหมัดสำหรับปัญหานี้ มันสามารถตอบคำถาม "เป็นคำที่ถูกต้อง X?" ดังนั้นคุณต้องสร้างสตริงที่เป็นไปได้ทั้งหมดเพื่อค้นหาคำ DAWG ช่วยให้คุณถามว่า "X เป็นคำนำหน้าของคำที่ถูกต้องหรือไม่และถ้าใช่ตัวอักษรอะไรที่สามารถติดตามได้" สิ่งนี้ช่วยให้คุณตัดพื้นที่การค้นหาให้เหลือเพียงส่วนเล็ก ๆ ของขนาดทั้งหมด
breadbox

Hashtable นั้นแย่มากเพราะมันช่วยป้องกันคุณในการคัดลอกคำศัพท์ที่จะไม่กลายเป็นคำที่สมบูรณ์ (dicttree.ceiling (fragment) .startWith (fragment)) แม้ว่ากระดานเกรงกลัวที่ให้มาจะมีคำศัพท์หลายล้านคำ แต่คุณสามารถขว้างปาออกเป็นส่วนใหญ่ได้หลังจากที่ตัวอักษร 2-3 ตัวถูกพันเข้าด้วยกัน การสำรวจเส้นทางต้นไม้ช้ากว่าการค้นหา hashtable แต่ต้นไม้อนุญาตให้คุณก้าวเท้าเลี่ยง 99% ของงานผ่านการย้อนรอย
Jim W

1

รายการของฉันอยู่ที่นี่ใน Dream.In.Code ~ 30ms ต่อการค้นหาบอร์ด (บนเครื่องหลัก 2 เครื่องควรเร็วกว่าด้วยแกนเพิ่มเติม)


ยังคงมองเป็นมัน แต่ลิงค์แรกของคุณบนหน้าเว็บที่ขาดหายไปใน: http://;-)
Gaffi

ดีมาก. ฉันจะพยายามขโมยสิ่งนั้นเพื่อตัวเองเป็นประสบการณ์การเรียนรู้ .NETถึงVBAไม่ยากเกินไป
Gaffi

คุณคิดว่าจะปรับปรุงคำตอบเพื่อรวมคะแนนเฉลี่ยของคุณเมื่อเรียกใช้รายการ ISPELL (ไม่ใช่ SOWPODS) หรือไม่ นั่นเป็นส่วนหนึ่งของความท้าทายและฉันสนใจที่จะดูว่าผลลัพธ์ของคุณเปรียบเทียบกับ breadbox อย่างไร
Gaffi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.