万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(一)
+https://developer.aliyun.com/article/1522007?spm=a2c6h.13148508.setting.18.439a4f0evqNcHz
三、功能优化:会堵棋的电脑角色
说明见代码注释。
void update_computer_move(char board[ROW][COL], int row, int col) { printf("电脑下棋!\n"); int x = y = 0; int i = j = 0; //检查每行,判断是否需要堵棋 for (i = 0; i < row; i++) { int count = 0; //用于标记棋数 //数每一行玩家下了多少个棋子 for (j = 0; j < col; j++) { if (board[i][j] == '*') count++; } //当某一行玩家已经下了两个棋-->要堵棋 if (count == 2) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') { board[i][j] = '#'; return; //落子,函数结束 } } } } //检查每列,判断是否需要堵棋 //思路与上面完全相同 for (j = 0; j < col; j++) { int count = 0; for (i = 0; i < row; i++) { if (board[i][j] == '*') count++; } //若一列已经有两个棋-->堵棋 if (count == 2) { for (i = 0; i < row; i++) { if (board[i][j] == ' ') { board[i][j] = '#'; return; //落子,函数结束 } } } } int diagonal_1 = 0; //标记主对角线 //检查主对角线,判断是否需要堵棋 for (i = 0; i <row; i++) { if (board[i][i] == '*') diagonal_1++; } if (diagonal_1 == 2) { for (i = 0; i <row; i++) { if (board[i][i] == ' ') { board[i][i] = '#'; return; //落子,函数结束 } } } int diagonal_2 = 0; //标记副对角线 //检查副对角线,判断是否需要堵棋 for (i = 0, j = col - 1; i < row && j >= 0; i++, j--) { if (board[i][j] == '*') diagonal_2++; } if (diagonal_2 == 2) { for (i = 0, j = col - 1; i < row && j >= 0; i++, j--) { if (board[i][j] == ' ') { board[i][j] = '#'; return; } } } //若不符合堵棋的条件-->随机落子 while (1) { x = rand() % row; y = rand() % col; if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '#'; return; } } }
四、 功能优化:递归实现 N 子棋动态输赢判断
从三子棋到N子棋(N行N列N子),所要作出的调整无非只有两个:
1. 动态打印棋盘(见上,通过改变符号常量ROW和COL来更改棋盘大小)
2. 动态判断输赢(不用if 一一列出最终棋盘上可能的情况再一一判断谁胜谁负)
1. 思路
示意图如下(忽略图画得很丑这个事实),向八个方向进行递归,检查并统计该棋子在递归方向有多少个。
我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。
之所以要用递归解决,是因为我们每次有落子后,都需要判断是否有NUMBER个连着的相同棋子。以向左递归,symbol = '*' 为例,思路如下:
判断该棋子左侧的棋子是否是'*' ?
-不是,return symbol_count(结束函数)
-是,计数器 symbol_count++。判断再左边的棋子是否是'*'?(……不断重复该问题,以此类推)
递归思路
找重复:不断向左遍历判断该位子上的棋子是否是symbol
找变化:棋子的坐标(不断向左移动)
找出口:发现第一个棋盘上不是该棋子的位置(return symbol_count)
最终判断某一方向上的symbol_count是否等于NUMBER,若等于,则该棋子胜出。
2. 代码展示
is_Same.c
//symbol 棋子符号,'*'为玩家,'#'为电脑 //x、y为坐标参数 //左 int is_Same_left(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((y-1 >= 0) && (board[x][y-1] == symbol)) { symbol_count ++; symbol_count += is_Same_left(board, symbol, x, y-1); } return symbol_count; } //右 int is_Same_right(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((y+1 < COL) && (board[x][y+1] == symbol)) { symbol_count ++; symbol_count += is_Same_right(board, symbol, x, y+1); } return symbol_count; } //上 int is_Same_over(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((x-1 >= 0) && (board[x-1][y] == symbol)) { symbol_count ++; symbol_count += is_Same_over(board, symbol, x-1, y); } return symbol_count; } //下 int is_Same_down(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((x+1 < ROW) && (board[x+1][y] == symbol)) { symbol_count ++; symbol_count += is_Same_down(board, symbol, x+1, y); } return symbol_count; } //主对角线上半部分 int is_Same_diagonal1_over(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((x-1 >= 0) && (y-1 >= 0) && (board[x-1][y-1] == symbol)) { symbol_count ++; symbol_count += is_Same_diagonal1_over(board, symbol, x-1, y-1); } return symbol_count; } //主对角线下半部分 int is_Same_diagonal1_down(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((x+1 < ROW) && (y+1 < COL) && (board[x+1][y+1] == symbol)) { symbol_count ++; symbol_count += is_Same_diagonal1_down(board, symbol, x+1, y+1); } return symbol_count; } //副对角线上半部分 int is_Same_diagonal2_over(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((x+1 < ROW) && (y-1 >= 0) && (board[x+1][y-1] == symbol)) { symbol_count ++; symbol_count += is_Same_diagonal2_over(board, symbol, x+1, y-1); } return symbol_count; } //副对角线下半部分 int is_Same_diagonal2_down(char board[ROW][COL], char symbol, int x, int y) { int symbol_count = 0; if((x-1 >= 0) && (y+1 < COL) && (board[x-1][y+1] == symbol)) { symbol_count ++; symbol_count += is_Same_diagonal2_down(board, symbol, x-1, y+1); } return symbol_count; }
test_is_Win.c
int is_Win(char borad[ROW][COL], char symbol, int x, int y) { int cnt1,cnt2,cnt3,cnt4; cnt1 = cnt2 = cnt3 = cnt4 = 1; //左右 cnt1 += is_Same_left(borad, symbol, x, y); cnt1 += is_Same_right(borad, symbol, x, y); //上下 cnt2 += is_Same_over(borad, symbol, x, y); cnt2 += is_Same_down(borad, symbol, x, y); //主对角线 cnt3 += is_Same_diagonal1_over(borad, symbol, x, y); cnt3 += is_Same_diagonal1_down(borad, symbol, x, y); //副对角线 cnt4 += is_Same_diagonal2_over(borad, symbol, x, y); cnt4 += is_Same_diagonal2_down(borad, symbol, x, y); //判断胜利 //NUMBER 为自己定义的符号常量,用于设置多少个棋子算赢 if ((cnt1 == NUMBER) || (cnt2 == NUMBER) || (cnt3 == NUMBER) || (cnt4 == NUMBER)) { return 2; //返回2,代表赢 } else { return is_full(borad, ROW, COL); //判断是否平局了 } }
五、总结
二维数组的使用;for 循环嵌套;递归判断棋子是否一样
(懒得写了,自己总结)
六、课设大作业资源
已上传为资源,有需要者可点击链接下载资源。
资源下载 :C课设大作业 -- 三子棋
https://download.csdn.net/download/wyd_333/86265917