「人向猿進階」之軟件工程第四課——結對編程

黃金點遊戲

      黃金點遊戲是一個數字小遊戲,其遊戲規則是:git

      N個同窗(N一般大於10),每人寫一個0~100之間的有理數 (不包括0或100),交給裁判,裁判算出全部數字的平均值,而後乘以0.618(所謂黃金分割常數),獲得G值。提交的數字最靠近G(取絕對值)的同窗獲得N分,離G最遠的同窗獲得-2分,其餘同窗得0分。玩了幾天之後,你們發現了一些頗有意思的現象,好比黃金點在逐漸地往下移動。編程

      如今請你們根據這個遊戲規則,編一個能夠多人一塊兒玩的小遊戲程序,要求以下:數組

      一、本做業屬於結對編程項目,必須由二人共同完成,並分別將本次做業過程發到博客,同時將本次做業源代碼提交到codeing系統;服務器

      二、若是可能的話儘可能以C/S或B/S方式實現,即利用服務器接收和處理全部玩家提交的數字,並將結果反饋給各玩家,玩家能夠經過客戶端提交的數字;函數

      三、若是採用單機方式實現的話,須要爲用戶提供便利的輸入界面;post

      四、該遊戲每次至少能夠運行10輪以上,並可以保留各輪比賽結果。測試

以上就是咱們這次的結對編程的題目與規則了。ui

整體結構如圖所示:spa

 

 

這是咱們代碼上傳的coding主頁地址:https://coding.net/u/xiaoyongwu.net

上述完了題目與設計,下面就該咱們的睿哥登場了,也是本次我結對編程的隊友——楊睿。

這是楊睿同窗的博客地址:http://www.cnblogs.com/ruiguo/

如下是咱們準備開始編程的照片、編程過程當中的照片、以及最後進行調試和測試的照片:

  楊睿同窗,是一個來自湖南的帥哥,可是可能因爲在北方讀書的緣故,因此在咱們班性格比較內向,不愛說話,可是對人很好,只要你找他幫忙,他確定會不遺餘力的幫你。  在這次的結對編程中,楊睿由於之前C語言學的通常,因此對於這次的編程仍是有很大的不自信的。後來咱們倆通過溝通,他對於這次的編程慢慢開始充滿了期待,但願能從中學到一些東西,而且在以後的結對編程中也體現出來了楊睿的信心、興趣以及心裏的想學好編程的想法。最終咱們倆肯定了計算黃金點,計算各輪次的得分以及玩家信息的創建給楊睿來寫,其餘的功能由我寫。

  在編程的過程中咱們遇到了不少問題,因爲之前咱們用結構數組用的比較少,而此次咱們倆是用的結構數組的存儲玩家信息,先動態的分配了M個玩家的地址,而後再往其中填上數據,因此在內存的分配與釋放上咱們倆遇到了小小的困難。事實證實兩我的編程比一個好,不到一下子,咱們倆就弄清事情的原委。還有一次咱們倆在結構數組指針的傳遞上出現了分歧,由於用的結構數組,須要傳遞給子函數數組指針,在用一個*仍是兩個*問題上咱們倆出現了初學者的困惑,可是後來一看C語言書就明白過來了。固然還有其餘的一些小問題,這兒就不一一列舉了。

  在代碼規範上面,我以爲咱們倆人仍是比較默契的,由於是我先寫的代碼,輪到楊睿的時候,他基本上是照着我以前的代碼風格寫下來的,儘管中間出現了一些小的問題,可是都不大,就像把兩個整數類型數據賦初值,楊睿喜歡連續賦值,例如:int a=0,b=1;而我比較喜歡int a=0;int b=1;因此基本上仍是問題不大。仍是之前大一的時候老師要求的好,上機時老師都給咱們要求了代碼的編寫規範(固然只是相對來講讓人更容易讀懂),而咱們也一直學在手中的。

  總的來講,這次的編程我以爲是成功的,儘管咱們倆個沒有實現C/S或者B/S方式,或者提供便利的美觀界面設計。可是我以爲這不是最重要的,重要的是咱們倆都能今後次編程中學到什麼,也許咱們都會有抱大腿的心態,跟着大神「作」,而後就真的是座了。也許咱們倆如今是剛剛起步,可是隻要基礎打得深,相信之後建高樓,蓋大廈不會是難事,張狂點說甚至是垂手可得。兩我的一塊兒編程的好處在於多了一雙眼睛幫助你檢查錯誤,就像日常寫字同樣,一疏忽(緊張)熟悉的字也寫錯,有人看着能夠時時提醒,特別是當你跟着一個比你強的人編程的時候,你將學到更多東西(儘管目前我還沒體驗過,可是一直在尋找這種成長的機會)。還有一個好處就是,若是有一天你生病了,可是又須要提交程序,若是一個能看你程序的人都沒有,那麼只能另尋它法了(固然這只是個比方,並不表明實際)。總之結對編程的好處不少,須要咱們慢慢去探尋,只有本身親自去找尋的東西纔會是本身體會最深的、記憶最深入的。

 

下面講講咱們的程序吧:

首先咱們用了一個people結構體,保存了玩家的序號、提交的數字、成績等等信息,而且起了一個PLAYER的別名。緊跟着的是咱們的子函數,包括:傳值函數、錄取信息函數、排序函數、釋放內存函數、計算函數、顯示函數等等。

struct People
{
    int n_num;//遊戲人員序號
    float digit;//所選數字
    float gap;//所選數字與黃金數字的差距
    int S_score;//本輪成績
    int score;//總成績
};
typedef struct People PLAYER;

PLAYER *give(PLAYER **ppla,int num);//把ppla賦給ppla2
PLAYER *Getinf(PLAYER **ppla,int i,int m);//獲取玩家信息
void sort_S_score(PLAYER **ppla2,int num);//每輪遊戲以後進行選擇排序
void sort_score(PLAYER **ppla,int num);//遊戲結束以後按照總分進行選擇排序
void freememory(PLAYER **ppla,int num);//釋放結構體數組的內存
float count_gold(PLAYER **ppla,int num);//計算黃金點
void count_score(PLAYER **ppla,int num,float g_num);//計算分數
void show(PLAYER **ppla2,int num);//顯示每輪遊戲結果

接下來是該程序最重要的主函數部分:

主函數是這樣設計的:首先定義了count變量,用作判斷是否循環玩遊戲,輸入玩家人數Num以及遊戲的輪次數id,而後動態分配結構體數組的空間給ppla、ppla2以及建立一個保存遊戲每輪得分信息的文件gold.txt。再依次創建每一個玩家的信息,而後根據所輸入的信息計算出黃金點G值,而後在根據G值計算出每一個玩家提交的數字與G值的距離,而且給與相應的分數。再把得分信息存入文件當中,而後根據得分把每一輪進行排序。等到最後一輪結束,再根據總成績進行排序,而且從文件中直接讀取信息輸出每位玩家在每一輪當中的得分狀況等。最後詢問是繼續遊戲仍是退出遊戲。

void main(int argc, char *argv[])
{
    int count=1;
    while(count)
    {
        int id=0,num=0;
        view(id,num);

        //動態創建結構體數組
        PLAYER **ppla;
        PLAYER **ppla2;
        ppla=(PLAYER **)malloc (num * sizeof(PLAYER *));
        if(ppla==NULL)
        {
            printf("not set the ppla\n");
            return ;
        }
        ppla2=(PLAYER **)malloc (num * sizeof(PLAYER *));
        if(ppla2==NULL)
        {
            printf("not set the ppla2\n");
            return ;
        }
        
        //打開文件
        FILE *fp;
        fp=fopen("gold.txt","w");
        if(fp==NULL)
        {
            printf("cano't open gold.txt\n");
            exit(0);
        }

        //開始每一輪的玩家信息輸入、最終結果的輸出
        int m=1;
        while(id)
        {
            printf("-----第%d輪-----\n",m);
            //創建每一個玩家的信息
            int i=0;
            for(i=0;i<num;++i)
            {
                ppla[i]=Getinf(ppla,i,m);
                if(ppla[i]==NULL)
                {
                    printf("error\n");
                    freememory(ppla,i);
                    return;
                }
            }
            
            //計算黃金點
            float g_num=0;
            g_num=count_gold(ppla,num);
            
            //計算得分
            count_score(ppla,num,g_num);
            
            //把本輪信息存入文件中
            int g=0;
            for(g=0;g<num;++g)
            {
                fprintf(fp,"\t%d\t%f\t%f\t%d\t\t%d\n",ppla[g]->n_num,ppla[g]->digit,ppla[g]->gap,ppla[g]->S_score,ppla[g]->score);
            }
            
            //把ppla的值給ppla2並對本輪排序
            int s=0;
            for(s=0;s<num;++s)
            {
                ppla2[s]=give(ppla,s);
            }
            sort_S_score(ppla2,num);
            
            //輸出每一輪的排名得分狀況
            printf("\t\t黃金點數字爲:%f\n\n",g_num);
            show(ppla2,num);
            
            int cls=0;
            printf("是(1)否(0)清屏?\n");
            scanf("%d",&cls);
            if(cls==1)
                system("CLS");
            m++;
            id--;
        }
        fclose(fp);
        
        //按照總分排序
        int yy=0;
        printf("=====遊戲已結束,是(1)否(0)按照總分紅績進行排序?\n");
        scanf("%d",&yy);
        if(yy==1)
        {
            sort_score(ppla,num);
            show(ppla,num);
        }

        //查看每一輪成績
        int xx=0,xy=0,g=0;
        m=m-1;
        xy=num;
        printf("\n=====遊戲已結束,是(1)否(0)查看各輪比賽成績?\n");
        scanf("%d",&xx);
        if(xx==1)
        {
            char ch[100];
            fp=fopen("gold.txt","r");
            num=m*num;
            int j=1;
            for(g=0;g<num;++g)
            {
                fgets(ch,50,fp);
                if((g%xy)==0)
                {
                    printf("\t\t\t第%d輪狀況\n",j);
                    j=j+1;
                    printf("\t序號\t提供數字\t黃金間距\t本輪成績\t總成績\n");
                }
                printf("%s",ch);
            }
        }
        freememory(ppla,xy);
        freememory(ppla2,xy);

        printf("====繼續遊戲請輸入(1),退出遊戲請輸入(0)======\n>>>");
        scanf("%d",&count);
        if(count==1)
            system("CLS");
    }
}

接下來介紹一下顯示函數:顯示函數包括兩個,一個是輸出每輪遊戲的結果,還有一個是輸出遊戲規則等,直接用輸出語句就好了。

//顯示每輪遊戲結果
void show(PLAYER **ppla2,int num)
{
    int i=0;
    printf("\t\t\t排名狀況\n");
    printf("\t序號\t提供數字\t黃金間距\t本輪成績\t總成績\n");
    for(i=0;i<num;++i)
    {
        printf("\t%d\t%f\t%f\t%d\t\t%d\n",ppla2[i]->n_num,ppla2[i]->digit,ppla2[i]->gap,ppla2[i]->S_score,ppla2[i]->score);
    }
}

void view(int &id,int &num)
{
    printf("=====================================================================\n");
    printf("                Welcome To The Gold Game               \n");
    printf("----------------------------------------------------------------------\n");
    printf("遊戲規則:N個同窗(N一般大於10),每人寫一個0~100之間的有理數\n");
    printf("\t(不包括0或100),交給裁判,裁判算出全部數字的平均值,而後乘以\n");
    printf("\t0.618(所謂黃金分割常數)獲得G值。提交的數字最靠近G(取絕對值)\n");
    printf("\t的同窗獲得N分,離G最遠的同窗獲得-2分,其餘同窗得0分。\n");
    printf("----------------------------------------------------------------------\n");
    printf("\t==========請輸入玩家人數==========\n\t>>");
    scanf("%d",&num);
    printf("\t==========請輸入遊戲輪數==========\n\t>>");
    scanf("%d",&id);
}

而後是獲取玩家信息函數,給子函數傳入結構體數組指針,玩家人數,遊戲輪次等,第一輪的時候須要給結構體數組中添加每一個玩家的結構體,須要動態分配新結構體,而後把結構體按照序號放入ppla當中,而且給玩家的提交數字、總分、每輪得分、間距等賦初值。

//獲取玩家信息
PLAYER *Getinf(PLAYER **ppla,int i,int m)
{
    if(m==1)
    {
        PLAYER *p;
        p=(PLAYER *) malloc (sizeof(PLAYER));
        if(p==NULL)
        {
            return NULL;
        }
        p->n_num =i+1;
        printf("\t\t請輸入第%d個玩家選擇的有理數字(0-100)\n\t\t>>>>",i+1);
        scanf("%f",&p->digit );
        printf("\n");
        p->gap =0;
        p->S_score =0;
        p->score =0;
        
        return (p);
    }
    else
    {
        printf("\t\t請輸入第%d個玩家選擇的有理數字(0-100)\n\t\t>>>>",i+1);
        scanf("%f",&ppla[i]->digit );
        printf("\n");
        ppla[i]->gap =0;
        ppla[i]->S_score =0;
        
        return (ppla[i]);
    }
}

下面介紹的是計算函數,計算函數也是兩個,一個是計算黃金點,一個是計算得分狀況。首先跟據玩家提供的數字進行求平均值,而後*0.618再返回黃金點值。再根據黃金點值計算出每位玩家提供的數字與gold_num的距離,找到最小的min和最大的max(其中包含了多我的提交相同數字的狀況),而後在給相應的玩家分數。

//計算黃金點
float count_gold(PLAYER **ppla,int num)
{
    float sum=0,gold=0;
    int j=0;
    
    for(j=0;j<num;++j)
    {
        sum += ppla[j]->digit;
    }
    sum=sum/num;
    gold = float(sum * g_gold);
    
    return gold;
}

//計算分數
void count_score(PLAYER **ppla,int num,float g_num)
{
    //把間距計算出來存入結構體中
    int k=0;
    float min=100,max=0;
    for(k=0;k<num;++k)
    {
        ppla[k]->gap = float(fabs(ppla[k]->digit - g_num));
        //找到最近的數字
        if(ppla[k]->gap < min)
        {
            min=ppla[k]->gap ;
        }
        //找到最遠的數字
        if(ppla[k]->gap >max)
        {
            max=ppla[k]->gap ;
        }
    }
    for(k=0;k<num;++k)
    {
        //避免有不少人選中同一個數字的狀況
        //本輪成績爲+二、0、-2,總成績加(減)2
        if(ppla[k]->gap ==min)
        {
            ppla[k]->S_score =num;
            ppla[k]->score =ppla[k]->score +ppla[k]->S_score;
        }
        else if(ppla[k]->gap ==max)
        {
            ppla[k]->S_score =-2;
            ppla[k]->score =ppla[k]->score +ppla[k]->S_score;
        }
        else
        {
            ppla[k]->score =ppla[k]->score +ppla[k]->S_score ;
        }
    }
}

下面是排序函數,排序咱們選用的是簡單的選擇排序,直接對每一輪的得分、總分進行排序。

//每輪遊戲以後按照本輪得分狀況進行選擇排序
void sort_S_score(PLAYER **ppla2,int num)
{
    PLAYER *p;
    int i,j,k;
    for(i=0;i<num-1;++i)
    {
        k=i;
        for(j=i+1;j<num;++j)
        {
            if(ppla2[j]->S_score > ppla2[k]->S_score )
            {
                k=j;
            }
        }
        if(k!=i)
        {
            p=ppla2[i];
            ppla2[i]=ppla2[k];
            ppla2[k]=p;
        }
    }
}

//遊戲結束以後按照總分進行選擇排序
void sort_score(PLAYER **ppla,int num)
{
    PLAYER *p;
    int i,j,k;
    for(i=0;i<num-1;++i)
    {
        k=i;
        for(j=i+1;j<num;++j)
        {
            if(ppla[j]->score > ppla[k]->score )
            {
                k=j;
            }
        }
        if(k!=i)
        {
            p=ppla[i];
            ppla[i]=ppla[k];
            ppla[k]=p;
        }
    }
}

最後就是內存的釋放了:當玩家退出遊戲後,直接把動態數組銷燬,釋放內存。

//釋放內存
void freememory(PLAYER **ppla,int num)
{
    int i;
    for(i=0;i<num;++i)
    {
        free(ppla[i]);
    }
    free(ppla);
}

下面是運行截圖:

測試一:11個玩家,玩4輪遊戲,前兩輪遊戲每一個玩家輸入數據相同,得出結果是:一個玩家滿分,一個玩家負滿分,其餘人0分。後兩輪隨意輸入,得出並非提交的數越小越容易得分,取決於你們整體的提交數字,相對來講在提交的數當中處於中等偏下的數字越有優點。

 

測試二:5個玩家,玩4輪,第一輪全部玩家輸入相同的數字,得出全部玩家都得分的結論;第二,三,四輪隨意輸入,計算得分。最後一張爲gold.txt中保存的每輪遊戲信息。

測試黃金點的規律:

使用的隨機函數生成0-100之間的有理數,而後用全局變量定義了一個數組保存黃金點數字,輸出每次遊戲以後的黃金點數字。

發現機器生成的數字,讓黃金點數字在30左右徘徊:

這是5人10輪的截屏:

這是10人20輪的截屏:

這是20人30輪的截屏:

相關文章
相關標籤/搜索