劣質代碼評析——猜數字問題(下)


前文鏈接:劣質代碼評析——猜數字問題(上)
【重構】

  這個問題的解決並不復雜,最多只需要三個步驟:

  1.  生成無重復數字的四位整數;
  2.  重復猜數字最多10次,猜中則宣布勝利,程序結束;
  3.  10次沒猜中,宣布失敗,程序結束。

  用C語言描述這個過程應該是這樣:

#define TIMES 10  //最多10次 
int main( void )
{
    int  count;
    //1. 生成無重復數字的四位整數;
    for( count = 0 ; count < TIMES ; count ++ )  //2. 重復猜數字最多10次,
    {
         //猜中則宣布勝利,程序結束;
    }
    //3. 10次沒猜中,宣布失敗,程序結束。  
    return 0;  
}

   現在認真審視一下這個結構,思考一下它有無漏洞。這件事情很值得去做,千萬不要錯在起跑線上,否則后面的工作可能都是無用功。
  沒有什么漏洞吧?得到這個結論並不困難,因為這本身就是一個簡單的問題。
  接下來補充完善這個代碼。
  寫代碼可以從前寫到后也可以從后向前寫,沒有一定之規。可以先寫難寫的部分也可以先完成容易的部分。下面的代碼完成了較為簡單的部分。

#include <stdio.h>

#define TIMES 10  //最多10次 

int main( void )
{
    int  count;
    //1. 生成無重復數字的四位整數;
    for( count = 0 ; count < TIMES ; count ++ )  //2. 重復猜數字最多10次
    {
         //猜數字
         //if( 猜中 ) 
         {
               puts("恭喜你猜對了");     //宣布勝利
               return 0;                 //程序結束;

         }
    }
    //3. 10次沒猜中
    printf("連續%d次你都沒猜中,抱歉,游戲結束\n",TIMES); //宣布失敗
    return 0;                                             //程序結束。
}

   這個代碼盡管沒有完成全部功能,但卻已經可以進行測試。測試之后繼續完善
  //1. 生成無重復數字的四位整數;
部分。
  這部分首先應該到考慮這個四位整數的存儲問題。考慮到后面的算法需要按位進行比較,因此將其數據結構選擇為

#define NUM 4     //正整數位數

int main( void )
{
    int  dig[NUM]; 
    /*……*/
}

 即按位分別存儲。
  由於需要程序模擬隨機效果,所以需要首先初始化種子數(seed)。否則程序每次運行的結果都將一樣:

#include <stdlib.h> 
#include <time.h>

srand( (unsigned) time( NULL ) );

   接下來要做的事情是利用rand()函數生成一個各位不同的四位偽隨機數,並將其分解然后存入dig數組,這事情不可能一蹴而就,所以這里大而化之地把這些任務交給一個函數完成

generate( dig , NUM );

   這樣,代碼就演化成為:

#include <stdio.h>
#include <stdlib.h> 
#include <time.h>

#define TIMES 10  //最多10次 
#define NUM 4     //正整數位數

int main( void )
{
    int  dig[NUM]; //存放四位整數 
    int  count;
    //1. 生成無重復數字的四位整數;
    srand( (unsigned)time( NULL ) ); //初始化seed 
    generate( dig , NUM );           //生成無重復數字的四位整數,分解存入dig數組 
    
    for( count = 0 ; count < TIMES ; count ++ )  //2. 重復猜數字最多10次,
    {
         //猜數字
         //if( 猜中 ) 
         {
               puts("恭喜你猜對了");     //宣布勝利
               return 0;                 //程序結束;

         }
    }
    //3. 10次沒猜中
    printf("連續%d次你都沒猜中,抱歉,游戲結束\n",TIMES); //宣布失敗
    return 0;                                             //程序結束。
}

   generate()函數首要的任務是生成一個四位偽隨機數,這可以通過rand() % ( 10000 - 1000 - 1 ) + 1000 這個表達式實現。之后需要檢查這個四位數是否有重復數字,如果沒有重復則將其分解存入數組。大體上這樣

do
{
   生成一個四位數
}
while(四位數有重復數字);

     分解存儲;

  但是為了更容易地判斷四位數是否有重復數字,一個技巧性的寫法是

do
{
   生成一個四位數;
   分解存儲;
}
while(四位數有重復數字);

   因此,generate()的這個函數的原型及定義為

#define MIN_5  10000
#define MIN_4  1000
#define TRUE  1
#define FALSE   0

void generate( int [] , int );

void generate( int d[] , int n )
{
     
     do
     {
         int temp = rand() % ( MIN_5 - MIN_4 - 1 ) + MIN_4 ; //生成一個四位數      
         resolve( temp , d , n );                           //分解存儲; 
     }
     while( be_reduplicate( d , n )  ==  TRUE );  //四位數有重復數字
}

   其中的resolve()函數的作用是將一個正整數分解並按次序存入數組:

 

#define TEN   10

void resolve( int ,  int [] , int );
void resolve( int t ,  int d[] , int n )
{

     while( n-- > 0 )
     {
         d[n] = t % TEN ;
         t /= TEN ;   
     }
}

 

  be_reduplicate()函數判斷四位數字中是否有重復,如果有重復數字則再調用rand()重新生成。

 


int be_reduplicate( int [] , int ); int be_reduplicate( int d[] , int n ) { int i; for( i = 0 ; i < n - 1 ; i ++ ) { int j ; for( j = i + 1 ; j < n ; j ++ ) if( d[i] == d[j] ) return TRUE ; } return FALSE ; }

    至此,//1. 生成無重復數字的四位整數;部分功能全部完成。下面繼續完成
  //2. 重復猜數字最多10次,
中尚未完成的部分。
  “//猜數字”的功能要求用戶輸入所猜的數,因此需要為這個輸入的數據預備存儲空間,所以

int g_dig[NUM];

   “//猜數字”這個過程本身可以用一個函數實現

void guess ( int [] , int ) ;
int main( void ) 
{ 
 /*……*/
   {
        int g_dig[NUM];
        
        guess ( g_dig , NUM ) ; //猜數字
    }
 /*……*/
}
void guess ( int d[] , int n )
{
     int i ;
     printf("輸入你猜的數:");
     for( i = 0 ; i < n ; i ++ )
          scanf("%1d" , d + i );     
}

   guess ()中的scanf("%1d" , d + i );是一種較為簡潔的實現方法,但是這種方法的健壯性並不夠。一旦用戶輸入了非十進制數字的非空白字符,就會出現問題。這個不足將在后面予以改進。
  最后編寫“//if( 猜中 )”部分的代碼。是否“猜中”可由一函數判斷

if( be_same(dig , g_dig , NUM ) == TRUE )

   其中be_same()函數為

int be_same ( int [] , int [] , int );
int be_same ( int ori[] , int spe[] , int n ) 
{
    int A = 0 , B = 0 ;
    int i ;
    
    for( i = 0 ; i < n ; i ++ )
         if( spe[i] == ori[i] )
             A++ ;
         else
             if( be_inside ( spe[i] , ori , n ) == TRUE )
                 B++;

    if( A == n )
       return TRUE ;
       
    printf("%dA%dB\n",A,B);         //輸出幾A幾B
    return FALSE ;   
}

   其中的be_inside()函數用於判斷第一個參數dig是否在后面的d數組中

int be_inside ( int  , int [] , int );
int be_inside ( int dig , int d[] , int n )
{
    while( n-- > 0 )
       if( dig == d[n] )
           return TRUE;

    return FALSE;
}

  至此,問題解決。下面給出這一問題的完整代碼。這個代碼在前面的基礎上對guess ()的健壯性做了改進,另外添加了在10次沒猜中時輸出被猜的數的代碼。

#include <stdio.h>
#include <stdlib.h> 
#include <time.h>

#define TIMES  10    //最多10次 
#define NUM    4     //正整數位數
#define MIN_5 	10000
#define MIN_4 	1000
#define TRUE 	1
#define FALSE  0
#define TEN    10

void generate( int [] , int );
void resolve( int ,  int [] , int );
int be_reduplicate( int [] , int );
void guess ( int [] , int ) ;
int be_same ( int [] , int [] , int );
int be_inside ( int  , int [] , int );
void out( int [] , int );

int main( void )
{
    int  dig[NUM]; //存放四位整數 
    int  count;
    //1.	生成無重復數字的四位整數;
    srand( (unsigned)time( NULL ) ); //初始化seed 
    generate( dig , NUM );           //生成無重復數字的四位整數,分解存入dig數組 
    
    for( count = 0 ; count < TIMES ; count ++ )  //2.	重復猜數字最多10次,
    {
         
         int g_dig[NUM];         //預備數據存儲空間
         
         guess ( g_dig , NUM ) ; //猜數字
         if( be_same(dig , g_dig , NUM ) == TRUE )	//if( 猜中 ) 
         {
               puts("恭喜你猜對了");     //宣布勝利
               system("PAUSE");return 0; //程序結束;

         }
    }
    //3.	10次沒猜中
    printf("那個數是:");                                                      
    out( dig , NUM );                                     //輸出被猜的數  
    printf("連續%d次你都沒猜中,抱歉,游戲結束\n",TIMES); //宣布失敗
    return 0;                                             //程序結束。
}

void out( int d[] , int n )
{
     int i ;
     for( i = 0 ; i < n ; i ++ )
          printf("%d " , d[i]);
     putchar('\n');
}

int be_inside ( int dig , int d[] , int n )
{
    while( n-- > 0 )
       if( dig == d[n] )
           return TRUE;

    return FALSE;
}

int be_same ( int ori[] , int spe[] , int n ) 
{
    int A = 0 , B = 0 ;
    int i ;
    
    for( i = 0 ; i < n ; i ++ )
         if( spe[i] == ori[i] )
             A++ ;
         else
             if( be_inside ( spe[i] , ori , n ) == TRUE )
                 B++;

    if( A == n )
       return TRUE ;
       
    printf("%dA%dB\n",A,B);     //輸出幾A幾B
    return FALSE ;   
}

void guess ( int d[] , int n )
{
     int i ;
     printf("輸入你猜的數:");
     for( i = 0 ; i < n ; i ++ )
     {
          scanf("%*[^0123456789]"); //讀非十進制數字字符 
          scanf("%1d" , d + i );
     }
          
}

int be_reduplicate( int d[] , int n )
{
    int i;
    for( i = 0 ; i < n - 1 ; i ++ )
    {
        int j ;
        for( j = i + 1 ; j < n ; j ++ )
             if( d[i] == d[j] )
                 return TRUE ;
    }
    
    return FALSE ;
}

void resolve( int t ,  int d[] , int n )
{

     while( n-- > 0 )
     {
         d[n] = t % TEN ;
         t /= TEN ;   
     }
}

void generate( int d[] , int n )   
{
     
     do
     {
         int temp = rand() % ( MIN_5 - MIN_4 - 1 ) + MIN_4 ; //生成一個四位數      
          resolve( temp , d , n );                            //分解存儲; 
     }
     while( be_reduplicate( d , n )  ==  TRUE );  //四位數有重復數字
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM