最近沉迷于《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 }