首页
社区
课程
招聘
[讨论]一个有趣的小游戏
发表于: 2005-6-8 15:13 7101

[讨论]一个有趣的小游戏

RoBa 活跃值
16
2005-6-8 15:13
7101
文曲星上的经典猜数游戏,电脑随机出一个四位数(没有重复数字)让你来猜,每次你说出猜的数后,返回信息告诉你有几个数位置对了数字也对了,有几个数数字对了但位置不对。比方说原数是5678,你猜了一个3658,那么返回2A 1B,表示有两个数(6和8)位置和数字都对了,还有一个数(5)对了但位置不对。当最后返回4A 0B时表示你猜中了这个数。

在我们学校的BBS上有这个,做成了一个赌博游戏,1次猜出赢100倍,2次猜出赢50倍,3次赢20倍,4次赢5倍,5次赢3倍,6次不输不赢,6次以上的就会把押的钱输掉。我们ACM队里的闲着没事在研究怎么写一个程序帮人来猜数,能挣到更多的钱。

我的初步想法是第一次猜0123,然后根据返回值,去掉不符合条件的数,再在所有符合条件的数里面猜最小的,直到猜出来为止。后来做了一些改进,不是在所有符合条件的数里猜最小的,而是把所有可猜的数都试一遍,看猜哪个数时能去掉更多的数,就选这个数来猜,算法慢了一些 O(N^2) ,但结果更好了一些。

下面我的程序的统计结果:(四位不重复的数共5040个)

1次 1      
2次 13     
3次 108   
4次 619   
5次 2004
6次 1932  
7次 360   
8次 3      

失败的概率是0.07,平均期望大概是2.6左右,也就是说用这个程序去玩,能赢回2.6倍的钱。

还有一位也写了一个程序(算法我记不太清了),结果是:

1 次 1
2 次 4
3 次 44
4 次 459
5 次 2375  
6 次 2048  
7 次 108  
8 次 1  

这个算法虽然失败概率很低,只有0.02,但大部分的数都集中在6上,平均期望只有2.0左右。

请大家可以想想还有没有更好的办法,我听说有人用哈夫曼树可以保证在6步之内猜到,不知道是怎么做的。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
2
高中时候上课每天跟同桌玩这个,每次都缴尽脑汁。
可是从来没去想过怎么做更快
2005-6-8 15:25
0
雪    币: 209
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
换一种思路,找出的随机函数是怎么产生的(百倍的难度,带来的是百倍利润),呵呵,有点题外话了
2005-6-11 20:59
0
雪    币: 10791
活跃值: (3689)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
1 0123
2 1234
3 2345
4 6789
然后根据返回结果判断,算法还没有去研究。
要讨论最好公布算法!
2005-6-18 15:43
0
雪    币: 10791
活跃值: (3689)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
猜数字游戏-人出题,电脑猜(转贴)
/*
* 文件名称: GuessNum.h
* 程序描述:
*    常见的小游戏【猜数字】的智能解法:
*    猜数字游戏:    即有四位十进制数字,一般可猜8次
*                每次返回aAbB(A表示数字正确并且位置正确,B表示数字正确但位置不正确)
*                如:假设要猜的数字是1234,如果游戏者猜0134即返回2A1B(3、4为A,1为B)
*
*    算法:每次选取熵最大的数字进行猜测,即:贪心算法
*
*    还有一个程序使用决策树的方法,参见GuessNumAll.cpp
*    另外还有一个程序GuessNumGame.cpp用来模拟猜数字游戏
*/
#include <vector>
#include <list>
#include <string>
using namespace std;

class           GuessNum
{
/* */
public:
    GuessNum(void);
    void                Guess(void);
    void                PrintStack(void);

/* */
private:
    bool                ConfirmAB(string first, string second, string ab);
    bool                GuessNext(void);
    int                 GetABValue(string first, string second);
    const static int    m_base[5];
    string              m_lastGuess;
    list<string>        m_setAll;
    list<string>        m_setCurr;
    vector<string>      m_stack;
    int                 m_times;
};
/*
[01/19-21:54:50]
猜数字游戏:
? 即有四位十进制数字,一般可猜8次
? 每次返回aAbB(A表示数字正确并且位置正确,B表示数字正确但位置不正确)
? 如:假设要猜的数字是1234,如果游戏者猜0134即返回2A1B(3、4为A,1为B)

思路:
? 模拟人猜数字的过程,先构造一个集合包括所有可能的数字(10*9*8*7=5040种),先乱猜一个,根据返回xAxB来把原来集合里面的不符合的都删掉,然后根据选取下一个。选下一个的原则是:选最能区分剩下的集合的那个数,即:被选择的那个数得到的返回值为xAxB中任意一种的概率差不多。
? 我使用信息量的方法,即:
?? 对集合set(size个)中每一个数字n,用n的每一种返回值(从0a0b到4a0b,共有15种)做删减,看各剩下多少个,用pi表示第i种返回值得情况,然后根据信息量公式:info(n) = ∑(pi/size) * log(pi/size)计算,然后取max(info(i)) i=0..size;最大的那个info(n)对应的数字就是被选择的数字。

下面是一个由电脑猜数字的程序  猜(0-9)中4个全排列中的一个   现在我想修改成(1-9)中选4个全排列中一个.  
-------------------
我修改的思路是:缩小全集范围 把带0的4位数字排除 下面红色为我修改的部分
               修改第一次随机猜过滤带0的随机数
但修改结果:随机数仍然有0出现.全集也仍然有0  这里我想惭愧我之前没有学C++有些看不懂 但对C比较熟悉点.谁能帮小弟弟 指点迷津  或提出你修改的方法  小弟不胜感谢.因为急需这个程序.请帮我修改下
/*
* 文件名称: GuessNum.cpp
* 程序描述: 实现GuessNum类
*/
#pragma warning(disable : 4786)
#include <iostream>
#include <algorithm>
#include <ctime>
#include <strstream>
#include <cmath>
#include "GuessNum.h"

const int GuessNum::    m_base[5]={ 0, 5, 9, 12, 14 };

/* */

GuessNum::GuessNum(void)
{
    m_times=0;

    //产生m_setAll
    unsigned int    num=123;
    char            a[10];
    while(num < 10000)
    {
        bool    valid=true;                 //表示数字没有重复
        sprintf(a, "%04u", num);
        for(int i=0; i < 4; i++)
        {
            for(int j=i + 1; j < 4; j++)
                if(a[i] == a[j]) valid=false;
        }

        for(int q=0; q < 4; q++)
            if(a[q] == '0') valid=false;    //红色部分为我添加的内容想缩小全集范围排除带0的数字
        if(valid) m_setAll.push_back(a);
        num++;
    }   //#while(num)
}       //#GuessNum()

/* */
void GuessNum::Guess(void)
{
    //重置数据集
    m_setCurr.clear();

    list<string>::iterator  itCopy=m_setAll.begin();
    while(itCopy != m_setAll.end()) m_setCurr.push_back(*itCopy++);
    m_stack.clear();
    m_times=0;

    //随机产生第一次猜测
    srand((unsigned)time(NULL));            //随机数种子
    bool            valid=false;
    unsigned int    iCode;
    while(!valid)
    {
        iCode=rand();
        while(iCode > 9999) iCode/=10;

        char    a[10];
        sprintf(a, "%04u", iCode);
        valid=true;
        for(int i=0; i < 4; i++)
            for(int j=i + 1; j < 4; j++)
                if(a[i] == a[j]) valid=false;
        for(int q=0; q < 4; q++)
            if(a[q] == '0') valid=false;    //红色部分为我添加的内容想排除带0的随机4位数
        if(valid) m_lastGuess=a;
    }

    while(!GuessNext());
}   //#Guess()

/* */
bool GuessNum::GuessNext(void)
{
    m_times++;

    //进行猜测,获取猜测结果
    string  result;
    cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
    PrintStack();
    cout << "第 " << m_times << " 次猜测: " << m_lastGuess << endl;
    cout << "请输入结果[?A?B]:";

    bool    valid=false;
    while(!valid)
    {
        cin >> result;

        //判断输入是否合法
        if(result.length() == 4 &&
                   result[0] >= '0' &&
                   result[0] <= '4' &&
                   result[2] >= '0' &&
                   result[2] <= '4' &&
                   (result[0] + result[2]) <= ('0' + '4') &&
                   toupper(result[1]) == 'A' &&
                   toupper(result[3]) == 'B')
        {
            valid=true;
        }

        if(!valid) cout << "输入不合法,请重新输入:";
    }   //#while

    //将结果入栈
    strstream   strm;
    strm << "第 " << m_times << "次: " << m_lastGuess << " -> " << result << endl;

    string  strTemp;
    getline(strm, strTemp);
    m_stack.push_back(strTemp);
    if(result[0] != '4')
    {
        //尚未猜对, 进行数据集的约简
        list<string>::iterator  itRemove;
        itRemove=m_setCurr.begin();
        while(itRemove != m_setCurr.end())
        {
            if(!ConfirmAB(m_lastGuess, *itRemove, result))
                itRemove=m_setCurr.erase(itRemove);
            else
                itRemove++;
        }

        //@@
        if(m_setCurr.size() == 0)
        {
            cerr << "你检查一下,肯定是哪一步输入错误!" << endl;
            cerr << "程序结束" << endl;
            exit(1);
        }
        else if(m_setCurr.size() == 1)
        {
            m_lastGuess=m_setCurr.front();
            return false;
        }

        //@@
        cout << "m_setAll.size() = " << m_setAll.size() << endl;
        cout << "m_setCurr.size() = " << m_setCurr.size() << endl;

        //产生下一个猜测数据
        double                  eMax=0;     //max entropy, 最大信息量
        string                  strMax;     //提供最大信息量的那个数串
        list<string>::iterator  itEn=m_setCurr.begin();

        //计算用每一个数来做区分时的信息量,取提供最大信息量的那个
        double                  eCurr=0;
        int                     abList[15]; //从0a0b到4a0b共15种情况
        memset(abList, 0, 15 * sizeof(int) / sizeof(char));
        while(itEn != m_setCurr.end())
        {
            list<string>::iterator  itTemp=m_setCurr.begin();
            while(itTemp != m_setCurr.end())
            {
                abList[GetABValue(*itEn, *itTemp)]++;
                itTemp++;
            }

            //统计熵

            //@@

            //            cout << "sigh.." << *itEn << endl;
            double  eTemp=0;
            double  eDiv=0;
            for(int i=0; i < 15; i++)
            {
                if(abList[i] != 0)          //避免0*log0
                {
                    eDiv=double(abList[i]) / double(m_setCurr.size());
                    eTemp+= -eDiv * log(eDiv);
                }
            }

            if(eTemp > eMax)
            {
                cout << "here eMax = " << eMax << endl;
                eMax=eTemp;
                strMax=*itEn;
            }

            itEn++;
        }   //#while(itEn)
        m_lastGuess=strMax;
        return false;
    }       //#if(result[0] != 4)
    else    // result[0] == 4
    {
        cout << "\n答案是: " << m_lastGuess << endl;
        cout << "一共用了 " << m_times << "次" << endl;
        PrintStack();
        return true;
    }
}           //#GuessNext()

/* */
void GuessNum::PrintStack(void)
{
    for(int i=0; i < m_stack.size(); i++) cout << m_stack[i] << endl;
}   //#PrintStack()

/* */
inline bool GuessNum::ConfirmAB(string first, string second, string ab)
{
    int a=0, b=0;
    for(int i=0; i < 4; i++)
    {
        for(int j=0; j < 4; j++)
            if(first[i] == second[j])
                if(i == j)
                    a++;
                else
                    b++;
    }

    if(ab[0] == char(a + '0') && ab[2] == char(b + '0'))
        return true;
    else
        return false;
}   //#ConfirmAB()

/* */
inline int GuessNum::GetABValue(string first, string second)
{
    int a=0;
    int b=0;
    for(int i=0; i < 4; i++)
    {
        for(int j=0; j < 4; j++)
            if(first[i] == second[j])
                if(i == j)
                    a++;
                else
                    b++;
    }

    return m_base[a] + b;
}   //#GetABValue()
//测试程序
#include <iostream>
#include "GuessNum.h"
using namespace std;

int main()
{
    GuessNum gn;
    cout << "GuessNum object created..." << endl;
    gn.Guess();
    return 0;
}
2005-6-19 12:37
0
游客
登录 | 注册 方可回帖
返回
//