【C语言】五子棋(c语言实现)
这里写目录标题
- 一、最终效果
- 二、菜单打印函数
- 三、棋盘的初始化和打印
- 四、 人人对战
- 4.1 落子判空函数
- 4.2 悔棋函数
- 4.3 判胜负函数
- 4.4 人人对战
- 五、人机对战
- 5.1 是将直接调用rand生成随机值,这就不可控
- 5.2 是根据棋子赢面来判断哪里落子最好
- 六、如果选择退出程序直接exit就行
- 七、主函数调用逻辑
- 八、源代码

一、最终效果
五子棋c语言实现
二、菜单打印函数
使用这个函数来接收用户选择的模式,并通过返回值返回。
逻辑是使用一个死循环来打印,根据玩家输入的数来判断进入什么模式,输入错误数字就不出循环,正确就出循环。
int menu()
{int choice = -1;while (1){printf("----------------欢迎使用五子棋------------------------\n");printf("- 请选择模式 -\n");printf("- 1.人-人对战游戏 -\n");printf("- 2.人-机对战游戏 -\n");printf("- 0.退出游戏 -\n");printf("------------------------------------------------------\n");printf("------------------------------------------------------\n");scanf("%d", &choice);if (choice > 4 || choice < 0)printf("错误输入");elsebreak;}return choice;
}
三、棋盘的初始化和打印
在这个函数中将表示棋盘的二维数组初始化为空状态,并通过棋盘数组的只来打印棋盘。打印棋盘时用到的符号:┏ ┳ ┓┣ ╋ ┫┗ ┻ ┛。要注意有些地方添加━和空格来控制打印的棋盘是正确的。
里面每一次打印都调用draw_chessman这个函数,在这个函数中将根据数组值来判断如何打印。
//绘制棋子
void draw_chessman(int type, char* tableline) {if (type == WHITE)printf(" ●");if (type == BLACK)printf(" ○");if (type == NO)printf("%s", tableline);
}
打印棋盘的逻辑就是因为没能设置光标坐标那就只有一行一行打印然后根据边界行列值来判断。
如果要看如何打印改变光标可以参考这篇文章win32 API
void initMap(int map[N + 1][N + 1])
{//第一行和第一列显示数字for (int i = 0; i < N + 1; i++){map[0][i] = i;map[i][0] = i;if (i >= 10){map[0][i] = i - 10;map[i][0] = i - 10;}}for (int i = 0; i < N + 1; i++){//打印第一行数字if (i == 0){for (int j = 0; j < N + 1; j++){printf("%-2d", map[0][j]);}printf("\n");}//打印第一行棋盘else if (i == 1){for (int j = 0; j < N + 1; j++){if (j == 0)printf("%d", map[i][j]);else if (j == 1)draw_chessman(map[i][j], " ┏");else if (j == N)draw_chessman(map[i][j], "━┓");elsedraw_chessman(map[i][j], "━┳");}printf("\n");}//打印最后一行else if (i == N){for (int j = 0; j < N + 1; j++){if (j == 0)printf("%d", map[i][j]);else if (j == 1)draw_chessman(map[i][j], " ┗");else if (j == N)draw_chessman(map[i][j], "━┛");elsedraw_chessman(map[i][j], "━┻");}printf("\n");}else{for (int j = 0; j < N + 1; j++){if (j == 0)printf("%d", map[i][j]);else if (j == 1)draw_chessman(map[i][j], " ┣");else if (j == N)draw_chessman(map[i][j], "━┫");elsedraw_chessman(map[i][j], "━╋");}printf("\n");}}
}
四、 人人对战
人人对战逻辑中就是先让黑方下棋,输入坐标,输入错误就再次输入(死循环实现),将该坐标的二维数组值变为黑,然后让其选择悔不悔棋,我实现的是只要1是悔棋其他数字就是不悔棋。执行完不悔棋后就判断是否胜利,如果胜就跳出选择是否重来一局,如果没胜就白棋下执行相同逻辑。
4.1 落子判空函数
在这个函数中判断坐标代表的二维数组值是不是空,不是就重新输入坐标。
void judgeEmpty(int map[][N + 1], int x, int y, int goal)
{while (1){if (map[x][y] != NO){printf("已有棋子,重新输入\n");scanf("%d%d", &x, &y);}else{map[x][y] = goal;initMap(map);break;}}
}
4.2 悔棋函数
该函数里面就是根据用户选择,如果悔棋然后将刚才的地方的数组值变回空。
int retract(int map[][N + 1], int x, int y)
{int choice = -1;printf("是否悔棋:是1\n");scanf("%d", &choice);if (choice == 1){map[x][y] = NO;initMap(map);}return choice;
}
执行完不悔棋后就判断是否胜利
4.3 判胜负函数
这个函数就是将下的这个棋子的横竖斜方向判胜负。(没有判断和棋情况),如果要判断就是二维数组全不为空状态,使用遍历判断就行。
int judgeWin(int map[][N + 1], int x, int y)
{for (int i = 1; i < N + 1; i++){for (int j = 1; j < N + 1; j++){if (j < N + 1 - 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//横{if (map[i][j] == map[i][j + 1]&& map[i][j + 1] == map[i][j + 2]&& map[i][j + 2] == map[i][j + 3]&& map[i][j + 3] == map[i][j + 4])return map[i][j];}if (i < N + 1 - 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//竖{if (map[i][j] == map[i + 1][j]&& map[i + 1][j] == map[i + 2][j]&& map[i + 2][j] == map[i + 3][j]&& map[i + 3][j] == map[i + 4][j])return map[i][j];}if (i < N + 1 - 4 && j < N + 1 - 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//右下_{if (map[i][j] == map[i + 1][j + 1]&& map[i + 1][j + 1] == map[i + 2][j + 2]&& map[i + 2][j + 2] == map[i + 3][j + 3]&& map[i + 3][j + 3] == map[i + 4][j + 4])return map[i][j];}if (i > 4 && j > 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//右上{if (map[i][j] == map[i - 1][j - 1]&& map[i - 1][j - 1] == map[i - 2][j - 2]&& map[i - 2][j - 2] == map[i - 3][j - 3]&& map[i - 3][j - 3] == map[i - 4][j - 4])return map[i][j];}}}return NO;
}
4.4 人人对战
void peopleFight(int map[][N + 1])
{printf("--------人-人对战---------------\n");initMap(map);while (1){int x = -1;int y = -1;black:while (1){printf("黑方落子\n");printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, BLACK);if (retract(map, x, y) == 1)goto black;//判断胜利int ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}white:while (1){printf("白方落子\n");printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, WHITE);if (retract(map, x, y) == 1)goto white;//判断胜利ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}}
}
五、人机对战
在这个函数中用户选择落子方,然后执行逻辑与人人对战一样。
但是电脑生成棋子提供两种选择:
5.1 是将直接调用rand生成随机值,这就不可控
方法一这样写对棋子的位置生成是很不可控的,会让机器随机乱落子。
void machine(int map[][N + 1])
{printf("--------人-机对战---------------\n");srand(time(NULL));int choice = -1;printf("请输入你执黑棋还是白棋,黑1,白2\n");scanf("%d", &choice);if (choice == 1){while (1){int x = -1;int y = -1;black:while (1){printf("黑方落子\n");printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, BLACK);//判断胜利int ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}if (retract(map, x, y) == 1)goto black;next:x = rand() % 15 + 1;y = rand() % 15 + 1;if (map[x][y] != NO)goto next;judgeEmpty(map, x, y, WHITE);//判断胜利ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}}}else if (choice == 2){while (1){int x = -1;int y = -1;next2:x = rand() % 15 + 1;y = rand() % 15 + 1;if (map[x][y] != NO)goto next2;judgeEmpty(map, x, y, BLACK);//判断胜利int ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}white:while (1){printf("白方落子\n");printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, WHITE);//判断胜利ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}if (retract(map, x, y) == 1)goto white;}}}
5.2 是根据棋子赢面来判断哪里落子最好
这样实现就会让机器落子一直是去围堵你的棋子,虽然还是很垃圾,但是比方法一好多了。
void AIjudge(int color, int map[N+1][N+1])
{int num = 1, left = 0, right = 0;int n, m, score = 0, max = 0;int dx, dy;for (int i = 1; i < N+1; i++){for (int j = 1; j < N+1; j++){score = 0;if (map[i][j] != 0)continue;else{dx = 1, dy = 0, n = i, m = j;while (1)//水平向右{n += dx, m += dy;if (map[n][m] == 0){right = 0; break;}else if (map[n][m] != color || n > 15){right++; break;}else if (num < 5) { num++; }}dx = -1, dy = 0;while (1) {//水平向左n += dx; m += dy;if (map[n][m] == 0) { left = 0; break; }else if (map[n][m] != color || n < 1) { left++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);//用data来算分数,并用score来记录// |方向num = 1; right = 0, left = 0;//每一次改变方向要重置这些变量dx = 0; dy = -1; n = i; m = j;//向上while (1) {n += dx; m += dy;if (map[n][m] == 0) { left = 0; break; }else if (map[n][m] != color || m < 1) { left++; break; }else if (num < 5) { num++; }}//向下dx = 0; dy = 1; n = i; m = j;while (1) {n += dx; m += dy;if (map[n][m] == 0) { right = 0; break; }else if (map[n][m] != color || m > 15) { right++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);// \方向num = 1; right = 0, left = 0;dx = 1; dy = 1; n = i; m = j;//向右下while (1) {n += dx; m += dy;if (map[n][m] == 0) { right = 0; break; }else if (map[n][m] != color || m > 15 || n > 15) { right++; break; }else if (num < 5) { num++; }}//向左上dx = -1; dy = -1; n = i; m = j;while (1) {n += dx; m += dy;if (n > 0 && m > 0 && map[n][m] == 0) { left = 0; break; }else if ((n > 0 && m > 0 && map[n][m] != color) || m < 1 || n < 1) { left++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);// /方向`num = 1; right = 0, left = 0;dx = 1; dy = -1; n = i; m = j;//向右上while (1) {n += dx; m += dy;if (map[n][m] == 0) { right = 0; break; }else if (map[n][m] != color || n > 15 || m < 1) { right++; break; }else if (num < 5) { num++; }}//向左下dx = -1; dy = 1; n = i; m = j;while (1) {n += dx; m += dy;if (n > 0 && m > 0 && map[n][m] == 0) { left = 0; break; }else if ((n > 0 && m > 0 && map[n][m] != color) || n < 1 || m > 15) { left++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);if (score > max) {//每一次用max保存分数,下一次比较,最后找出最大值max = score;position[0] = i;//用来保存每一次的位置和分数position[1] = j;position[2] = score;}}}}
}
int Data(int num, int count)
{switch (num){case 1: if (count == 0)//活一:表示在该处落子,就只有这一个棋子,两边都没有阻挡(边界或对方的棋子)优势较大return 2;else if (count == 1)//冲一:表示在该处落子,就只有这一个棋子,两边有一种阻挡(边界或对方的棋子)优势较小return 1;else return 0;break;case 2:if (count == 0)//活二;接下来都同理活一和冲一return 20;else if (count == 1)//冲二return 10;else return 0;break;case 3:if (count == 0)//活三return 300;else if (count == 1)//冲三return 50;else return 0;break;case 4:if (count == 0)//活四return 4000;else if (count == 1)//冲四return 1000;else return 0;break;case 5://五return 5000;break;default:return 0;break;}
}
void machine(int map[][N + 1])
{printf("--------人-机对战---------------\n");int choice = -1;do {printf("请输入你执黑棋还是白棋,黑1,白2\n");scanf("%d", &choice);} while (choice != 1 && choice != 2);initMap(map);if (choice == 1){while (1){int x = -1;int y = -1;black:while (1){printf("黑方落子\n");printf("现在棋盘为%d*%d请输入要落下的坐标x y\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, BLACK);if (retract(map, x, y)){map[x][y] = 0;initMap(map);goto black;}int ret = judgeWin(map, x, y);if (ret == BLACK) //判断胜利break;int old_max = 0, new_max = 0, n = 0, m = 0;int AIx = -1, AIy = -1;AIjudge(1, map);//判断黑子的优势位置old_max = position[2];//保存该位置分数AIx = position[0]; AIy = position[1];//保存该位置的坐标,注意行列和xy轴的对应关系position[0] = 0, position[1] = 0, position[2] = 0;AIjudge(-1, map);//判断白子的优势位置new_max = position[2];//保存分数if (new_max >= old_max) //判断哪个位置的分数大,从而判断是堵截还是进攻{AIx = position[0]; AIy = position[1];}judgeEmpty(map, AIx, AIy, WHITE);ret = judgeWin(map, x, y);if (ret == WHITE) //判断胜利break;}}else if (choice == 2){while (1){int x = -1, y = -1;int old_max = 0, new_max = 0, n = 0, m = 0;int AIx = -1, AIy = -1;AIjudge(1, map);//判断黑子的优势位置old_max = position[2];//保存该位置分数AIx = position[0]; AIy = position[1];//保存该位置的坐标,注意行列和xy轴的对应关系position[0] = 0, position[1] = 0, position[2] = 0;AIjudge(-1, map);//判断白子的优势位置new_max = position[2];//保存分数if (new_max >= old_max) //判断哪个位置的分数大,从而判断是堵截还是进攻{AIx = position[0]; AIy = position[1];}judgeEmpty(map, AIx, AIy, BLACK);int ret = judgeWin(map, x, y);if (ret == BLACK) //判断胜利 break;white:while (1){printf("白方落子\n");printf("现在棋盘为%d*%d请输入要落下的坐标x y\n", N, N);scanf("%d,%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, WHITE);if (retract(map, x, y)){map[x][y] = 0;initMap(map);goto white;}ret = judgeWin(map, x, y);if (ret == WHITE) //判断胜利break;}}}
六、如果选择退出程序直接exit就行
七、主函数调用逻辑
用死循环调用,然后根据menu返回值通过switch-case来调用相应模式函数,最后在让用户选择是否再来一局,如果再来一局就不出循环,不再来就出循环。
int main()
{while (1){int map[N + 1][N + 1] = { 0 };int choice = menu();switch (choice){case 1://调用人人对打peopleFight(map);break;case 2://调用人机对打machine(map);break;case 0:printf("退出成功\n");exit(0);break;}int a = 0;printf("是否再来一盘:是1,不是0\n");scanf("%d", &a);if (a == 0)break;}return 0;
}
八、源代码
源码呈上,为方便复制写在同一个文件之下,可以多文件进行。
# define _CRT_SECURE_NO_WARNINGS 1;
#include<stdio.h>
#include <stdlib.h>
#include<time.h>
#define N 15 //表示棋盘的行列
#define NO 0 //表示没下子的状态
#define BLACK 1
#define WHITE -1
//棋盘初始化函数
void initMap(int map[N + 1][N + 1]);
//落子函数
void judgeEmpty(int map[][N + 1], int x, int y, int goal);
//人人对打
void peopleFight(int map[][N + 1]);
//判断胜利
int judgeWin(int map[][N + 1], int x, int y);
//悔棋
int retract(int map[][N + 1], int x, int y);
//人机对打
void machine(int map[][N + 1]);
//打印菜单并返回选择的模式
int menu();
void AIjudge(int color, int map[N + 1][N + 1]);
int Data(int num, int count);
int position[3];int main()
{while (1){int map[N + 1][N + 1] = { 0 };int choice = menu();switch (choice){case 1://调用人人对打peopleFight(map);break;case 2://调用人机对打machine(map);break;case 0:printf("退出成功\n");exit(0);break;}int a = 0;printf("是否再来一盘:是1,不是0\n");scanf("%d", &a);if (a == 0)break;}return 0;
}
int menu()
{int choice = -1;while (1){printf("----------------欢迎使用五子棋------------------------\n");printf("- 请选择模式 -\n");printf("- 1.人-人对战游戏 -\n");printf("- 2.人-机对战游戏 -\n");printf("- 0.退出游戏 -\n");printf("------------------------------------------------------\n");printf("------------------------------------------------------\n");scanf("%d", &choice);if (choice > 4 || choice < 0)printf("错误输入");elsebreak;}return choice;
}void judgeEmpty(int map[][N + 1], int x, int y, int goal)
{while (1){if (map[x][y] != NO){printf("已有棋子,重新输入\n");scanf("%d%d", &x, &y);}else{map[x][y] = goal;initMap(map);break;}}
}int judgeWin(int map[][N + 1], int x, int y)
{for (int i = 1; i < N + 1; i++){for (int j = 1; j < N + 1; j++){if (j < N + 1 - 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//横{if (map[i][j] == map[i][j + 1]&& map[i][j + 1] == map[i][j + 2]&& map[i][j + 2] == map[i][j + 3]&& map[i][j + 3] == map[i][j + 4])return map[i][j];}if (i < N + 1 - 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//竖{if (map[i][j] == map[i + 1][j]&& map[i + 1][j] == map[i + 2][j]&& map[i + 2][j] == map[i + 3][j]&& map[i + 3][j] == map[i + 4][j])return map[i][j];}if (i < N + 1 - 4 && j < N + 1 - 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//右下_{if (map[i][j] == map[i + 1][j + 1]&& map[i + 1][j + 1] == map[i + 2][j + 2]&& map[i + 2][j + 2] == map[i + 3][j + 3]&& map[i + 3][j + 3] == map[i + 4][j + 4])return map[i][j];}if (i > 4 && j > 4 && (map[i][j] == BLACK || map[i][j] == WHITE))//右上{if (map[i][j] == map[i - 1][j - 1]&& map[i - 1][j - 1] == map[i - 2][j - 2]&& map[i - 2][j - 2] == map[i - 3][j - 3]&& map[i - 3][j - 3] == map[i - 4][j - 4])return map[i][j];}}}return NO;
}//绘制棋子
void draw_chessman(int type, char* tableline) {if (type == WHITE)printf(" ●");if (type == BLACK)printf(" ○");if (type == NO)printf("%s", tableline);
}void initMap(int map[N + 1][N + 1])
{//第一行和第一列显示数字for (int i = 0; i < N + 1; i++){map[0][i] = i;map[i][0] = i;if (i >= 10){map[0][i] = i - 10;map[i][0] = i - 10;}}for (int i = 0; i < N + 1; i++){//打印第一行数字if (i == 0){for (int j = 0; j < N + 1; j++){printf("%-2d", map[0][j]);}printf("\n");}//打印第一行棋盘else if (i == 1){for (int j = 0; j < N + 1; j++){if (j == 0)printf("%d", map[i][j]);else if (j == 1)draw_chessman(map[i][j], " ┏");else if (j == N)draw_chessman(map[i][j], "━┓");elsedraw_chessman(map[i][j], "━┳");}printf("\n");}//打印最后一行else if (i == N){for (int j = 0; j < N + 1; j++){if (j == 0)printf("%d", map[i][j]);else if (j == 1)draw_chessman(map[i][j], " ┗");else if (j == N)draw_chessman(map[i][j], "━┛");elsedraw_chessman(map[i][j], "━┻");}printf("\n");}else{for (int j = 0; j < N + 1; j++){if (j == 0)printf("%d", map[i][j]);else if (j == 1)draw_chessman(map[i][j], " ┣");else if (j == N)draw_chessman(map[i][j], "━┫");elsedraw_chessman(map[i][j], "━╋");}printf("\n");}}
}//void machine(int map[][N + 1])
//{
// printf("--------人-机对战---------------\n");
// srand(time(NULL));
// int choice = -1;
// printf("请输入你执黑棋还是白棋,黑1,白2\n");
// scanf("%d", &choice);
// if (choice == 1)
// {
// while (1)
// {
// int x = -1;
// int y = -1;
// black:
// while (1)
// {
// printf("黑方落子\n");
// printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);
// scanf("%d%d", &x, &y);
// if (x < 0 || x > N || y < 0 || y > N)
// printf("错误输入\n");
// else
// break;
// }
// judgeEmpty(map, x, y, BLACK);
// //判断胜利
// int ret = judgeWin(map, x, y);
// if (ret == BLACK)
// {
// printf("黑方获胜\n");
// return;
// }
// else if (ret == WHITE)
// {
// printf("白方获胜\n");
// return;
// }
// if (retract(map, x, y) == 1)
// goto black;
// next:
// x = rand() % 15 + 1;
// y = rand() % 15 + 1;
// if (map[x][y] != NO)
// goto next;
// judgeEmpty(map, x, y, WHITE);
// //判断胜利
// ret = judgeWin(map, x, y);
// if (ret == BLACK)
// {
// printf("黑方获胜\n");
// return;
// }
// else if (ret == WHITE)
// {
// printf("白方获胜\n");
// return;
// }
// }
// }
// else if (choice == 2)
// {
// while (1)
// {
// int x = -1;
// int y = -1;
// next2:
// x = rand() % 15 + 1;
// y = rand() % 15 + 1;
// if (map[x][y] != NO)
// goto next2;
// judgeEmpty(map, x, y, BLACK);
// //判断胜利
// int ret = judgeWin(map, x, y);
// if (ret == BLACK)
// {
// printf("黑方获胜\n");
// return;
// }
// else if (ret == WHITE)
// {
// printf("白方获胜\n");
// return;
// }
// white:
// while (1)
// {
// printf("白方落子\n");
// printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);
// scanf("%d%d", &x, &y);
// if (x < 0 || x > N || y < 0 || y > N)
// printf("错误输入\n");
// else
// break;
// }
// judgeEmpty(map, x, y, WHITE);
// //判断胜利
// ret = judgeWin(map, x, y);
// if (ret == BLACK)
// {
// printf("黑方获胜\n");
// return;
// }
// else if (ret == WHITE)
// {
// printf("白方获胜\n");
// return;
// }
// if (retract(map, x, y) == 1)
// goto white;
//
// }
// }
//
//}void peopleFight(int map[][N + 1])
{printf("--------人-人对战---------------\n");initMap(map);while (1){int x = -1;int y = -1;black:while (1){printf("黑方落子\n");printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, BLACK);if (retract(map, x, y) == 1)goto black;//判断胜利int ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}white:while (1){printf("白方落子\n");printf("请输入要落下的坐标现在棋盘为%d*%d\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, WHITE);if (retract(map, x, y) == 1)goto white;//判断胜利ret = judgeWin(map, x, y);if (ret == BLACK){printf("黑方获胜\n");return;}else if (ret == WHITE){printf("白方获胜\n");return;}}
}int retract(int map[][N + 1], int x, int y)
{int choice = -1;printf("是否悔棋:是1\n");scanf("%d", &choice);if (choice == 1){map[x][y] = NO;initMap(map);}return choice;
}
void AIjudge(int color, int map[N+1][N+1])
{int num = 1, left = 0, right = 0;int n, m, score = 0, max = 0;int dx, dy;for (int i = 1; i < N+1; i++){for (int j = 1; j < N+1; j++){score = 0;if (map[i][j] != 0)continue;else{dx = 1, dy = 0, n = i, m = j;while (1)//水平向右{n += dx, m += dy;if (map[n][m] == 0){right = 0; break;}else if (map[n][m] != color || n > 15){right++; break;}else if (num < 5) { num++; }}dx = -1, dy = 0;while (1) {//水平向左n += dx; m += dy;if (map[n][m] == 0) { left = 0; break; }else if (map[n][m] != color || n < 1) { left++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);//用data来算分数,并用score来记录// |方向num = 1; right = 0, left = 0;//每一次改变方向要重置这些变量dx = 0; dy = -1; n = i; m = j;//向上while (1) {n += dx; m += dy;if (map[n][m] == 0) { left = 0; break; }else if (map[n][m] != color || m < 1) { left++; break; }else if (num < 5) { num++; }}//向下dx = 0; dy = 1; n = i; m = j;while (1) {n += dx; m += dy;if (map[n][m] == 0) { right = 0; break; }else if (map[n][m] != color || m > 15) { right++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);// \方向num = 1; right = 0, left = 0;dx = 1; dy = 1; n = i; m = j;//向右下while (1) {n += dx; m += dy;if (map[n][m] == 0) { right = 0; break; }else if (map[n][m] != color || m > 15 || n > 15) { right++; break; }else if (num < 5) { num++; }}//向左上dx = -1; dy = -1; n = i; m = j;while (1) {n += dx; m += dy;if (n > 0 && m > 0 && map[n][m] == 0) { left = 0; break; }else if ((n > 0 && m > 0 && map[n][m] != color) || m < 1 || n < 1) { left++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);// /方向`num = 1; right = 0, left = 0;dx = 1; dy = -1; n = i; m = j;//向右上while (1) {n += dx; m += dy;if (map[n][m] == 0) { right = 0; break; }else if (map[n][m] != color || n > 15 || m < 1) { right++; break; }else if (num < 5) { num++; }}//向左下dx = -1; dy = 1; n = i; m = j;while (1) {n += dx; m += dy;if (n > 0 && m > 0 && map[n][m] == 0) { left = 0; break; }else if ((n > 0 && m > 0 && map[n][m] != color) || n < 1 || m > 15) { left++; break; }else if (num < 5) { num++; }}score += Data(num, right + left);if (score > max) {//每一次用max保存分数,下一次比较,最后找出最大值max = score;position[0] = i;//用来保存每一次的位置和分数position[1] = j;position[2] = score;}}}}
}
int Data(int num, int count)
{switch (num){case 1: if (count == 0)//活一:表示在该处落子,就只有这一个棋子,两边都没有阻挡(边界或对方的棋子)优势较大return 2;else if (count == 1)//冲一:表示在该处落子,就只有这一个棋子,两边有一种阻挡(边界或对方的棋子)优势较小return 1;else return 0;break;case 2:if (count == 0)//活二;接下来都同理活一和冲一return 20;else if (count == 1)//冲二return 10;else return 0;break;case 3:if (count == 0)//活三return 300;else if (count == 1)//冲三return 50;else return 0;break;case 4:if (count == 0)//活四return 4000;else if (count == 1)//冲四return 1000;else return 0;break;case 5://五return 5000;break;default:return 0;break;}
}
void machine(int map[][N + 1])
{printf("--------人-机对战---------------\n");int choice = -1;do {printf("请输入你执黑棋还是白棋,黑1,白2\n");scanf("%d", &choice);} while (choice != 1 && choice != 2);initMap(map);if (choice == 1){while (1){int x = -1;int y = -1;black:while (1){printf("黑方落子\n");printf("现在棋盘为%d*%d请输入要落下的坐标x y\n", N, N);scanf("%d%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, BLACK);if (retract(map, x, y)){map[x][y] = 0;initMap(map);goto black;}int ret = judgeWin(map, x, y);if (ret == BLACK) //判断胜利break;int old_max = 0, new_max = 0, n = 0, m = 0;int AIx = -1, AIy = -1;AIjudge(1, map);//判断黑子的优势位置old_max = position[2];//保存该位置分数AIx = position[0]; AIy = position[1];//保存该位置的坐标,注意行列和xy轴的对应关系position[0] = 0, position[1] = 0, position[2] = 0;AIjudge(-1, map);//判断白子的优势位置new_max = position[2];//保存分数if (new_max >= old_max) //判断哪个位置的分数大,从而判断是堵截还是进攻{AIx = position[0]; AIy = position[1];}judgeEmpty(map, AIx, AIy, WHITE);ret = judgeWin(map, x, y);if (ret == WHITE) //判断胜利break;}}else if (choice == 2){while (1){int x = -1, y = -1;int old_max = 0, new_max = 0, n = 0, m = 0;int AIx = -1, AIy = -1;AIjudge(1, map);//判断黑子的优势位置old_max = position[2];//保存该位置分数AIx = position[0]; AIy = position[1];//保存该位置的坐标,注意行列和xy轴的对应关系position[0] = 0, position[1] = 0, position[2] = 0;AIjudge(-1, map);//判断白子的优势位置new_max = position[2];//保存分数if (new_max >= old_max) //判断哪个位置的分数大,从而判断是堵截还是进攻{AIx = position[0]; AIy = position[1];}judgeEmpty(map, AIx, AIy, BLACK);int ret = judgeWin(map, x, y);if (ret == BLACK) //判断胜利 break;white:while (1){printf("白方落子\n");printf("现在棋盘为%d*%d请输入要落下的坐标x y\n", N, N);scanf("%d,%d", &x, &y);if (x < 0 || x > N || y < 0 || y > N)printf("错误输入\n");elsebreak;}judgeEmpty(map, x, y, WHITE);if (retract(map, x, y)){map[x][y] = 0;initMap(map);goto white;}ret = judgeWin(map, x, y);if (ret == WHITE) //判断胜利break;}}}
相关文章:

【C语言】五子棋(c语言实现)
这里写目录标题 一、最终效果二、菜单打印函数三、棋盘的初始化和打印四、 人人对战4.1 落子判空函数4.2 悔棋函数4.3 判胜负函数4.4 人人对战 五、人机对战5.1 是将直接调用rand生成随机值,这就不可控5.2 是根据棋子赢面来判断哪里落子最好 六、如果选择退出程序直…...

【数据结构——查找】顺序查找(头歌实践教学平台习题)【合集】
目录😋 任务描述 相关知识 测试说明 我的通关代码: 测试结果: 任务描述 本关任务:实现顺序查找的算法。 相关知识 为了完成本关任务,你需要掌握:1.根据输入数据建立顺序表,2.顺序表的输出,…...

Python的3D可视化库【vedo】2-1 (plotter模块) 绘制器的使用
文章目录 1 相关用语及其关系2 Plotter类的基本使用3 Plotter类具体的初始化设置3.1 全部初始化参数3.2 使用不同的axes vedo是Python实现的一个用于辅助科学研究的3D可视化库。 vedo的plotter模块封装了绘制器类Plotter。 Plotter实例可以用于显示3D图形对象、控制渲染器行为、…...

6.1 初探MapReduce
MapReduce是一种分布式计算框架,用于处理大规模数据集。其核心思想是“分而治之”,通过Map阶段将任务分解为多个简单任务并行处理,然后在Reduce阶段汇总结果。MapReduce编程模型包括Map和Reduce两个阶段,数据来源和结果存储通常在…...

【数模学习笔记】模糊综合评价
声明:以下笔记中的图片均来自“数学建模学习交流”清风老师的课程ppt,仅用作学习交流使用 模糊综合评价 文章目录 模糊综合评价模糊数学经典集合和模糊集合的基本概念经典集合和特征函数模糊集合和隶属函数模糊集合的分类 隶属函数的确定方法方法一 模糊…...
【C语言】库函数常见的陷阱与缺陷(四):内存内容操作函数[1]--memcmp
目录 一、功能与用法 1.1. 功能 1.2. 场景用法 二、陷阱与缺陷 2.1. 比较范围限制问题 2.2. 数据类型兼容性隐患 2.3. 其它 三、安全使用建议 四、代码示例 在 C 语言编程的世界里,内存内容操作函数宛如精密的工具,肩负着操控内存数据的重任。它们能灵活地复制、移动…...

jmeter CLI Mode 传参实现动态设置用户数
一.需求 CLI 运行模式下每次运行想要传入不同的用户数,比如寻找瓶颈值的场景,需要运行多次设置不同的用户数。 二.解决思路 查看官方API Apache JMeter - Users Manual: Getting Started api CLI Mode 一节中提到可以使用如下参数做属性的替换&#…...

数据库和SQL的基本概念
目录 定义数据分类非结构化数据:半结构化数据 : 结构化数据 : SQL(Structured Query Language)概念分类 数据库管理概念理解 定义 数据库(Database)是按照数据结构来组织、存储和管理数据的建立在计算机存储设备上的仓库。 数据库是长期储存在计算机内、有组织的…...
CSS系列(9)-- Transform 变换详解
前端技术探索系列:CSS Transform 变换详解 🔄 致读者:探索 CSS 变换的魔力 👋 前端开发者们, 今天我们将深入探讨 CSS Transform,学习如何创建引人注目的 2D 和 3D 变换效果。 2D 变换基础 Ὠ…...

一些浅显易懂的IP小定义
IP归属地(也叫IP地址,IP属地) 互联网协议地址,每个设备上的唯一的网络身份证明。用于确保网络数据能够精准传送到你的设备上。 基于IP数据云全球IP归属地解析,示例Python代码 curl -X POST https://route.showapi.co…...
C 语言动态爱心代码
C 语言动态爱心代码 代码 #include <stdio.h> #include <math.h> #include <windows.h> #include <tchar.h> float f(float x, float y, float z) {float a x * x 9.0f / 4.0f * y * y z * z - 1;return a * a * a - x * x * z * z * z - 9.0f / …...

【Figma_01】Figma软件初始与使用
Figma初识与学习准备 背景介绍软件使用1.1 切换主题1.2 官方社区 设计界面2.1 创建一个项目2.2 修改文件名2.3 四种模式2.4 新增界面2.5 图层2.6 工具栏2.7 属性栏section透明度和圆角改变多边形的边数渐变效果描边设置阴影等特效拖拽相同的图形 背景介绍 Ul设计:User Interfa…...

【Python篇】PyQt5 超详细教程——由入门到精通(序篇)
文章目录 PyQt5 超详细入门级教程前言序篇:1-3部分:PyQt5基础与常用控件第1部分:初识 PyQt5 和安装1.1 什么是 PyQt5?1.2 在 PyCharm 中安装 PyQt51.3 在 PyCharm 中编写第一个 PyQt5 应用程序1.4 代码详细解释1.5 在 PyCharm 中运…...

day2 数据结构 结构体的应用
思维导图 小练习: 定义一个数组,用来存放从终端输入的5个学生的信息【学生的信息包含学生的姓名、年纪、性别、成绩】 1>封装函数 录入5个学生信息 2>封装函数 显示学生信息 3>封装函数 删除第几个学生信息,删除后调用显示学…...
CSS 进阶教程:从定位到动画与布局
文章目录 🌟 CSS 进阶教程:从定位到动画与布局🌟 目录🌟 1. 定位**Static(默认定位)****Relative(相对定位)****Absolute(绝对定位)****Fixed(固定…...
Nginx性能优化全方案:打造一个高效服务器
Nginx性能优化全方案:打造一个高效服务器 调整工作进程数和线程数调整工作进程数调整线程数 启用Gzip压缩安装Gzip模块配置Gzip压缩 配置缓存策略配置浏览器缓存时间配置代理服务器缓存时间 优化文件访问方式使用sendfile()函数发送文件数据启用sendfile_max_chunk和…...
详解Maven的setting配置文件中mirror和repository的区别
在Maven的setting中,我们常常会用到mirror和repository配置,其中mirror是镜像的意思,而repository则是仓库的意思,它们两者都可以改变maven下载项目以来jar包的地址。 仓库(repository) repository就是个…...

框架模块说明 #07 API加密
背景 在实际开发过程中,我们通常会涉及到数据加密的问题。本文重点探讨两个方面:一是外部接口调用时的数据加密,二是服务间调用的数据加密与解密。 对于外部接口调用,每个用户将拥有独立的动态 AES 加密密钥(KEY&…...

安卓BLE蓝牙开发经验分享
注意点一:一开始必须申请权限,否则后面根本无法成功。 注意点二:BLE使用向某个特征写入来发送数据,写入一次默认长度是23字节,必须向蓝牙设备申请更大字节的写入才能发送更多字节。(23字节是BLE通信的最小…...
后缀表达式有什么场景应用
后缀表达式(Reverse Polish Notation, RPN)在多个领域中有广泛的应用,主要由于其简洁性和易于计算机处理的特点。以下是一些主要的应用场景: ### 1. **编译器和解释器** 后缀表达式在编译器和解释器中用于表达式求值。由于后缀表…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...