C语言实现五子棋功能全解析
目录
- 1、game.h
- 2、test.c
- 3、game.c
- 4、游戏功能详解
- (1)、棋盘初始化
- (2)、棋盘的打印
- (3)、玩家下棋
- (4)、电脑下棋
- (5)、判断游戏输赢
- (6)、判断棋盘是否满了
- 5、AI算法下棋
- (1)、判断自己是否会赢(CheckComputer)
- (2)、对玩家进行拦截(CheckPlayer)
- (3)、加入AI算法后game.c的改动
1、game.h
game.h:自定义头文件,用于:
- 库函数头文件的包含
- 符号与结构的声明
- 函数的定义
//防止头文件被重复包含 #pragma once //头文件的包含 #include<stdio.h> #include<stdlib.h> #include<time.h> //符号的定义:使棋盘的大小可以跟着row和col的改变而改变 #define ROW 5 #define COL 5 //函数的声明 //棋盘初始化 void BoardInit(char arr[ROW][COL], int row, int col); //打印棋盘 void BoardPrint(char arr[ROW][COL], int row, int col); //玩家下棋 void PlayerMove(char arr[ROW][COL], int row, int col); //电脑下棋 void ComputerMove(char arr[ROW][COL], int row, int col); //判断输赢 char IsWin(char arr[ROW][COL], int row, int col); //判断棋盘是否满了 int IsFull(char board[ROW][COL], int row, int col);
2、test.c
test.c:用于游戏逻辑的测试
#define _CRT_SECURE_NO_WARNINGS 1 //自定义头文件的包含 #include"game.h" void menu() { printf("================================\n"); printf("========= 1. play ==========\n"); printf("========= 0. exit ==========\n"); printf("================================\n"); } //游戏逻辑的实现 void game() { //定义一个二维数组来存储下棋的数据 char arr[ROW][COL] = { 0 }; //棋盘初始化 BoardInit(arr, ROW, COL); //打印棋盘 BoardPrint(arr, ROW, COL); char ch = 0; while (1) { //玩家下棋 PlayerMove(arr, ROW, COL); //打印棋盘 BoardPrint(arr, ROW, COL); //判断输赢 ch = IsWin(arr, ROW, COL); if (ch != 'C') break; //电脑下棋 ComputerMove(arr, ROW, COL); //打印棋盘 BoardPrint(arr, ROW, COL); //判断输赢 ch = IsWin(arr, ROW, COL); if (ch != 'C') break; } if (ch == '*') printf("直接拿下!\n"); else if (ch == '#') printf("你竟然打不过人机!\n"); else printf("平局,得加油啊!\n"); } int main() { int input = 0; //设置随机数种子 srand((unsigned int)time(NULL)); do { //菜单 menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: //玩游戏 game(); break; case 0: printf("退出游戏\n"); break; default: printf("输入错误,请重新输入!\n"); break; } } while (input); return 0; }
3、game.c
game.c:游戏功能的实现
#define _CRT_SECURE_NO_WARNINGS 1 //自定义头文件的包含 #include"game.h" //函数的定义 //棋盘初始化 void BoardInit(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { arr[i][j] = ' '; } } } //打印棋盘 void BoardPrint(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { //打印分割竖向分割 for (j = 0; j < col; j++) { printf(" %c ", arr[i][j]); if (j < col - 1) printf("|"); } //一行完毕之后打印分隔符 printf("\n"); //打印横向分割 if (i < row - 1) //最后一行不打印横线分隔符 { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } } //一行完毕之后打印分隔符 printf("\n"); } } //玩家下棋 void PlayerMove(char arr[ROW][COL], int row, int col) { //获取玩家坐标 int x = 0; int y = 0; printf("玩家下棋\n"); while (1) { printf("请输入坐标:>"); scanf("%d %d", &x, &y); //判断坐标合法性 if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { //把玩家坐标对应数组下标 x -= 1; y -= 1; //判断坐标是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '*'; //假设玩家为*号 break; } else { printf("该坐标已被占用\n"); } } else { printf("坐标非法\n"); } } } //电脑下棋 void ComputerMove(char arr[ROW][COL], int row, int col) { printf("电脑下棋\n"); while (1) { //在主函数生成种子srand //随机生成范围内的坐标 int x = rand() % row; int y = rand() % col; //判断坐标是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '#'; //假设电脑为#号 break; } } } //判断输赢 char IsWin(char board[ROW][COL], int row, int col) { /* * 约定返回*代表玩家赢 * 返回#代表电脑赢 * 返回D代表平局 * 返回C代表继续 */ int i = 0; int j = 0; //判断行 for (i = 0; i < row; i++) { int count = 0; //标记相同棋子的个数 for (j = 0; j < col - 1; j++) { if (board[i][j] == board[i][j + 1] && board[i][j] != ' ') count++; } if (count == col - 1) //一次判断有两个棋子 return board[i][j]; } //判断列 for (i = 0; i < col; i++) { int count = 0; for (j = 0; j < row - 1; j++) { if (board[j][i] == board[j + 1][i] && board[j][i] != ' ') { count++; } } if (count == row - 1) return board[j][i]; } //判断两条斜边 //第一条 int count = 0; for (i = 0, j = 0; i < row - 1 && j < col - 1; i++, j++) { if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //第二条 count = 0; //把count重新置为0(易错) //注意:这里i+1,j-1,所以i小于row-1,j>0,而不是i<row,j>=0(易错) for (i = 0, j = col - 1; i < row - 1 && j > 0; i++, j--) { if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //判断棋盘是否满了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情况都没有返回,游戏继续 return 'C'; } //判断棋盘是否满了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情况都没有返回,游戏继续 return 'C'; } //判断棋盘是否满了 int IsFull(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') return 0; //有空格就返回0 } } return 1; }
4、游戏功能详解
(1)、棋盘初始化
void BoardInit(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { arr[i][j] = ' '; } } }
(2)、棋盘的打印
void BoardPrint(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { //打印分割竖向分割 for (j = 0; j < col; j++) { printf(" %c ", arr[i][j]); if (j < col - 1) printf("|"); } //一行完毕之后打印分隔符 printf("\n"); //打印横向分割 if (i < row - 1) //最后一行不打印横线分隔符 { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } } //一行完毕之后打印分隔符 printf("\n"); } }
(3)、玩家下棋
void PlayerMove(char arr[ROW][COL], int row, int col) { //获取玩家坐标 int x = 0; int y = 0; printf("玩家下棋\n"); while (1) { printf("请输入坐标:>"); scanf("%d %d", &x, &y); //判断坐标合法性 if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { //把玩家坐标对应数组下标 x -= 1; y -= 1; //判断坐标是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '*'; //假设玩家为*号 break; } else { printf("该坐标已被占用\n"); } } else { printf("坐标非法\n"); } } }
(4)、电脑下棋
void ComputerMove(char arr[ROW][COL], int row, int col) { printf("电脑下棋\n"); while (1) { //在主函数生成种子srand //随机生成范围内的坐标 int x = rand() % row; int y = rand() % col; //判断坐标是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '#'; //假设电脑为#号 break; } } }
(5)、判断游戏输赢
char IsWin(char board[ROW][COL], int row, int col) { /* * 约定返回*代表玩家赢 * 返回#代表电脑赢 * 返回D代表平局 * 返回C代表继续 */ int i = 0; int j = 0; //判断行 for (i = 0; i < row; i++) { int count = 0; //标记相同棋子的个数 for (j = 0; j < col - 1; j++) { if (board[i][j] == board[i][j + 1] && board[i][j] != ' ') count++; } if (count == col - 1) //一次判断有两个棋子 return board[i][j]; } //判断列 for (i = 0; i < col; i++) { int count = 0; for (j = 0; j < row - 1; j++) { if (board[j][i] == board[j + 1][i] && board[j][i] != ' ') { count++; } } if (count == row - 1) return board[j][i]; } //判断两条斜边 //第一条 int count = 0; for (i = 0, j = 0; i < row - 1 && j < col - 1; i++, j++) { if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //第二条 count = 0; //把count重新置为0(易错) //注意:这里i+1,j-1,所以i小于row-1,j>0,而不是i<row,j>=0(易错) for (i = 0, j = col - 1; i < row - 1 && j > 0; i++, j--) { if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //判断棋盘是否满了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情况都没有返回,游戏继续 return 'C'; } //判断棋盘是否满了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情况都没有返回,游戏继续 return 'C'; }
(6)、判断棋盘是否满了
int IsFull(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (board[i][j] == ' ') return 0; //有空格就返回0 } } return 1; }
5、AI算法下棋
大家可以发现,在上面的代码中,电脑下棋是非常笨拙的,因为电脑产生的坐标是随机的,即不会拦截玩家,也不会判断自己,所以这里我们可以设计一个小小的算法来让电脑变得聪明起来,让它拥有拦截和判断功能。具体思路和代码如下:
(1)、判断自己是否会赢(CheckComputer)
//电脑检查自己是否会赢 //约定如果在函数内部成功判断就返回1 //判断失败则返回0 int CheckComputer(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; //判断每一行是否有两个相连的棋子,如果有,且第三个棋格为空,则落棋 for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][0] == '#' && board[i][2] == ' ') { board[i][2] = '#'; return 1; //成功判断,返回1 } if (board[i][0] == board[i][2] && board[i][0] == '#' && board[i][1] == ' ') { board[i][1] = '#'; return 1; } if (board[i][1] == board[i][2] && board[i][1] == '#' && board[i][0] == ' ') { board[i][0] = '#'; return 1; } } //判断每一列是否有两个相连的棋子,如果有,且第三个棋格为空,则落棋 for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[0][j] == '#' && board[2][j] == ' ') { board[2][j] = '#'; return 1; } if (board[0][j] == board[2][j] && board[0][j] == '#' && board[1][j] == ' ') { board[1][j] = '#'; return 1; }if (board[1][j] == board[2][j] && board[1][j] == '#' && board[0][j] == ' ') { board[0][j] = '#'; return 1; } } //判断两条对角线是否有两个相连的棋子,如果有,且第三个棋格为空,则落棋 { //第一条 if (board[0][0] == board[1][1] && board[0][0] == '#' && board[2][2] == ' ') { board[2][2] = '#'; return 1; } if (board[0][0] == board[2][2] && board[0][0] == '#' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][2] && board[1][1] == '#' && board[0][0] == ' ') { board[0][0] = '#'; return 1; } //第二条 if (board[0][2] == board[1][1] && board[0][2] == '#' && board[2][0] == ' ') { board[2][0] = '#'; return 1; } if (board[0][2] == board[2][0] && board[0][2] == '#' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][0] && board[1][1] == '#' && board[0][2] == ' ') { board[0][2] = '#'; return 1; } //如果上面都没返回,说明不符合赢的条件,返回0 return 0; } }
(2)、对玩家进行拦截(CheckPlayer)
//电脑检查玩家是否会赢(逻辑和CheckComputer完全相同) //约定成功拦截返回1 //无需拦截或者拦截不了返回0 int CheckPlayer(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; //判断每一行是否有两个相连的棋子,如果有,且第三个棋格为空,则拦截 for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][0] == '*' && board[i][2] == ' ') { board[i][2] = '#'; return 1; //成功拦截,返回1 } if (board[i][0] == board[i][2] && board[i][0] == '*' && board[i][1] == ' ') { board[i][1] = '#'; return 1; } if (board[i][1] == board[i][2] && board[i][1] == '*' && board[i][0] == ' ') { board[i][0] = '#'; return 1; } } //判断每一列是否有两个相连的棋子,如果有,且第三个棋格为空,则拦截 for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[0][j] == '*' && board[2][j] == ' ') { board[2][j] = '#'; return 1; } if (board[0][j] == board[2][j] && board[0][j] == '*' && board[1][j] == ' ') { board[1][j] = '#'; return 1; }if (board[1][j] == board[2][j] && board[1][j] == '*' && board[0][j] == ' ') { board[0][j] = '#'; return 1; } } //判断两条对角线是否有两个相连的棋子,如果有,且第三个棋格为空,则拦截 { //第一条 if (board[0][0] == board[1][1] && board[0][0] == '*' && board[2][2] == ' ') { board[2][2] = '#'; return 1; } if (board[0][0] == board[2][2] && board[0][0] == '*' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][2] && board[1][1] == '*' && board[0][0] == ' ') { board[0][0] = '#'; return 1; } //第二条 if (board[0][2] == board[1][1] && board[0][2] == '*' && board[2][0] == ' ') { board[2][0] = '#'; return 1; } if (board[0][2] == board[2][0] && board[0][2] == '*' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][0] && board[1][1] == '*' && board[0][2] == ' ') { board[0][2] = '#'; return 1; } //如果上面都没返回,说明不符合拦截的条件,返回0 return 0; } }
注意:我这里采用的判断方法是枚举,由于五子棋的枚举情况比较复杂,而我目前也没想到更好的算法来进行判断,所以这里我只写了三子棋的AI判断代码,如果有大佬有更好的算法或者判断思路,欢迎在评论区留言。
(3)、加入AI算法后game.c的改动
上面我们已经完成了CheckComputer和CheckPlayer这两个函数的定义,现在我们只需要把这两个函数实现放入到game.c中并且在在电脑下棋(ComputerMove)中调用这两个函数即可。
//电脑下棋 void ComputerMove(char board[ROW][COL], int row, int col) { printf("电脑下棋\n"); //定义两个标识符变量来接收两个判断函数的返回值 int flag1 = 0; int flag2 = 0; flag1 = CheckComputer(board, row, col); //如果flag1 == 0 时才进行flag2 的判断,避免当二者都为1时下两步棋(易错) if (flag1 == 0) { flag2 = CheckPlayer(board, row, col); } if (flag1 == 0 && flag2 == 0) //当CheckComputer和CheckPlayer都没落棋时,就随机下 { while (1) { //在主函数生成种子srand //随机生成范围内的坐标 int x = rand() % row; int y = rand() % col; //判断坐标是否被占用 if (board[x][y] == ' ') { board[x][y] = '#'; //假设电脑为#号 break; } } } }
注意:这里的AI算法只适用于三子棋,如果要使用的话需要把头文件中的ROW和COL改为3,同时不要忘记在头文件中对两个判断函数进行声明。
到此这篇关于C语言实现五子棋功能全解析的文章就介绍到这了,更多相关C语言五子棋内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
赞 (0)