शब्द लगता है (उर्फ लिंगो)


13

इस चुनौती का लक्ष्य सबसे कम संभव प्रयासों में एक शब्द का अनुमान लगाने में सक्षम कार्यक्रम लिखना है। यह लिंगो टीवी शो ( http://en.wikipedia.org/wiki/Lingo_(US_game_show) ) की अवधारणा पर आधारित है ।

नियम

अपनी कमांड लाइन पर पहले तर्क के रूप में पारित एक शब्द की लंबाई को देखते हुए, खिलाड़ी कार्यक्रम अपने मानक आउटपुट पर एक एकल चरित्र के बाद अनुमान लगाकर शब्द का अनुमान लगाने के पांच प्रयासों का निपटान करता है \n

एक अनुमान लगाए जाने के बाद, कार्यक्रम को अपने मानक इनपुट पर एक स्ट्रिंग मिलती है, इसके बाद एकल \nचरित्र भी होता है।

स्ट्रिंग में अनुमान लगाने के लिए शब्द के समान लंबाई है और निम्नलिखित वर्णों के अनुक्रम से युक्त है:

  • X: जिसका अर्थ है कि दिया गया अक्षर अनुमान लगाने के लिए शब्द में मौजूद नहीं है
  • ?: जिसका अर्थ है कि दिया गया अक्षर अनुमान लगाने के लिए शब्द में मौजूद है, लेकिन किसी अन्य स्थान पर
  • O: जिसका अर्थ है कि इस स्थान पर अक्षर का सही अनुमान लगाया गया है

उदाहरण के लिए, यदि अनुमान करने के लिए शब्द है dents, और कार्यक्रम के शब्द भेजता है dozes, यह प्राप्त होगा OXX?Oक्योंकि dऔर sसही हैं, eखो जाता है, और oऔर zमौजूद नहीं हैं।

सावधान रहें कि यदि कोई पत्र अनुमान लगाने के लिए शब्द की तुलना में अनुमान लगाने के प्रयास में अधिक बार मौजूद है, तो यह अनुमान लगाने के लिए शब्द में पत्र की संख्या की संख्या से अधिक और अधिक बार चिह्नित नहीं किया जाएगा । उदाहरण के लिए, यदि अनुमान लगाने के लिए शब्द है और प्रोग्राम भेजता है , तो यह प्राप्त होगा क्योंकि पता लगाने के लिए एक ही है ।?OcoziestossesXOXXOOs

शब्दों को अंग्रेजी शब्द सूची से चुना जाता है। यदि प्रोग्राम द्वारा भेजा गया शब्द सही लंबाई का मान्य शब्द नहीं है, तो प्रयास को स्वचालित विफलता माना जाता है और केवल Xलौटा दिया जाता है।
खिलाड़ी कार्यक्रम को यह मान लेना चाहिए कि wordlist.txtप्रति पंक्ति एक शब्द नाम और एक शब्द है जो वर्तमान कार्य निर्देशिका में मौजूद है, और आवश्यकतानुसार पढ़ा जा सकता है।
अनुमानों में केवल अल्फ़ाबेटिक लो-केस वर्ण ( [a-z]) शामिल होना चाहिए ।
कार्यक्रम के लिए किसी अन्य नेटवर्क या फ़ाइल संचालन की अनुमति नहीं है।

खेल तब समाप्त होता है जब एक स्ट्रिंग जिसमें केवल शामिल होता Oहै या कार्यक्रम के 5 प्रयासों के बाद वापस आ जाता है और शब्द का अनुमान लगाने में सक्षम नहीं था।

स्कोरिंग

गेम का स्कोर दिए गए सूत्र द्वारा दिया गया है:

score = 100 * (6 - number_of_attempts)

इसलिए यदि पहली कोशिश में शब्द का सही अनुमान लगाया जाता है, तो 500 अंक दिए जाते हैं। आखिरी कोशिश 100 अंकों की है।

शब्द शून्य अंक का अनुमान लगाने में विफलता।

गड्ढा

खिलाड़ी कार्यक्रमों का मूल्यांकन उन्हें 4 और 13 वर्णों के बीच प्रत्येक शब्द लंबाई के लिए 100 यादृच्छिक शब्दों का अनुमान लगाने की कोशिश करके किया जाएगा ।
यादृच्छिक शब्द चयन अग्रिम द्वारा किया जाएगा इसलिए सभी प्रविष्टियों को समान शब्दों का अनुमान लगाना होगा।

जीतने वाला कार्यक्रम, और स्वीकृत उत्तर, उच्चतम स्कोर तक पहुंचने वाला व्यक्ति होगा।

Https://github.com/noirotm/lingo पर कोड का उपयोग करके प्रोग्राम एक उबंटू वर्चुअल मशीन में चलाया जाएगा । किसी भी भाषा में कार्यान्वयन को तब तक स्वीकार किया जाता है जब तक कि उसे संकलित करने और / या चलाने के लिए उचित निर्देश दिए जाते हैं।

मैं गिट रिपॉजिटरी में माणिक में कुछ परीक्षण कार्यान्वयन प्रदान कर रहा हूं, उनसे प्रेरणा लेने के लिए स्वतंत्र महसूस करता हूं।

यह प्रश्न समय-समय पर प्रकाशित उत्तरों के लिए रैंकिंग के साथ अद्यतन किया जाएगा ताकि चुनौती देने वाले अपनी प्रविष्टियों में सुधार कर सकें।

आधिकारिक अंतिम मूल्यांकन 1 जुलाई को होगा

अपडेट करें

एंट्रीज़ अब wordlistN.txt4 और 13 के बीच एन के लिए वर्तमान शब्द की लंबाई के लिए शब्द सूची को पढ़ने के लिए फ़ाइलों की उपस्थिति का अनुमान लगा सकते हैं ।

उदाहरण के लिए, एक wordlist4.txtफ़ाइल है जिसमें सभी चार अक्षर शब्द हैं, और wordlist10.txtसभी दस अक्षर शब्द हैं, और इसी तरह।

पहले दौर के नतीजे

2014-07-01 की तारीख में, तीन प्रविष्टियाँ प्रस्तुत की गई हैं, निम्नलिखित परिणाम के साथ:

                        4       5       6       7       8       9       10      11      12      13      Total
./chinese-perl-goth.pl  8100    12400   15700   19100   22100   25800   27900   30600   31300   33600   226600
java Lingo              10600   14600   19500   22200   25500   28100   29000   31600   32700   33500   247300
./edc65                 10900   15800   22300   24300   27200   29600   31300   33900   33400   33900   262600

** Rankings **
1: ./edc65 (262600)
2: java Lingo (247300)
3: ./chinese-perl-goth.pl (226600)

सभी प्रविष्टियों ने लगातार प्रदर्शन किया, एक स्पष्ट विजेता के साथ, @ edc65 की C ++ की प्रविष्टि।

सभी प्रतियोगी काफी कमाल के हैं। मैं अभी तक @ चीनी-पर्ल-गोथ को भी हरा नहीं पाया हूं।
यदि अधिक प्रविष्टियाँ प्रस्तुत की जाती हैं, तो एक और मूल्यांकन होगा। वर्तमान प्रविष्टियों को भी बेहतर बनाया जा सकता है अगर आपको लगता है कि आप बेहतर कर सकते हैं।


1
बस स्पष्ट करने के लिए: यदि प्रोग्राम शब्द का अनुमान लगाने में 6 से अधिक समय लेता है, तो क्या यह नकारात्मक अंक या सिर्फ शून्य प्राप्त करता है? दूसरे शब्दों में, क्या हमें नकारात्मक बिंदुओं से बचने की कोशिश करने के बाद कार्यक्रम छोड़ने के लिए तर्क की आवश्यकता है? (नियम शून्य अंक कहते हैं यदि कार्यक्रम शब्द का अनुमान लगाने में विफल रहता है)
डेंकेमेस

1
पांच प्रयासों के बाद @ZoveGames, आपके प्रोग्राम से बाहर निकल जाना चाहिए, लेकिन गेम इंजन इसे जबरन समाप्त कर देगा यदि यह ऐसा करने से मना करता है :)
SirDarius

1
@ रीचर्डा हाँ ठीक है, पायथन के बारे में चिंता मत करो, यह एक प्रथम श्रेणी का नागरिक है, इसलिए मुझे कुछ अजगर कोड चलाने में कोई समस्या नहीं होगी :)
सिराड्राय

1
@ अन्याय इस के लिए बहुत बहुत धन्यवाद! मैं आखिरकार जारी रख सकता हूं!
मिस्टरबला

1
@ अन्यायपूर्ण विचार वास्तव में, मैं इसे लागू करने की कोशिश
करूंगा

जवाबों:


5

सी ++ 267700 अंक

एक पुराने MasterMind इंजन से पोर्टिंग।
मास्टरमाइंड से अंतर:

  • अधिक स्लॉट
  • और प्रतीक
  • बड़ा समाधान स्थान (लेकिन गैर बहुत, क्योंकि सभी प्रतीकों के संयोजन की अनुमति नहीं है)
  • प्रतिक्रिया बहुत जानकारीपूर्ण है, इसलिए हमारे पास प्रत्येक अनुमान के बाद अधिक जानकारी है
  • प्रतिक्रिया धीमी उत्पन्न करने के लिए है और यह एक दया है क्योंकि मेरे एल्गोरिथ्म को इसे बहुत कुछ करना है।

मूल विचार उस शब्द को चुन रहा है जो समाधान स्थान को कम करता है। एल्गोरिथ्म वास्तव में पहले अनुमान (मेरा मतलब 'दिन') के लिए धीमा है, लेकिन सबसे अच्छा पहला अनुमान केवल शब्द लेन पर निर्भर करता है, इसलिए यह स्रोत में हार्डकोड है। दूसरे अनुमान सेकंड के मामले में किए जाते हैं।

कोड

(जी ++ + -ओ 3 के साथ संकलन)

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <ctime>
#include <cstdlib>

using namespace std;

class LRTimer
{
private:
    time_t start;
public:
    void startTimer(void)
    {
        time(&start);
    }

    double stopTimer(void)
    {
        return difftime(time(NULL),start);
    } 

};

#define MAX_WORD_LEN 15
#define BIT_QM 0x8000

LRTimer timer;
int size, valid, wordLen;

string firstGuess[] = { "", "a", "as", "iao", "ares", 
    "raise", "sailer", "saltier", "costlier", "clarities", 
    "anthelices", "petulancies", "incarcerates", "allergenicity" };

class Pattern
{
public:
    char letters[MAX_WORD_LEN];
    char flag;
    int mask;

    Pattern() 
        : letters(), mask(), flag()
    {
    }

    Pattern(string word) 
        : letters(), mask(), flag()
    {
        init(word);
    }

    void init(string word)
    {
        const char *wdata = word.data();
        for(int i = 0; i < wordLen; i++) {
            letters[i] = wdata[i];
            mask |= 1 << (wdata[i]-'a');
        }
    }

    string dump()
    {
        return string(letters);
    }

    int check(Pattern &secret)
    {
        if ((mask & secret.mask) == 0)
            return 0;

        char g[MAX_WORD_LEN], s[MAX_WORD_LEN];
        int r = 0, q = 0, i, j, k=99;
        for (i = 0; i < wordLen; i++)
        {
            g[i] = (letters[i] ^ secret.letters[i]);
            if (g[i])
            {
                r += r;
                k = 0;
                g[i] ^= s[i] = secret.letters[i];
            }
            else
            {
                r += r + 1;
                s[i] = 0;
            }
        }
        for (; k < wordLen; k++)
        {
            q += q;
            if (g[k]) 
            {
                for (j = 0; j < wordLen; j++)
                    if (g[k] == s[j])
                    {
                        q |= BIT_QM;
                        s[j] = 0;
                        break;
                    }
            }
        }
        return r|q;
    }

    int count(int ck, int limit);

    int propcheck(int limit);

    void filter(int ck);
};

string dumpScore(int ck)
{
    string result(wordLen, 'X');
    for (int i = wordLen; i--;)
    {
        result[i] = ck & 1 ? 'O' : ck & BIT_QM ? '?' : 'X';
        ck >>= 1;
    }
    return result;
}

int parseScore(string ck)
{
    int result = 0;
    for (int i = 0; i < wordLen; i++)
    {
        result += result + (
            ck[i] == 'O' ? 1 : ck[i] == '?' ? BIT_QM: 0
        );
    }
    return result;
}

Pattern space[100000];

void Pattern::filter(int ck)
{
    int limit = valid, i = limit;
//  cerr << "Filter IN Valid " << setbase(10) << valid << " This " << dump() << "\n"; 

    while (i--)
    {
        int cck = check(space[i]);
//      cerr << setbase(10) << setw(8) << i << ' ' << space[i].dump() 
//          << setbase(16) << setw(8) << cck << " (" << Pattern::dumpScore(cck) << ") ";

        if ( ck != cck )
        {
//          cerr << " FAIL\r" ;
            --limit;
            if (i != limit) 
            {
                Pattern t = space[i];
                space[i] = space[limit];
                space[limit] = t;
            }
        }
        else
        {
//          cerr << " PASS\n" ;
        }
    }
    valid = limit;
//  cerr << "\nFilter EX Valid " << setbase(10) << valid << "\n"; 
};

int Pattern::count(int ck, int limit)
{
    int i, num=0;
    for (i = 0; i < valid; ++i)
    {
        if (ck == check(space[i]))
            if (++num >= limit) return num;
    }
    return num;
}

int Pattern::propcheck(int limit)
{
    int k, mv, nv;

    for (k = mv = 0; k < valid; ++k)
    {
        int ck = check(space[k]);
        nv = count(ck, limit);
        if (nv >= limit)
        {
            return 99999;
        }
        if (nv > mv) mv = nv;
    }
    return mv;
}

int proposal(bool last)
{
    int i, minnv = 999999, mv, result;

    for (i = 0; i < valid; i++) 
    {
        Pattern& guess = space[i];
//      cerr << '\r' << setw(6) << i << ' ' << guess.dump();
        if ((mv = guess.propcheck(minnv)) < minnv)
        {
//          cerr << setw(6) << mv << ' ' << setw(7) << setiosflags(ios::fixed) << setprecision(0) << timer.stopTimer() << " s\n";
            minnv = mv;
            result = i;
        }
    }   
    if (last) 
        return result;
    minnv *= 0.75;
    for (; i<size; i++) 
    {
        Pattern& guess = space[i];
//      cerr << '\r' << setw(6) << i << ' ' << guess.dump();
        if ((mv = guess.propcheck(minnv)) < minnv)
        {
//          cerr << setw(6) << mv << ' ' << setw(7) << setiosflags(ios::fixed) << setprecision(0) << timer.stopTimer() << " s\n";
            minnv = mv;
            result = i;
        }
    }   
    return result;
}

void setup(string wordfile)
{
    int i = 0; 
    string word;
    ifstream infile(wordfile.data());
    while(infile >> word)
    {
        if (word.length() == wordLen) {
            space[i++].init(word);
        }
    }
    infile.close(); 
    size = valid = i;
}

int main(int argc, char* argv[])
{
    if (argc < 2) 
    {
        cerr << "Specify word length";
        return 1;
    }

    wordLen = atoi(argv[1]);

    timer.startTimer();
    setup("wordlist.txt");
    //cerr << "Words " << size 
    //  << setiosflags(ios::fixed) << setprecision(2)
    //  << " " << timer.stopTimer() << " s\n";

    valid = size;
    Pattern Guess = firstGuess[wordLen];
    for (int t = 0; t < 5; t++)
    {
        cout << Guess.dump() << '\n' << flush;
        string score;
        cin >> score;
        int ck = parseScore(score);
        //cerr << "\nV" << setw(8) << valid << " #" 
        //  << setw(3) << t << " : " << Guess.dump()
        //  << " : " << score << "\n";
        if (ck == ~(-1 << wordLen))
        {
            break;
        }
        Guess.filter(ck); 
        Guess = space[proposal(t == 3)];
    }
    // cerr << "\n";

    double time = timer.stopTimer();
    //cerr << setiosflags(ios::fixed) << setprecision(2)
    //   << timer.stopTimer() << " s\n";

    return 0;
}

मेरा स्कोर

लिंगो के साथ मूल्यांकन, 100 राउंड:

4   9000
5   17700
6   22000
7   25900
8   28600
9   29700
10  31000
11  32800
12  33500
13  34900

कुल 265'100

स्व मूल्यांकित अंक

यहाँ मेरे औसत अंक हैं, पूरे वर्डलिस्ट पर रन बनाए हैं। पूरी तरह से भरोसेमंद नहीं है क्योंकि परीक्षणों के दौरान एल्गोरिथ्म के कुछ विवरण बदल गए हैं।

 4 # words  6728 PT AVG   100.98 87170.41 s
 5 # words 14847 PT AVG   164.44 42295.38 s
 6 # words 28127 PT AVG   212.27 46550.00 s 
 7 # words 39694 PT AVG   246.16 61505.54 s
 8 # words 49004 PT AVG   273.23 63567.45 s
 9 # words 50655 PT AVG   289.00 45438.70 s
10 # words 43420 PT AVG   302.13 2952.23 s
11 # words 35612 PT AVG   323.62 3835.00 s
12 # words 27669 PT AVG   330.19 5882.98 s
13 # words 19971 PT AVG   339.60 2712.98 s

इन संख्याओं के अनुसार, मेरा औसत स्कोर 257'800 के पास होना चाहिए

पिट स्कोर

अंत में मैंने रूबी को स्थापित किया, इसलिए अब मेरे पास एक 'आधिकारिक' स्कोर है:

    4       5       6       7       8       9      10      11      12      13   TOTAL
10700   16300   22000   25700   27400   30300   32000   33800   34700   34800   267700

मेरा इरादा कुछ ऐसा बनाने का था। काश मैं समाधान स्थान को सही मायने में कम नहीं कर पाता, इसलिए मैंने इसका अनुमान लगाया। और मेरा अजगर में है, तो यह भी धीमी है, हाहा। मैंने पहला अनुमान भी हार्डकोड किया। तुम्हारा निश्चित रूप से छोटे तार के लिए मेरा से बेहतर है। तुलना करने के लिए क्या आप इनपुट के एक ही सेट पर मेरे कार्यान्वयन के साथ परीक्षण कर सकते हैं? इसके अलावा हमारे पास पहले अनुमानों का एक अलग सेट है।
जस्टफुल

@ अन्याय ने लिंगो के साथ कुछ दौर की कोशिश की। मैंने गड्ढे के साथ जांच नहीं की (मेरे पास रूबी स्थापित नहीं है)। हमारे स्कोर करीब हैं, यह मेरे भाग्य की बात है।
edc65

आपका बेहतर है, मुझे लगता है कि आपके रिपोर्ट किए गए औसत से बेहतर है कि मैंने स्कोर किया। यद्यपि आपको अधिक समय लगता है।
जस्टफुल

यह अब तक का सबसे मजबूत खिलाड़ी लगता है। मैं आज बाद में आधिकारिक परिणाम चलाने जा रहा हूं, देखते रहो!
सिडरियस जूल

उफ़, ऊपर मेरी टिप्पणी के लिए सुधार, मैं भूल गया कि मेरा सबमिशन जावा में है।
बजे

5

जावा, 249700 अंक (मेरी परीक्षा में चीनी पर्ल गोथ)

अद्यतन रैंक सूची:

                        4 5 6 7 8 9 10 11 12 13 कुल
perl चीनी_पर__गॉथ.प्ल ६ chinese०० १२३०० १६ ९ ०० १ ९ २०० २३०००० २०० २०० २०० २००२०० ३२०० ३३०० ९ २००100३100००
जावा लिंगो 9400 14700 18900 21000 26300 28700 30300 32400 33800 34200 249700

यहां पुरानी रैंक सूची का उपयोग किया गया है pit.rb:

                        4 5 6 7 8 9 10 11 12 13 कुल
माणिक खिलाड़ी- example.rb 200 400 400 500 1800 1400 1700 1600 3200 4400 15600
माणिक खिलाड़ी- example2.rb 2700 3200 2500 4300 7300 6300 8200 10400 13300 15000 73200
माणिक खिलाड़ी- example3.rb 4500 7400 9900 13700 15400 19000 19600 22300 24600 27300 163700
perl चीनी_पर__गॉठ.प्ल ६४०० १४६०० १६५०० २००० २००५ २२६०० २००० २००२०० ३६०० ३०००० ३००00००१११००
जावा लिंगो 4800 13100 16500 21400 27200 29200 30600 32400 33700 36100 245000

** रैंकिंग **
1: जावा लिंगो (245000)
2: perl
3: माणिक खिलाड़ी- example3.rb (163700)
4: माणिक खिलाड़ी-example2.rb (73200)
5: माणिक खिलाड़ी-example.rb (15600)

@Chineseperlgoth की तुलना में, मैं छोटे शब्दों में खो जाता हूं (<6 वर्ण) लेकिन मैं लंबे शब्दों में जीतता हूं (> = 6 वर्ण)।

विचार @chineseperlgoth के समान है, यह सिर्फ इतना है कि मेरा मुख्य विचार अनुमान खोजने के बारे में है (एक ही लंबाई का कोई भी शब्द हो सकता है, जरूरी नहीं कि शेष संभावनाओं में से एक हो) जो अगले अनुमान के लिए सबसे अधिक जानकारी देता है।

वर्तमान में मैं अभी भी सूत्र के साथ खेल रहा हूं, लेकिन ऊपर के स्कोरबोर्ड के लिए, मैं उस शब्द को चुनता हूं जिसके लिए न्यूनतम उपज होगी:

-num_confusion * एन्ट्रापी

नवीनतम संस्करण अगले सर्वश्रेष्ठ अनुमान को खोजने के लिए विभिन्न स्कोरिंग का उपयोग करता है, जो वर्तमान अनुमान के बाद "एकल संभावना" की संख्या को अधिकतम कर रहा है। यह सभी संभावित उम्मीदवारों पर छंटनी की गई वर्डलिस्ट (समय बचाने के लिए) में सभी शब्दों की कोशिश करके किया जाता है, और देखें कि कौन सा अनुमान "एकल संभावना" का उत्पादन करने के लिए अधिक संभावित है (अर्थात, इस अनुमान के बाद केवल एक संभावित उत्तर होगा) अगला अनुमान।

उदाहरण के लिए यह रन:

नए दौर की शुरुआत, शब्द वरदान है
समझे: सियोरा
भेजा गया है?
समझे: सबसे ऊपर
भेजा गया: XOX? X
समझे: भिक्षुओं
प्रेषित: XO? XO
समझे: bewig
प्रेषित: OXXXX
समझे: वरदान
प्रेषित: OOOOO
गोल स्कोर 100 के साथ जीता

पहले तीन अनुमानों से, हम पहले से ही कहीं "n" के साथ "* oo * s" प्राप्त कर चुके हैं और हमें अभी भी और अधिक अक्षर जानने की जरूरत है। अब इस एल्गोरिथ्म की सुंदरता यह है कि शब्दों का अनुमान लगाने के बजाय जो उस रूप के समान हैं, यह शब्द का अनुमान लगाता है जिसका पिछले अनुमानों से कोई संबंध नहीं है, अधिक पत्र देने की कोशिश कर रहा है, उम्मीद है कि लापता पत्र का खुलासा कर रहा है। इस मामले में यह भी होता है कि लापता "बी" के लिए सही ढंग से स्थिति प्राप्त की जाए, और सही अंतिम अनुमान "वरदान" के साथ समाप्त होता है।

यहाँ कोड है:

import java.util.*;
import java.io.*;

class Lingo{
    public static String[] guessBestList = new String[]{
                                "",
                                "a",
                                "sa",
                                "tea",
                                "orae",
                                "seora", // 5
                                "ariose",
                                "erasion",
                                "serotina",
                                "tensorial",
                                "psalterion", // 10
                                "ulcerations",
                                "culteranismo",
                                "persecutional"};
    public static HashMap<Integer, ArrayList<String>> wordlist = new HashMap<Integer, ArrayList<String>>();

    public static void main(String[] args){
        readWordlist("wordlist.txt");
        Scanner scanner = new Scanner(System.in);
        int wordlen = Integer.parseInt(args[0]);
        int roundNum = 5;
        ArrayList<String> candidates = new ArrayList<String>();
        candidates.addAll(wordlist.get(wordlen));
        String guess = "";
        while(roundNum-- > 0){
            guess = guessBest(candidates, roundNum==4, roundNum==0);
            System.out.println(guess);
            String response = scanner.nextLine();
            if(isAllO(response)){
                break;
            }
            updateCandidates(candidates, guess, response);
            //print(candidates);
        }
    }

    public static void print(ArrayList<String> candidates){
        for(String str: candidates){
            System.err.println(str);
        }
        System.err.println();
    }

    public static void readWordlist(String path){
        try{
            BufferedReader reader = new BufferedReader(new FileReader(path));
            while(reader.ready()){
                String word = reader.readLine();
                if(!wordlist.containsKey(word.length())){
                    wordlist.put(word.length(), new ArrayList<String>());
                }
                wordlist.get(word.length()).add(word);
            }
        } catch (Exception e){
            System.exit(1);
        }
    }

    public static boolean isAllO(String response){
        for(int i=0; i<response.length(); i++){
            if(response.charAt(i) != 'O') return false;
        }
        return true;
    }

    public static String getResponse(String word, String guess){
        char[] wordChar = word.toCharArray();
        char[] result = new char[word.length()];
        Arrays.fill(result, 'X');
        for(int i=0; i<guess.length(); i++){
            if(guess.charAt(i) == wordChar[i]){
                result[i] = 'O';
                wordChar[i] = '_';
            }
        }
        for(int i=0; i<guess.length(); i++){
            if(result[i] == 'O') continue;
            for(int j=0; j<wordChar.length; j++){
                if(result[j] == 'O') continue;
                if(wordChar[j] == guess.charAt(i)){
                    result[i] = '?';
                    wordChar[j] = '_';
                    break;
                }
            }
        }
        return String.valueOf(result);
    }

    public static void updateCandidates(ArrayList<String> candidates, String guess, String response){
        for(int i=candidates.size()-1; i>=0; i--){
            String candidate = candidates.get(i);
            if(!response.equals(getResponse(candidate, guess))){
                candidates.remove(i);
            }
        }
    }

    public static int countMatchingCandidates(ArrayList<String> candidates, String guess, String response){
        int result = 0;
        for(String candidate: candidates){
            if(response.equals(getResponse(candidate, guess))){
                result++;
            }
        }
        return result;
    }

    public static String[] getSample(ArrayList<String> words, int size){
        String[] result = new String[size];
        int[] indices = new int[words.size()];
        for(int i=0; i<words.size(); i++){
            indices[i] = i;
        }
        Random rand = new Random(System.currentTimeMillis());
        for(int i=0; i<size; i++){
            int take = rand.nextInt(indices.length-i);
            result[i] = words.get(indices[take]);
            indices[take] = indices[indices.length-i-1];
        }
        return result;
    }

    public static String guessBest(ArrayList<String> candidates, boolean firstGuess, boolean lastGuess){
        if(candidates.size() == 1){
            return candidates.get(0);
        }
        String minGuess = candidates.get(0);
        int wordlen = minGuess.length();
        if(firstGuess && guessBestList[wordlen].length()==wordlen){
            return guessBestList[wordlen];
        }
        int minMatches = Integer.MAX_VALUE;
        String[] words;
        if(lastGuess){
            words = candidates.toArray(new String[0]);
        } else if (candidates.size()>10){
            words = bestWords(wordlist.get(wordlen), candidates, 25);
        } else {
            words = wordlist.get(wordlen).toArray(new String[0]);
        }
        for(String guess: words){
            double sumMatches = 0;
            for(String word: candidates){
                int matches = countMatchingCandidates(candidates, guess, getResponse(word, guess));
                if(matches == 0) matches = candidates.size();
                sumMatches += (matches-1)*(matches-1);
            }
            if(sumMatches < minMatches){
                minGuess = guess;
                minMatches = sumMatches;
            }
        }
        return minGuess;
    }

    public static String[] bestWords(ArrayList<String> words, ArrayList<String> candidates, int size){
        int[] charCount = new int[123];
        for(String candidate: candidates){
            for(int i=0; i<candidate.length(); i++){
                charCount[(int)candidate.charAt(i)]++;
            }
        }
        String[] tmp = (String[])words.toArray(new String[0]);
        Arrays.sort(tmp, new WordComparator(charCount));
        String[] result = new String[size+Math.min(size, candidates.size())];
        String[] sampled = getSample(candidates, Math.min(size, candidates.size()));
        for(int i=0; i<size; i++){
            result[i] = tmp[tmp.length-i-1];
            if(i < sampled.length){
                result[size+i] = sampled[i];
            }
        }
        return result;
    }

    static class WordComparator implements Comparator<String>{
        int[] charCount = null;

        public WordComparator(int[] charCount){
            this.charCount = charCount;
        }

        public Integer count(String word){
            int result = 0;
            int[] multiplier = new int[charCount.length];
            Arrays.fill(multiplier, 1);
            for(char chr: word.toCharArray()){
                result += multiplier[(int)chr]*this.charCount[(int)chr];
                multiplier[(int)chr] = 0;
            }
            return Integer.valueOf(result);
        }

        public int compare(String s1, String s2){
            return count(s1).compareTo(count(s2));
        }
    }
}

बहुत बढ़िया, यह प्रविष्टि गंभीरता से मजबूत है! मुझे याद है कि टीवी शो में मानव खिलाड़ियों को एक समान रणनीति का उपयोग करते हुए देखा गया था जब वे वर्तमान सुरागों से एक शब्द का अनुमान लगाने में असमर्थ थे।
सिडराइजर

3

पर्ल

सुधार के लिए अभी भी कुछ जगह है लेकिन कम से कम यह शामिल उदाहरण खिलाड़ियों को हरा देता है :)

मान लिया गया है कि शब्दसूची को कैशिंग करने के लिए वर्तमान निर्देशिका तक पहुँच (इसे थोड़ा तेज़ चलाने के लिए); wordlist.lenN.storका उपयोग कर फ़ाइलें पैदा करेगा Storable। यदि यह एक समस्या है, read_cached_wordlistतो कोड को निकालें और केवल उपयोग करने के लिए बदलें read_wordlist

व्याख्या

सबसे पहले, मैं वर्तमान वर्डलिस्ट ( build_histogram) में सभी शब्दों में पत्र आवृत्तियों का एक हिस्टोग्राम बनाता हूं । फिर मुझे अपना अगला अनुमान चुनने की जरूरत है - जो कि किया जाता है find_best_word। स्कोरिंग एल्गोरिथ्म बस हिस्टोग्राम मूल्यों को एक साथ जोड़ रहा है, पहले से देखे गए अक्षरों को लंघन कर रहा है। यह मुझे एक शब्द देता है जिसमें शब्दसूची में सबसे अधिक अक्षर होते हैं। यदि किसी दिए गए स्कोर के साथ एक से अधिक शब्द हैं, तो मैं एक यादृच्छिक रूप से चुनता हूं। एक शब्द मिल जाने के बाद, मैं इसे गेम इंजन को भेजता हूं, उत्तर पढ़ें और फिर इसके साथ कुछ उपयोगी करने की कोशिश करें :)

मैं शर्तों का एक सेट बनाए रखता हूं, अर्थात, ऐसे अक्षर जो किसी शब्द में दिए गए पद पर हो सकते हैं। शुरू में, यह सिर्फ सरल है (['a'..'z'] x $len), लेकिन यह उत्तर में दिए गए संकेतों पर आधारित है (देखें update_conds)। मैं उन स्थितियों में से एक रेगेक्स का निर्माण करता हूं और इसके माध्यम से वर्डलिस्ट को फ़िल्टर करता हूं।

परीक्षणों के दौरान मुझे पता चला है कि उपरोक्त फ़िल्टरिंग ?बहुत अच्छी तरह से संभाल नहीं करता है , इसलिए दूसरा फ़िल्टर ( filter_wordlist_by_reply)। यह इस तथ्य का लाभ उठाता है कि एक पत्र जैसा कि ?एक अलग स्थिति में शब्द में होता है, और तदनुसार शब्दसूची को फ़िल्टर करता है।

इन चरणों को मुख्य लूप के प्रत्येक पुनरावृत्ति के लिए दोहराया जाता है, जब तक कि समाधान नहीं मिलता है (या अब स्टड से पढ़ना संभव नहीं है, जिसका अर्थ है विफलता)।

कोड

#!perl
use strict;
use warnings;
use v5.10;
use Storable;

$|=1;

sub read_wordlist ($) {
    my ($len) = @_;
    open my $w, '<', 'wordlist.txt' or die $!;
    my @wordlist = grep { chomp; length $_ == $len } <$w>;
    close $w;
    \@wordlist
}

sub read_cached_wordlist ($) {
    my ($len) = @_;
    my $stor = "./wordlist.len$len.stor";
    if (-e $stor) {
        retrieve $stor
    } else {
        my $wl = read_wordlist $len;
        store $wl, $stor;
        $wl
    }
}

sub build_histogram ($) {
    my ($wl) = @_;
    my %histo = ();
    for my $word (@$wl) {
        $histo{$_}++ for ($word =~ /./g);
    }
    \%histo
}

sub score_word ($$) {
    my ($word, $histo) = @_;
    my $score = 0;
    my %seen = ();
    for my $l ($word =~ /./g) {
        if (not exists $seen{$l}) {
            $score += $histo->{$l};
            $seen{$l} = 1;
        }
    }
    $score
}

sub find_best_word ($$) {
    my ($wl, $histo) = @_;
    my @found = (sort { $b->[0] <=> $a->[0] } 
                 map [ score_word($_, $histo), $_ ], @$wl);
    return undef unless @found;
    my $maxscore = $found[0]->[0];
    my @max;
    for (@found) {
        last if $_->[0] < $maxscore;
        push @max, $_->[1];
    }
    $max[rand @max]
}

sub build_conds ($) {
    my ($len) = @_;
    my @c;
    push @c, ['a'..'z'] for 1..$len;
    \@c
}

sub get_regex ($) {
    my ($cond) = @_;
    local $" = '';
    my $r = join "", map { "[@$_]" } @$cond;
    qr/^$r$/
}

sub remove_cond ($$$) {
    my ($conds, $pos, $ch) = @_;
    return if (scalar @{$conds->[$pos]} == 1);
    return unless grep { $_ eq $ch } @{$conds->[$pos]};
    $conds->[$pos] = [ grep { $_ ne $ch } @{$conds->[$pos]} ]
}

sub add_cond ($$$) {
    my ($conds, $pos, $ch) = @_;
    return if (scalar @{$conds->[$pos]} == 1);
    return if grep { $_ eq $ch } @{$conds->[$pos]};
    push @{$conds->[$pos]}, $ch
}

sub update_conds ($$$$) {
    my ($word, $reply, $conds, $len) = @_;
    my %Xes;
    %Xes = ();
    for my $pos (reverse 0..$len-1) {
        my $r = substr $reply, $pos, 1;
        my $ch = substr $word, $pos, 1;

        if ($r eq 'O') {
            $conds->[$pos] = [$ch]
        }

        elsif ($r eq '?') {
            for my $a (0..$len-1) {
                if ($a == $pos) {
                    remove_cond $conds, $a, $ch
                } else {
                    unless (exists $Xes{$a} and $Xes{$a} eq $ch) {
                        add_cond($conds, $a, $ch);
                    }
                }
            }
        }

        elsif ($r eq 'X') {
            $Xes{$pos} = $ch;
            for my $a (0..$len-1) {
                remove_cond $conds, $a, $ch
            }
        }
    }
}

sub uniq ($) {
    my ($data) = @_;
    my %seen; 
    [ grep { !$seen{$_}++ } @$data ]
}

sub filter_wordlist_by_reply ($$$) {
    my ($wl, $word, $reply) = @_;
    return $wl unless $reply =~ /\?/;
    my $newwl = [];
    my $len = length $reply;
    for my $pos (0..$len-1) {
        my $r = substr $reply, $pos, 1;
        my $ch = substr $word, $pos, 1;
        next unless $r eq '?';
        for my $a (0..$len-1) {
            if ($a != $pos) {
                if ('O' ne substr $reply, $a, 1) {
                    push @$newwl, grep { $ch eq substr $_, $a, 1 } @$wl
                }
            }
        }
    }
    uniq $newwl
}

my $len = $ARGV[0] or die "no length";
my $wl = read_cached_wordlist $len;
my $conds = build_conds $len;

my $c=0;
do {
    my $histo = build_histogram $wl;
    my $word = find_best_word $wl, $histo;
    die "no candidates" unless defined $word;
    say $word;
    my $reply = <STDIN>; 
    chomp $reply;
    exit 1 unless length $reply;
    exit 0 if $reply =~ /^O+$/;
    update_conds $word, $reply, $conds, $len;
    $wl = filter_wordlist_by_reply $wl, $word, $reply;
    $wl = [ grep { $_ =~ get_regex $conds } @$wl ]
} while 1

1
मेरे नियम मूल रूप से डिस्क पर लिखने से मना करते हैं, लेकिन मैं इसे शब्द सूची के कैशिंग की अनुमति देने के लिए एक अपवाद बनाता हूं, क्योंकि मुझे जो बड़ा मिला वह पूरी तरह से परीक्षण करने के लिए धीमी गति से बनाता है :)
SirDarius

यह प्रविष्टि मेरे अपने (अभी तक अप्रकाशित) प्रयासों से बेहतर काम करती है। क्या आप अपने एल्गोरिथ्म को थोड़ा समझा सकते हैं?
सिडराइजर

मैंने एक छोटा स्पष्टीकरण जोड़ा है; कोड स्वरूपण को थोड़ा ठीक किया।
चीनी पर्ल गोथ

@SirDarius: मुझे नहीं लगता कि कोई भी नुकसान होगा अगर कोई विशेष परीक्षण एक शब्द सूची का उपयोग करता है जिसमें केवल उचित लंबाई की प्रविष्टियाँ होती हैं। यद्यपि किसी प्रोग्राम के लिए फ़ाइल के भीतर शब्दों को अनदेखा करना कठिन नहीं होना चाहिए, जिसकी लंबाई निर्दिष्ट के अलावा है, ऐसे शब्दों का अस्तित्व कम से कम धीमी गति से परीक्षण करना होगा। इसके अलावा, मुझे आश्चर्य है कि अगर एक वैकल्पिक कार्यक्रम को निर्दिष्ट करने की अनुमति देने में मूल्य होगा, जो एक शब्द सूची और एन दिया जाता है, तो मानक आउटपुट के लिए एक शब्द सूची भेजेगा जो कुछ भी फैशन में सबसे उपयोगी होता है ...
Supercat

... और मुख्य कार्यक्रम को एक कच्चे शब्द सूची के बजाय इसका उपयोग करने की अनुमति दें (इसलिए यदि कुछ पूर्व विश्लेषण की आवश्यकता है, तो इसे प्रत्येक शब्द की लंबाई के लिए केवल एक बार किया जाना चाहिए, बजाय एक बार खेल के)।
सुपरकट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.