問題描述
井字遊戲是三、四歲小孩就會玩的棋賽。在一個 3 列(row) x 3 行(column)的空格中,參與遊戲的兩人一個只能填入 O,另一個只能填入 X,兩人從頭到尾輪流填入自己的符號,誰先將自己的符號在垂直、水平或斜角方向連成 3 個,誰就勝利。在此假設遊戲一開始都是填入 O 的人先填。
請寫一個程式,來判斷給定任一未完成的棋局,其最後結束時(O 贏、X 贏或平手)共有幾種不同的棋盤面,而其中 O 贏的棋盤面、X 贏的棋盤面、和局的棋盤面各有幾種。
例如,下圖中,最左邊的棋局,其結束時的棋盤面會有右邊四種可能。第一種 O 贏,第二種平手,第三、四種都是 X 贏,因此輸出(4, 1, 2, 1)。
輸入檔格式(C:\ttt\input.txt)
測試檔案中共有一行,以九個連續符號表示未完成的棋局,前三個符號代表該棋局第一列的三個格子的狀態,第四到第六個符號代表該棋局第二列的三個格子的狀態,第七到第九個符號代表該棋局第三列的三個格子的狀態。這些符號除了「O」與「X」外,以「-」來代表尚未被填入的格子。輸入檔中不會有不合理的棋局出現。
輸出檔格式 (C:\ttt\output.txt)
依序輸出該棋局最後可能出現的不同棋盤面數、O 贏的棋盤面數,X 贏的棋盤面數,以及平手的棋盤面數,此四個數字以空白隔開。
輸入檔範例1
OX--X-XOO
輸出檔範例1
4 1 2 1
輸入檔範例2
O-OXO-X--
輸出檔範例2
11 9 1 1
輸入檔範例3
O-OXO-X-X
輸出檔範例3
4 2 1 1
解題思考
對於這一題,我選擇使用遞迴函式來模擬雙方下棋。
首先,我們注意到一個不太明顯的小地方。棋盤的空白處(即未下棋的地方)數量,除了可以來判斷棋盤是否已下滿之外,還可以用它代表現在下棋的人是 O 或是 X。由於是 O 先下,所以剩餘空白處的數量只是奇數就是 O 下,相反的,是偶數則為 X 下。
我們根據以上提到的,判斷由誰下棋。並藉由迴圈來模擬下在各種地方的情況。並且,在每下完一步都檢查是否分出勝負。
單是這樣還不行,因為即使下的順序不同,最後可能還會出現棋局重複的情形。因此,我們還需要儲存已出現過的棋局。你可以使用一個二維字元陣列。
不過,由於我不想耗費一個陣列存一個棋局(就是棋盤的每一格由一個陣列元素儲存),我將棋局轉換成一個九位數的整數。第一位代表第 1 格,第二位代表第 2 格,以此類推。這樣一個棋局就只需要一個整數變數就能代表了。當然,僅供參考,有人有更好的方法也可以提出。
參考解答(C)
#include <stdio.h> #include <stdlib.h> void play(void); int search(void); int num = 0, win[2] = {0, 0}, top = 1, st[500]; char t[9]; int main(void) { int i, sum; // 宣告檔案串流 FILE *in, *out = fopen("C:/ttt/output.txt", "w+"); if (!(in = fopen("C:/ttt/input.txt", "r"))) { printf("找不到 input.txt\n"); system("pause"); exit(1); } printf("讀入資料 . . .\n"); for (i = 0; i < 9; i++) { t[i] = fgetc(in); printf("%c", t[i]); if (t[i] == '-') { num += 1; } } fclose(in); printf("\n---------\n"); // 呼叫遞迴處理棋局 play(); sum = win[0] + win[1] + win[2]; // 將結果印在輸出檔 fprintf(out, "%d %d %d %d", sum, win[1], win[0], win[2]); fclose(out); // 將結果標準輸出 printf("局數: %d\nO 勝: %d\nX 勝: %d\n和局: %d\n", sum, win[1], win[0], win[2]); printf("---------\n輸出資料在 ouput.txt\n"); system("pause"); } void play(void) { int i; // 判別是否分出勝負 if ((t[4] != '-') && (((t[0] == t[4]) && (t[4] == t[8])) || ((t[6] == t[4]) && (t[4] == t[2])))) { if (search()) { return; } win[(num + 1) % 2] += 1; return; } for (i = 0; i < 3; i++) { if (((t[i] != '-') && (t[i] == t[i + 3]) && (t[i] == t[i + 6])) || ((t[3 * i] != '-') && (t[3 * i] == t[3 * i + 1]) && (t[3 * i] == t[3 * i + 2]))) { if (search()) { return; } win[(num + 1) % 2] += 1; return; } } // 判別是否和局 if (num == 0) { if(search()) { return; } win[2] += 1; return; } // 使用窮舉法實現每種可能 for (i = 0; i < 9; i++) { if (t[i] == '-') { t[i] = num % 2 ? 'O' : 'X'; num--; play(); num++; t[i] = '-'; } } } int search(void) { int i, temp = 0; // 將棋局轉換存入變數 for (i = 0; i < 9; i++) { if (t[i] == 'O') { temp = temp * 10 + 1; } else if (t[i] == 'X') { temp *= 10; } else { temp = temp * 10 + 2; } } for (i = 0; i < top; i++) { // 查詢棋局是否重複 if (temp == st[i]) { return 1; } } // 將目前棋局記錄下來 st[top] = temp; top++; return 0; }
0 回覆:
張貼留言