網頁

2008年6月27日 星期五

【教學】C語言,期末作業 - 踩地雷(2008.06.28 原始檔OK!)。

題目:
本作業將使用C語言進行踩地雷遊戲的數字計算。

大部分的人都玩過踩地雷的遊戲,遊戲的開始是在一個M*N的陣列中佈有許多地雷,你必須將佈有地雷的地方插一個旗子,在沒有地雷的地方,填入這個格字的週遭(即上下左右斜上斜下等八個格子)佈有地雷的數字。下圖為Windows作業系統所附的遊戲畫面。


在這個作業中,題目的產生藉執行 finalproj2_gen.exe 程式而來,執行時,你必須輸入你的學號(不含A)七個數字作為命令列的引數。下列的問題就是在命令列輸入:finalproj2_gen 9628000 所得到,因此每個人的題目是不相同的。這個程式產生的題目會放在名稱為 "A"+學號+".in" 的檔案中,例如這裡的問題會放在 A9628000.in 的檔案中。題目中的 '*' 表示是地雷,'.' 表示不是地雷。題目第一列的兩個整數分別表示地雷矩陣的大小,亦即列數與行數。
17 36
*..*.**..........*.....*.........*..
.......*.....*.*......*.....*...**..
...**...............*...*.....*.....
........*...........*......**.*.....
.......*.....**..........*.....*..*.
...**............*...*.*.*.....*....
*...*.....**.*.*..*........*...**...
*...............*....*.....*........
.................*..*...........*...
....*...............**...**...*.....
*..**..*..*...........*.*....*.*...*
..**......*..........*.*.**...**....
.*.....*.............*..*...*.....*.
...*..**...*.*.**.....*......**.*.*.
....*.*.....*............**.........
..*...........*.*..*.**..*....**.**.
.*.*..**..**.....*...*...........*..
作業的輸出是以C語言計算所有非地雷點的週遭地雷數,輸出的格式第一行為"Answer for field " + 學號。接著列印地雷矩陣的資訊,有地雷的地方仍然印著 '*' ,沒地雷的地方則印著其週遭的地雷數。上述例題的答案如下所示。你必須將你的答案直接用程式輸出,輸出檔案的命名為 "A"+學號+".out" 。例如此例答案的輸出檔名為A9628000.out 。


Answer for field A9628000
*11*2**2100011212*10012*100111013*20
1123433*10001*2*211112*32101*212**20
001**112210011211002*312*11334*32210
00122112*10012210002*201222**3*31111
0012211*21001**1111122213*32223*21*1
111**211112233422*211*2*3*31103*4211
*213*20001**2*2*33*12231213*202**100
*201110001222122*3222*10002*20133200
11011100000000012*12*42012321112*100
1113*211111100001112**222**112*32111
*23**21*12*20000000134*3*5421*5*201*
23**312222*2000000002*4*4**223**2122
1*43213*2122212221002*43*322*44423*2
112*23**201*3*2**10012*223222**2*3*2
0123*3*31012*334321123322**113443432
12*32333112322*2*22*3**12*3101**3**1
1*3*11**11**11122*213*31111001223*31
話說...有些人連這都不會開...
 
流程:
>讀入檔案,並使用字串陣列接收。
>>將字串陣列的符號轉成數字代替,並用一個二維陣列接收。(EX:有地雷-->arr[i][j]=9,無地雷:-->arr[i][j]=0)
>>>用數個if 判斷arr[i][j]四周的8種不同的狀況,若成立,使arr[i][j]+1
>>>>輸出檔案。
其實,做法只需要用fscanf(@@,##,$$)就可以一行一行的輸入.in的內容了。
@@=FILE指標的名稱 ##=%s(若欲輸入的內容是數字,就可以用%d) $$=字串名稱(char ch[80][80] 或 int input-->切記要"&input")
至於印出的話,可以用fprintf(@@,"",$$)。用法跟printf()一樣,只是最前面多了@@-->FILE指標名稱
(我覺得提示的有點太多了...) 
判斷關鍵:
利用二維陣列的索引值相關性做判斷。其原理很簡單。

假設正中間的Arr[i][j]是陣列檢查到的值。
那麼,你只需要利用i、j的索引值去檢查他四周8個陣列的值是不是有出現地雷,這樣就可以知道Arr[i][j]四周有幾顆地雷。
原始碼:

#include stdio.h
#include stdlib.h
int main(void)
    FILE *fin,*fout;//fin讀入題目的檔案指標 fout輸出答案的檔案指標 
    char ch[80][80];//讀入資料之字串陣列 
    int row,col,i=0,j,k1,k2,**arr,*arrData;//row col i j控制陣列用 arr答案陣列 k1 k2判斷範圍之用 
    bool judge=0;//判斷用 
/*////////////開始導入題目////////////*/   
    fin=fopen("\A9628393.in","r");
    if(fin!=NULL)
    {
        if(judge==0)
        {//第一次讀入 寫入列、欄 
            fscanf(fin,"%d %d",&col,&row);
            judge=1;
        }
        while(!feof(fin))
        {
        //第二次之後 寫入地雷原型 
            fscanf(fin,"%s",ch[i]);
            i++;      
        }
        printf("題目檔開檔完成!\n");
        fclose(fin);
    }
    else
        printf("題目檔開檔失敗!\n");
/*/////////malloc製作動態陣列/////////*/   
    arr=(int **)malloc(col*sizeof(int *));
    arrData=(int *)malloc(col*row*sizeof(int));
    for(i=0;i <col;i++,arrData+=row)< />
        arr[i]=arrData;
/*////////////將動態陣列歸零////////////*/   
    for(i=0;i <col;i++)< />
        for(j=0;j <row;j++)< />
            arr[i][j]=0;
/*將字串陣列的地雷原型寫入至arr二維陣列:地雷=9 無地雷=0*/
    for(i=0;i <col;i++)< />
    {
        for(j=0;j <row;j++)< />
        {
            if(ch[i][j]==42)
                arr[i][j]=9;
            else if(ch[i][j]==42)
                arr[i][j]=0;
        }
    }
/*//////判斷arr[i][j]圍繞地雷之九宮格的值都加1//////*/ 
    for(i=0;i <col;i++)< />
    {
        for(j=0;j <row;j++)< />
        {
            if(arr[i][j]==9)
            {
                for(k1=-1;k1<=1;k1++)
                {
                    for(k2=-1;k2<=1;k2++)
                    {
                        if(i+k1>=0 && j+k2>=0 && i+k1
0 && k2!=0)
                        {
                            arr[i+k1][j+k2]++;
                        }//if 
                    }
                }//判斷範圍 
            }//if 
        }
    }//主迴圈 
/*//////////////開始導出答案//////////////*/  
    fout=fopen("\A9628393.out","w");
    if(fout!=NULL)
    {//將答案arr陣列寫入至A9628393.out 
        fprintf(fout,"Answer for field A9628393\n");
        for(i=0;i <col;i++)< />
        {
            for(j=0;j <row;j++)< />
            {
                if(arr[i][j]==9)
                    fprintf(fout,"*");
                else
                    fprintf(fout,"%d",arr[i][j]);
            }
            fprintf(fout,"\n");
        }
        printf("解答寫入完成!\n");
        fclose(fout);
    }
    else
        printf("解答寫入失敗!\a\n");
    system("pause");
    return 0;
}

5 則留言:

  1. 我如果要做的話 如果 array[i][j] 剛好為 * 做出適當判斷 再依判斷把周圍符合條件的格子+1

    回覆刪除
  2. 喵的~老子每次看到你左邊那張穿著燭苳高中制服的照片,就會笑個不停...

    回覆刪除
  3. 關於讀檔與動態陣列的部分

    我的建議是int ch[80][80]是不必要的
    可以在if(judge==0)的時候就把col跟row讀入
    用malloc建立動態陣列了
    然後在下面就可以直接用動態列讀入資料了
    就不用在下面再附加一個迴圈把資料從ch轉移過去arrData

    這樣會比較省記憶體且能節省運算
    重要的是既然用到動態陣列了
    而ch是固定在80*80只要col或row超過了
    那麼資料轉移的時候就會發生問題

    回覆刪除
  4. 呃 另外
    "*" 是字串
    '*' 是字元
    兩者看起來相同其實是不同
    "*" = {'*','0'}
    '*' = {'*'}
    小心使用有時候會有錯誤發生

    回覆刪除