(智慧)三子棋 電腦能看一步 要點小技巧才能贏
阿新 • • 發佈:2019-02-02
三子棋是個簡單的遊戲,學完陣列後可以用陣列和函式的知識寫一個三子棋
三子棋這個遊戲,如果雙方都會玩的話其實只會平局,比較無聊,但如何讓程式會玩就有點意思了
直接看程式碼吧
標頭檔案 game.h
#ifndef __GAME_H__ #define __GAME_H__ #include<stdio.h> #include<stdlib.h> #include<string.h> #define ROWS 3 #define COLS 3 void init_board(char board[ROWS][COLS], int row, int col); //初始化棋盤 void display_board(char board[ROWS][COLS], int row, int col); //列印棋盤和棋子 void player_move(char board[ROWS][COLS], int row, int col); //玩家走 void computer_move(char board[ROWS][COLS], int row, int col); //電腦走 char check_win(char board[ROWS][COLS], int row, int col); //檢查輸贏 #endif //__GAME_H__
測試程式碼 text.c
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include"game.h" void game(int f) { char board[ROWS][COLS]; char ret = 0; init_board(board, ROWS, COLS); //初始化 display_board(board, ROWS, COLS); //列印空棋盤 switch (f) //判斷誰先走,不需要brek和default { case 1: player_move(board, ROWS, COLS); display_board(board, ROWS, COLS); case 2: computer_move(board, ROWS, COLS); display_board(board, ROWS, COLS); //開始無需檢查輸贏 } while (1) { player_move(board, ROWS, COLS); display_board(board, ROWS, COLS); if (ret = check_win(board, ROWS, COLS) != ' ') { break; } computer_move(board, ROWS, COLS); //電腦和玩家走後 display_board(board, ROWS, COLS); //都列印一次棋盤 if (ret = check_win(board, ROWS, COLS) != ' ') //並檢查輸贏,分出勝負結束迴圈 { break; } } if (ret == 'X') { printf("你贏了\n"); } if (ret == 'O') { printf("hehe\n"); } if (ret == 'q') { printf("平局\n"); } } void menu() //選單 { printf("*********************************\n"); printf("*********** 三子棋 **********\n"); printf("******* YOU WILLN'T WIN *******\n"); printf("*********** 選擇 ***********\n"); printf("**** 1-先走 2-後走 0-退出 *****\n"); printf("*********************************\n"); } int main() { int input = 0; do { menu(); //列印選單 scanf("%d", &input); //玩家選擇 printf("\n"); switch (input) //根據玩家輸入,給game函式傳參或報錯、結束 { case 0: break; case 1: game(1); break; case 2: game(2); break; default: printf("輸入錯誤,重新輸入\n"); break; } } while (input); //只要玩家不選0,就接著玩 return 0; }
遊戲函式庫game
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void init_board(char board[ROWS][COLS], int row, int col) { memset(board, ' ', col*row*sizeof(char)); //將棋盤陣列初始化為空格 } static int is_full(char board[ROWS][COLS], int row, int col) //檢查期盼是否滿了 { int i = 0, j = 0; for (i = 0; i < row; i++) { for (j = 0; j < row; j++) { if (board[i][j] == ' ') { return 0; //有空格,沒滿 } } } return 1; //沒空格,滿了 } char check_win(char board[ROWS][COLS], int row, int col) { int i = 0; for (i = 0; i < row; i++) { if ((board[i][0] == board[i][1]) && (board[i][1] == board[i][2])) //檢查行 { return board[i][0]; } } for (i = 0; i < col; i++) { if ((board[0][i] == board[1][i]) && (board[1][i] == board[2][i])) //檢查列 { return board[0][i]; } } if (((board[0][0] == board[1][1]) && (board[1][1] == board[2][2])) || ((board[2][0] == board[1][1]) && (board[1][1] == board[0][2]))) //檢查對角線 { return board[0][0]; } else if (is_full(board, row, col)) //檢查棋盤滿了沒 { return 'q'; } return ' '; } void display_board(char board[ROWS][COLS], int row, int col) //列印棋盤 { int i = 0; for (i = 0; i < row; i++) { printf(" %c | %c | %c \n", board[0][i], board[1][i], board[2][i]); if (i != 2) { printf("---|---|---\n"); } } } void player_move(char board[ROWS][COLS], int row, int col) //玩家走 { unsigned int x = 0, y = 0; while (1) { printf("請輸入座標》:\n"); scanf("%d%d", &x, &y); x--; y--; if (((x<3)||(y<3))&&(board[y][x] == ' ')) { board[y][x] = 'X'; break; } printf("輸入錯誤,請重新輸入。\n"); } } void computer_move(char board[ROWS][COLS], int row, int col) //電腦走 { int max_p = 0; //最大權分,(用於找出權分最大的位置) int pow = 0; //權分 int move[] = { 0, 0 }; //落子位置 int i, j; for (i = 0; i < 3; i++) //兩個for迴圈假設電腦下在每一個位置 { for (j = 0; j < 3; j++) { if (board[i][j] == ' ') //該假設位置為空格(無子),開始計算 { //將棋盤轉化為兩個棋盤,一維就夠了,好處理 char px[9] = { '\0' }; //玩家落子盤 char po[9] = { '\0' }; //電腦已經落子的盤 int k = 0; for (k = 0; k < 9; k++) //傳值玩家落子位置 { if (*(board[0] + k) == 'X') { px[k] = *(board[0] + k); } else { px[k] = ' '; } } for (k = 0; k < 9; k++) //傳值電腦落子位置 { if (*(board[0] + k) == 'O') { po[k] = *(board[0] + k); } else { po[k] = ' '; } } k = i * 3 + j; //三維棋盤位置轉化為一維棋盤位置 px[k] = 'O'; //賦值 po[k] = 'O'; //↓位置權分計算 pow = ((px[0] - ' ' + 1)*(px[1] - ' ' + 1)*(px[2] - ' ' + 1)*(((po[0] == 'O') ^ (po[1] == 'O') ^ (po[2] == 'O'))) + (px[3] - ' ' + 1)*(px[4] - ' ' + 1)*(px[5] - ' ' + 1)*(((po[3] == 'O') ^ (po[4] == 'O') ^ (po[5] == 'O'))) + (px[6] - ' ' + 1)*(px[7] - ' ' + 1)*(px[8] - ' ' + 1)*(((po[6] == 'O') ^ (po[7] == 'O') ^ (po[8] == 'O'))) + (px[0] - ' ' + 1)*(px[3] - ' ' + 1)*(px[6] - ' ' + 1)*(((po[0] == 'O') ^ (po[3] == 'O') ^ (po[6] == 'O'))) + (px[1] - ' ' + 1)*(px[4] - ' ' + 1)*(px[7] - ' ' + 1)*(((po[1] == 'O') ^ (po[4] == 'O') ^ (po[7] == 'O'))) + (px[2] - ' ' + 1)*(px[5] - ' ' + 1)*(px[8] - ' ' + 1)*(((po[2] == 'O') ^ (po[5] == 'O') ^ (po[8] == 'O'))) + (px[0] - ' ' + 1)*(px[4] - ' ' + 1)*(px[8] - ' ' + 1)*(((po[0] == 'O') ^ (po[4] == 'O') ^ (po[8] == 'O'))) + (px[2] - ' ' + 1)*(px[4] - ' ' + 1)*(px[6] - ' ' + 1)*(((po[2] == 'O') ^ (po[4] == 'O') ^ (po[6] == 'O'))) + (po[0] - ' ' + 1)*(po[1] - ' ' + 1)*(po[2] - ' ' + 1)*(!((px[0] == 'X') || (px[1] == 'X') || (px[2] == 'X'))) + (po[3] - ' ' + 1)*(po[4] - ' ' + 1)*(po[5] - ' ' + 1)*(!((px[3] == 'X') || (px[4] == 'X') || (px[5] == 'X'))) + (po[6] - ' ' + 1)*(po[7] - ' ' + 1)*(po[8] - ' ' + 1)*(!((px[6] == 'X') || (px[7] == 'X') || (px[8] == 'X'))) + (po[0] - ' ' + 1)*(po[3] - ' ' + 1)*(po[6] - ' ' + 1)*(!((px[0] == 'X') || (px[3] == 'X') || (px[6] == 'X'))) + (po[1] - ' ' + 1)*(po[4] - ' ' + 1)*(po[7] - ' ' + 1)*(!((px[1] == 'X') || (px[4] == 'X') || (px[7] == 'X'))) + (po[2] - ' ' + 1)*(po[5] - ' ' + 1)*(po[8] - ' ' + 1)*(!((px[2] == 'X') || (px[5] == 'X') || (px[8] == 'X'))) + (po[0] - ' ' + 1)*(po[4] - ' ' + 1)*(po[8] - ' ' + 1)*(!((px[0] == 'X') || (px[4] == 'X') || (px[8] == 'X'))) + (po[2] - ' ' + 1)*(po[4] - ' ' + 1)*(po[6] - ' ' + 1)*(!((px[2] == 'X') || (px[4] == 'X') || (px[6] == 'X')))); if (pow > max_p) //新的權分高於已有最大權分,交換值,改變落子位置 { max_p = pow; move[0] = i; move[1] = j; } } } } board[move[0]][move[1]] = 'O'; //所有假設位置的權分比較結束,落子 } //權分解釋:把所有玩家贏和電腦贏的可能都列出,假設該位置落子,得到權分(位置權分【位置字元-‘ ’+1】) // 玩家棋盤權分,以某一行為例 // ((px[0] - ' ' + 1)*(px[1] - ' ' + 1)*(px[2] - ' ' + 1)*(((po[0] == 'O') ^ (po[1] == 'O') ^ (po[2] == 'O'))) // {初始值為1,一行的權分為每個位置權分的乘積 } {該行如果電腦已落子,取消該行權分 } // 電腦棋盤權分 // 也是一樣,一種贏的可能權分為每個位置權分積,如果玩家已落子則取消該可能的權分