游戲介紹
井字棋,英文名叫Tic-Tac-Toe,是一種在3*3格子上進行的連珠游戲,和五子棋類似。然后由分別代表O和X的兩個游戲者輪流在格子里留下標記(一般來說先手者為X),任意三個標記形成一條直線(包括行、列、對角線、反對角線),則為獲勝。
解決策略
重點在於電腦方如何下棋,我們采取估計棋局每個位置的權重,首先要對棋局進行分類。
---3個為空,重要性最低,權值設置為1 //視為暫時不管
---2個空1個對方,重要性次低,權值為10 //一下三個區別不大,可比較隨意的設置
----1個空格1個對方1個己方,重要行較低,權值50
----2個空格1個己方,重要性較高,權值為100
---1個空格2個對方,重要性次高,權值500 //不堵住會輸
---1個空格2個己方,重要性最高,權值1000 //可以直接贏
注意幾點:
1、權值之間的間距可以設大一點
2、對每個空位置,權值等於行權值+列權值+對角線權值+反對角線權值,這4中權值都可以用上面的估算,但不做改進會出bug
考慮如下情況:
(1,3)-->(3,1)-->(1,1)-->(2,1)電腦就輸了
---->
--->
-->
-->人獲勝
關鍵在於第二步,應該選擇一個非角的位置,原因在於此時右上角位置的權值大於中上位置,分析權值的來源
右上角時,10+10+100(對角線己方),而中上時,10+100(行己方),所以同樣是2空1己方時,己方位於行或列的權重應大於己方位於對角線。
所以按行或列計算時,2空1己方的權值可改為200
代碼實現
1 #include<stdio.h> 2 #include<Windows.h> 3 4 const int ROW = 3; 5 const int COL = 3; 6 int chessboard[ROW][COL]; 7 int score[ROW][COL]; 8 9 void Initmap(); 10 void Showmap(); //打印棋局 11 bool isWin(); //判斷是否有一方獲勝 12 bool isFull(); //判斷棋盤是否為滿 13 void PcPlay(); //電腦下棋 14 void HumanPlay(); //人下棋 15 16 int main() 17 { 18 Initmap(); 19 Showmap(); 20 while ((!isFull()) && (!isWin())) 21 { 22 HumanPlay(); 23 system("cls"); 24 Showmap(); 25 if (isWin()) 26 break; 27 28 Sleep(500); //模擬實際過程,讓電腦慢點,hh 29 PcPlay(); 30 system("cls"); 31 Showmap(); 32 } 33 34 if (isFull()) 35 printf("\n\n平局\n"); 36 37 system("pause"); 38 return 0; 39 } 40 41 void Initmap() 42 { 43 for (int i = 0; i < ROW; i++) 44 for (int j = 0; j < COL; j++) 45 chessboard[i][j] = 1; 46 } 47 48 void Showmap() 49 { 50 for (int i = 0; i < ROW; i++) 51 { 52 for (int j = 0; j < COL; j++) 53 { 54 if (chessboard[i][j] == 1) //"1"代表空 55 printf("□"); 56 if (chessboard[i][j] == 2) //"2"代表人 57 printf("■"); 58 if (chessboard[i][j] == 5) //"5"代表電腦 59 printf("●"); 60 } 61 printf("\n"); 62 } 63 } 64 65 bool isWin() 66 { 67 int sum = 0; 68 for (int i = 0; i < ROW; i++) //對每行判斷是否獲勝 69 { 70 for (int j = 0; j < COL; j++) 71 sum += chessboard[i][j]; 72 73 if (sum == 6) 74 { 75 printf("人獲勝!\n"); 76 return true; 77 } 78 if (sum == 15) 79 { 80 printf("電腦獲勝!\n"); 81 return true; 82 } 83 sum = 0; 84 } 85 86 for (int j = 0; j < ROW; j++) //對每列判斷是否獲勝 87 { 88 for (int i = 0; i < COL; i++) 89 sum += chessboard[i][j]; 90 91 if (sum == 6) 92 { 93 printf("人獲勝!\n"); 94 return true; 95 } 96 if (sum == 15) 97 { 98 printf("電腦獲勝!\n"); 99 return true; 100 } 101 sum = 0; 102 } 103 104 for (int i = 0; i < ROW; i++) //對對角線判斷是否獲勝 105 sum += chessboard[i][i]; 106 if (sum == 6) 107 { 108 printf("人獲勝!\n"); 109 return true; 110 } 111 if (sum == 15) 112 { 113 printf("電腦獲勝!\n"); 114 return true; 115 } 116 117 sum = 0; 118 for (int i = 0; i < ROW; i++) //對反對角線判斷是否獲勝 119 sum += chessboard[i][2 - i]; 120 if (sum == 6) 121 { 122 printf("人獲勝!\n"); 123 return true; 124 } 125 if (sum == 15) 126 { 127 printf("電腦獲勝!\n"); 128 return true; 129 } 130 131 return false; 132 } 133 134 bool isFull() 135 { 136 for (int i = 0; i < ROW; i++) 137 for (int j = 0; j < COL; j++) 138 if (chessboard[i][j] == 1) 139 return false; 140 return true; 141 } 142 143 void HumanPlay() 144 { 145 int x, y; 146 printf("請輸入棋子的橫坐標X:"); 147 scanf_s("%d", &x); 148 printf("請輸入棋子的縱坐標Y:"); 149 scanf_s("%d", &y); 150 151 while (x < 1 || x>3 || y < 1 || y>3) 152 { 153 printf("\n請正確輸入!\n"); 154 printf("x,y均屬於1~3\n\n"); 155 156 printf("請輸入棋子的橫坐標X:"); 157 scanf_s("%d", &x); 158 printf("請輸入棋子的縱坐標Y:"); 159 scanf_s("%d", &y); 160 } 161 162 while (chessboard[3 - y][x - 1] != 1) 163 { 164 printf("\n\n該位置已被占用!\n"); 165 printf("請選擇正確的位置\n\n"); 166 Sleep(1000); 167 168 printf("\n請輸入棋子的橫坐標X:"); 169 scanf_s("%d", &x); 170 printf("請輸入棋子的縱坐標Y:"); 171 scanf_s("%d", &y); 172 } 173 174 chessboard[3 - y][x - 1] = 2; 175 } 176 177 void PcPlay() 178 { 179 int sum = 0; 180 for (int i = 0; i < ROW; i++) 181 for (int j = 0; j < COL; j++) 182 score[i][j] = 0; 183 184 // 對每行進行分數統計 185 for (int i = 0; i < ROW; i++) 186 { 187 for (int j = 0; j < COL; j++) 188 sum += chessboard[i][j]; 189 190 switch (sum) 191 { 192 case 3: //1+1+1;重要性:最低;權重:1 193 for (int k = 0; k < COL; k++) 194 { 195 if (chessboard[i][k] == 1) 196 score[i][k] += 1; 197 } 198 break; 199 case 4: //1+1+2;重要性:次低;權重:10 200 for (int k = 0; k < COL; k++) 201 { 202 if (chessboard[i][k] == 1) 203 score[i][k] += 10; 204 } 205 break; 206 case 8: //1+2+5;重要性:較低,權值50 207 for (int k = 0; k < COL; k++) 208 { 209 if (chessboard[i][k] == 1) 210 score[i][k] += 50; 211 } 212 break; 213 case 7: //1+1+5;重要性:較高;權重:200 214 for (int k = 0; k < COL; k++) 215 { 216 if (chessboard[i][k] == 1) 217 score[i][k] += 200; //把行列的重要性比對角線高 218 } 219 break; 220 case 5: //1+2+2;重要性:次高;權重:500 221 for (int k = 0; k < COL; k++) 222 { 223 if (chessboard[i][k] == 1) 224 score[i][k] += 500; 225 } 226 break; 227 case 11: //1+5+5;重要性:最高;權重:1000 228 for (int k = 0; k < COL; k++) 229 { 230 if (chessboard[i][k] == 1) 231 score[i][k] += 1000; 232 } 233 break; 234 } 235 sum = 0; 236 } 237 238 // 對每列進行分數統計 239 for (int j = 0; j < COL; j++) 240 { 241 for (int i = 0; i < ROW; i++) 242 sum += chessboard[i][j]; 243 244 switch (sum) 245 { 246 case 3: 247 for (int k = 0; k < COL; k++) 248 { 249 if (chessboard[k][j] == 1) 250 score[k][j] += 1; 251 } 252 break; 253 case 4: 254 for (int k = 0; k < COL; k++) 255 { 256 if (chessboard[k][j] == 1) 257 score[k][j] += 10; 258 } 259 break; 260 case 8: 261 for (int k = 0; k < 262 COL; k++) 263 { 264 if (chessboard[k][j] == 1) 265 score[k][j] += 50; 266 } 267 break; 268 case 7: 269 for (int k = 0; k < COL; k++) 270 { 271 if (chessboard[k][j] == 1) //1+1+5;重要性:較高;權重:200 272 score[k][j] += 200; 273 } 274 break; 275 case 5: 276 for (int k = 0; k < COL; k++) 277 { 278 if (chessboard[k][j] == 1) 279 score[k][j] += 500; 280 } 281 break; 282 case 11: 283 for (int k = 0; k < COL; k++) 284 { 285 if (chessboard[k][j] == 1) 286 score[k][j] += 1000; 287 } 288 break; 289 } 290 sum = 0; 291 } 292 293 // 對對角線進行分數統計 294 for (int i = 0; i < ROW; i++) 295 sum += chessboard[i][i]; 296 switch (sum) 297 { 298 case 3: 299 for (int i = 0; i < COL; i++) 300 { 301 if (chessboard[i][i] == 1) 302 score[i][i] += 1; 303 } 304 break; 305 case 4: 306 for (int i = 0; i < COL; i++) 307 { 308 if (chessboard[i][i] == 1) 309 score[i][i] += 10; 310 } 311 break; 312 case 8: 313 for (int i = 0; i < COL; i++) 314 { 315 if (chessboard[i][i] == 1) 316 score[i][i] += 50; 317 } 318 break; 319 case 7: //1+1+5;權重:100 320 for (int i = 0; i < COL; i++) 321 { 322 if (chessboard[i][i] == 1) 323 score[i][i] += 100; 324 } 325 break; 326 case 5: 327 for (int i = 0; i < COL; i++) 328 { 329 if (chessboard[i][i] == 1) 330 score[i][i] += 500; 331 } 332 break; 333 case 11: 334 for (int i = 0; i < COL; i++) 335 { 336 if (chessboard[i][i] == 1) 337 score[i][i] += 1000; 338 } 339 break; 340 } 341 342 // 對反對角線進行分數統計 343 sum = 0; 344 for (int i = 0; i < ROW; i++) 345 sum += chessboard[i][2 - i]; 346 switch (sum) 347 { 348 case 3: 349 for (int i = 0; i < COL; i++) 350 { 351 if (chessboard[i][2 - i] == 1) 352 score[i][2 - i] += 1; 353 } 354 break; 355 case 4: 356 for (int i = 0; i < COL; i++) 357 { 358 if (chessboard[i][2 - i] == 1) 359 score[i][2 - i] += 10; 360 } 361 break; 362 case 8: 363 for (int i = 0; i < COL; i++) 364 { 365 if (chessboard[i][2 - i] == 1) 366 score[i][2 - i] += 50; 367 } 368 break; 369 case 7: 370 for (int i = 0; i < COL; i++) 371 { 372 if (chessboard[i][2 - i] == 1) //1+1+5;權重:100 373 score[i][2 - i] += 100; 374 } 375 break; 376 case 5: 377 for (int i = 0; i < COL; i++) 378 { 379 if (chessboard[i][2 - i] == 1) 380 score[i][2 - i] += 500; 381 } 382 break; 383 case 11: 384 for (int i = 0; i < COL; i++) 385 { 386 if (chessboard[i][2 - i] == 1) 387 score[i][2 - i] += 1000; 388 } 389 break; 390 } 391 392 int maxRow = 0, maxCol = 0; 393 for (int i = 0; i < ROW; i++) 394 for (int j = 0; j < COL; j++) 395 { 396 if (score[i][j] > score[maxRow][maxCol]) 397 { 398 maxRow = i; 399 maxCol = j; 400 } 401 } 402 chessboard[maxRow][maxCol] = 5; 403 }