C趣味編程百例(30)人機(jī)猜數(shù)游戲(2)

字號:

92.人機(jī)猜數(shù)游戲(2)
     將以上游戲雙方倒一下,請人想一個(gè)四位的整數(shù),計(jì)算機(jī)來猜,人給計(jì)算機(jī)提示信息,最終看計(jì)算機(jī)用幾次猜出一個(gè)人“想”的數(shù)。請編程實(shí)現(xiàn)。
    *問題分析與算法設(shè)計(jì)
     解決這類問題時(shí),計(jì)算機(jī)的思考過程不可能象人一樣具完備的推理能力,關(guān)鍵在于要將推理和判斷的過程變成一種機(jī)械的過程,找出相應(yīng)的規(guī)則,否則計(jì)算機(jī)難以完成推理工作。
     基于對問題的分析和理解,將問題進(jìn)行簡化,求解分為兩個(gè)步聚來完成:首先確定四位數(shù)字的組成,然后再確定四位數(shù)字的排列順序??梢粤谐鋈缦乱?guī)則:
     1)分別顯示四個(gè)1,四個(gè)2,......,四個(gè)0,確定四位數(shù)字的組成。
     2)依次產(chǎn)生四位數(shù)字的全部排列(依次兩兩交換全部數(shù)字的位置)。
     3)根據(jù)人輸入的正確數(shù)字及正確位置的數(shù)目,進(jìn)行分別處理:
     (注意此時(shí)不出現(xiàn)輸入的情況,因?yàn)樵谒膫€(gè)數(shù)字已經(jīng)確定的情況下,若有3個(gè)位置正確,則第四個(gè)數(shù)字的位置必然也是正確的)
     若輸入4:游戲結(jié)束。
     判斷本次輸入與上次輸入的差值
     若差為2:說明前一次輸入的一定為0,本次輸入的為2,本次交換的兩個(gè)數(shù)字的位置是正確的,只要交換另外兩個(gè)沒有交換過的數(shù)字即可結(jié)束游戲。
     若差為-2:說明前一次輸入的一定為2,本次的一定為0。說明剛交換過的兩個(gè)數(shù)字的位置是錯(cuò)誤的,只要將交換的兩個(gè)數(shù)字位置還原,并交換另外兩個(gè)沒有交換過的數(shù)字即可結(jié)束游戲。
     否則:若本次輸入的正確位置數(shù)<=上次的正確位置數(shù)
     則恢復(fù)上次四位數(shù)字的排列,控制轉(zhuǎn)3)
     否則:將本次輸入的正確位置數(shù)作為“上次輸入的正確位置數(shù)”,控制轉(zhuǎn)3)。
    *程序與程序注釋
    #include
    #include
    void bhdy(int s,int b);
    void prt();
    int a[4],flag,count;
    void main()
    {
     int b1,b2,i,j,k=0,p,c;
     printf("Game guess your number in mind is # # # #.\n");
     for(i=1;i<10&&k<4;i++) /*分別顯示四個(gè)1~9確定四個(gè)數(shù)字的組成*/
     {
     printf("No.%d:your number may be:%d%d%d%d\n",++count,i,i,i,i);
     printf("How many digits have bad correctly guessed:");
     scanf("%d",&p); /*人輸入包含幾位數(shù)字*/
     for(j=0;j     a[k+j]=i; /*a[]:存放已確定數(shù)字的數(shù)組*/
     k+=p; /*k:已確定的數(shù)字個(gè)數(shù)*/
     }
     if(k<4) /*自動(dòng)算出四位中包的個(gè)數(shù)*/
     for(j=k;j<4;j++)
     a[j]=0;
     i=0;
     printf("No.%d:your number may be:%d%d%d%d\n",++count,a[0],a[1],a[2],a[3]);
     printf("How many are in exact positions:"); /*順序顯示四位數(shù)字*/
     scanf("%d",&b1); /*人輸入有幾位位置是正確的*/
     if(b1==4){prt();exit(0);} /*四位正確,打印結(jié)果。結(jié)束游戲*/
     for(flag=1,j=0;j<3&&flag;j++) /*實(shí)現(xiàn)四個(gè)數(shù)字的兩兩(a[j],a[k]交換*/
     for(k=j+1;k<4&&flag;k++)
     if(a[j]!=a[k])
     {
     c=a[j];a[j]=a[k];a[k]=c; /*交換a[j],a[k]*/
     printf("No.%d:Your number may be: %d%d%d%d\n",++count,a[0],a[1],a[2],a[3]);
     printf("How many are in exact positins:");
     scanf("%d",&b2); /*輸入有幾個(gè)位置正確*/
     if(b2==4){prt();flag=0;} /*若全部正確,結(jié)束游戲*/
     else if(b2-b1==2)bhdy(j,k); /*若上次與本次的差為2,則交換兩個(gè)元素即可結(jié)束*/
     else if(b2-b1==-2) /*若上次與本次的差為-2,則說明已交換的(a[j],a[k])是錯(cuò)誤的
     將(a[j],a[k]還原后,只要交換另外兩個(gè)元素即可結(jié)束游戲*/
     {
     c=a[j];a[j]=a[k];a[k]=c;
     bhdy(j,k);
     }
     else if(b2<=b1)
     {
     c=a[j];a[j]=a[k];a[k]=c; /*恢復(fù)交換的兩個(gè)數(shù)字*/
     }
     else b1=b2; /*其它情況則將新輸入的位置信息作為上次的位置保存*/
     }
     if(flag) printf("You input error!\n"); /*交換結(jié)果仍沒結(jié)果,只能是人輸入的信息錯(cuò)誤*/
    }
    void prt() /*打印結(jié)果,結(jié)束游戲*/
    {
     printf("Now your number must be %d%d%d%d.\n",a[0],a[1],a[2],a[3]);
     printf("Game Over\n");
    }
    void bhdy(int s,int b)
    {
     int i,c=0,d[2];
     for(i=0;i<4;i++) /*查找s和b以外的兩個(gè)元素下標(biāo)*/
     if(i!=s&&i!=b) d[c++]=i;
     i=a[d[1>;a[d[1>=a[d[0>; a[d[0>=i; /*交換除a[s]和a[b]以外的兩個(gè)元素*/
     prt(); /*打印結(jié)果,結(jié)束游戲*/
     flag=0;
    }
    *運(yùn)行示例假設(shè)人想的四位數(shù)是:7215
    Game Begin
    Now guess your number in mind is # # # #.
    No.1:your number may be:1111
    *問題的進(jìn)一步討論
     本程序具有邏輯結(jié)構(gòu)清析、算法簡單正確的優(yōu)點(diǎn),但在接受人的輸入信息時(shí)缺少必要的出錯(cuò)保護(hù)功能,同時(shí)在進(jìn)行第三步推理過程中沒有保留每次猜出的數(shù)字位置信息及人輸入的回答,這樣對于每次人輸入的信息就無法進(jìn)行合法性檢查,即無法檢查人的輸入信息是否自相矛盾;同晨也無法充分利用前面的結(jié)果。
     這些缺陷是可以改進(jìn)的,但最后一個(gè)問題改進(jìn)難度較大,留給大家自己去完成。
    *思考題
     “一條龍游戲”。在一個(gè)3×3的棋盤上,甲乙雙方進(jìn)行對棄,雙方在棋盤上輪流放入棋子,如果一方的棋子成一直線(橫、豎或斜線),則該方贏。請編寫該游戲程序?qū)崿F(xiàn)人與機(jī)器的比賽。比賽結(jié)果有三種:輸、贏或平。
     在編程過程中請首先分析比賽中怎樣才能獲勝,找出第一步走在什么位置就最可能贏。