最近沉迷於《NetHack》、《DCSS》等字符遊戲,對其很感興趣,因而用C語言寫了個字符界面的井字棋小遊戲。歡迎你們指教。dom
編寫時遇到了一些問題,我原先準備用循環,直到讀取到正確的輸入。可該死的getchar函數,在讀取後,又把回車又傳給下次循環,我不得不對其進行處理。函數
設定井字棋的AI時,有個有趣的地方就是,先下四個角比先下中心優點更大,這違背了我之前的直覺。spa
1 #include <stdio.h> 2 #include <ctype.h> 3 #include <string.h> 4 #include <stdlib.h> 5 #include <time.h> 6 7 void drawBoard(char *board) //繪製棋盤 8 { 9 printf("%c|%c|%c\n", board[7], board[8], board[9]); 10 puts("-+-+-"); 11 printf("%c|%c|%c\n", board[4], board[5], board[6]); 12 puts("-+-+-"); 13 printf("%c|%c|%c\n", board[1], board[2], board[3]); 14 puts("-+-+-"); 15 } 16 17 char inputPlayerLetter() //玩家選擇棋子 18 { 19 char letter; 20 puts("你想用X仍是O?"); 21 do{ 22 letter = toupper(getchar()); 23 if (letter == '\n') 24 continue; 25 if (letter != 'X' && letter != 'O') 26 puts("你想用X仍是O?"); 27 }while (letter != 'X' && letter != 'O'); 28 29 return letter; 30 31 } 32 33 void makeMove(char *board,char letter,int move) //落子 34 { 35 board[move] = letter; 36 } 37 38 _Bool isWinner(char *bo, char le) //斷定是否獲勝 39 { 40 return ((bo[7] == le && bo[8] == le && bo[9] == le) || 41 (bo[4] == le && bo[5] == le && bo[6] == le) || 42 (bo[1] == le && bo[2] == le && bo[3] == le) || 43 (bo[7] == le && bo[4] == le && bo[1] == le) || 44 (bo[8] == le && bo[5] == le && bo[2] == le) || 45 (bo[9] == le && bo[6] == le && bo[3] == le) || 46 (bo[7] == le && bo[5] == le && bo[3] == le) || 47 (bo[9] == le && bo[5] == le && bo[1] == le)); 48 } 49 50 const char getBoardCopy(char *board) //複製棋盤,讓電腦預判可能出現的狀況 51 { 52 char boardCopy[10]; 53 for (int i = 1; i < 10; i++) 54 boardCopy[i] = board[i]; 55 return *boardCopy; 56 } 57 58 _Bool isSpaceFree(char *board,int move) //判斷棋盤上是否爲空 59 { 60 return board[move] == ' '; 61 } 62 63 int getPlayerMove(char *board) //讀取玩家棋子移動 64 { 65 puts("你下一步走哪裏?(1-9)"); 66 int move; 67 do { 68 move = getchar() - '0'; 69 if (move == '\n' - '0') 70 continue; 71 if (move < 1 || move > 9 || !isSpaceFree(board, move)) 72 puts("你下一步走哪裏?(1-9)"); 73 }while (move < 1 || move > 9 || !isSpaceFree(board, move)); 74 return move; 75 } 76 77 int chooseRandomMoveFromList(char *board,char *movelist, int n) //隨機讀取計算機可移動的位置 78 { 79 int possibleMove[4]; //每輪選擇最多隻有四個 80 int j = 0; 81 for (int i = 0; i < n; i++) 82 if (isSpaceFree(board, movelist[i] - '0')) 83 possibleMove[j++] = movelist[i] - '0'; 84 85 if (j != 0) 86 return possibleMove[rand()%j]; 87 else 88 return 0; 89 } 90 91 int getComputerMove(char board[], char computerLetter) //得到計算機的移動 92 { 93 char playerLetter; 94 char boardCopy[10]; 95 if (computerLetter == 'X') //根據計算機的棋子,判斷玩家棋子 96 playerLetter = 'O'; 97 else 98 playerLetter = 'X'; 99 100 for (int i = 1; i < 10; i++){ //若是下一步可獲勝,下那一步 101 strcpy(boardCopy, board); 102 if (isSpaceFree(boardCopy, i)) { 103 makeMove(boardCopy, computerLetter,i); 104 if (isWinner(boardCopy, computerLetter)) 105 return i; 106 } 107 } 108 109 for (int i = 1; i < 10; i++){ //若是下一步玩家會獲勝,佔那個位置 110 strcpy(boardCopy,board); 111 if (isSpaceFree(boardCopy, i)) { 112 makeMove(boardCopy, playerLetter,i); 113 if (isWinner(boardCopy, playerLetter)) 114 return i; 115 } 116 } 117 118 int move; //若是下一步不是決勝步 119 move = chooseRandomMoveFromList(board, "1379", 4); //四個角優先 120 if (move != 0) 121 return move; 122 123 move = chooseRandomMoveFromList(board, "5", 1); //中間 124 if (move != 0) 125 return move; 126 127 return chooseRandomMoveFromList(board, "2468", 4); //剩下的位置 128 } 129 130 _Bool isBoardFull(char *board) //判斷棋盤是否滿了 131 { 132 for (int i = 1; i < 10; i++) 133 if (isSpaceFree(board, i)) 134 return 0; 135 return 1; 136 } 137 138 _Bool isAgain() //再來一局 139 { 140 char again; 141 do{ 142 again = tolower(getchar()); 143 if (again == '\n') 144 continue; 145 if (again != 'n' && again != 'y') 146 puts("請輸入y或n。"); 147 }while (again != 'n' && again != 'y'); 148 149 if (again == 'y') 150 return 1; 151 else 152 return 0; 153 } 154 155 156 int main() 157 { 158 puts("歡迎來玩井字棋!") ; 159 160 while(1) { //遊戲 161 char theBoard[10]; 162 for (int i = 1; i < 10; i++) //將棋盤設爲空白 163 theBoard[i] = ' '; 164 char playerLetter = inputPlayerLetter(); //得到玩家所選的棋子 165 char computerLetter = (playerLetter == 'X')?'O': 'X' ; 166 //得到計算機的棋子 167 _Bool isTurnPlayer; //設定是不是玩家回合 168 int move; 169 srand((unsigned)time(NULL)); //隨機前後手 170 if (rand() % 2) { 171 isTurnPlayer = 0; 172 puts("電腦先走。"); 173 }else { 174 isTurnPlayer = 1; 175 puts("玩家先走。"); 176 } 177 _Bool gameIsPlaying = 1; //設定遊戲是否進行 178 179 while (gameIsPlaying) { 180 181 if (isTurnPlayer) { //若是是玩家回合 182 drawBoard(theBoard); 183 move = getPlayerMove(theBoard); 184 makeMove(theBoard, playerLetter, move); 185 186 if (isWinner(theBoard, playerLetter)) { //若是獲勝 187 drawBoard(theBoard); 188 puts("太棒了!你獲勝了!"); 189 gameIsPlaying = 0; 190 } 191 192 else { //若是平局 193 if (isBoardFull(theBoard)) { 194 drawBoard(theBoard); 195 puts("平局了!"); 196 break; 197 } 198 else //設定輪到計算機 199 isTurnPlayer = 0; 200 } 201 } 202 203 else { //輪到計算機了 204 move = getComputerMove(theBoard, computerLetter); 205 makeMove(theBoard, computerLetter, move); 206 if (isWinner(theBoard, computerLetter)) { //若是獲勝 207 drawBoard(theBoard); 208 puts("電腦戰勝了你!你輸了。"); 209 gameIsPlaying = 0; 210 } else { //平局 211 if (isBoardFull(theBoard)) { 212 drawBoard(theBoard); 213 puts("平局了!"); 214 break; 215 } else // 設定輪到玩家 216 isTurnPlayer = 1; 217 } 218 } 219 } 220 puts("再來一局?(yes或no)"); 221 if (!isAgain()) 222 break; 223 } 224 return 0; 225 }