赞
踩
通过基础的C语言知识,来实现三子棋的人工智能,让电脑可以实现自由落子、堵棋以及赢棋的功能。整体代码650行左右,适合刚接触编程的同学,在实现程序的同时,增加自己的编程知识,并且锻炼用逻辑思维思考的能力。
目录
游戏代码主要由三部分组成,text.c存放主函数,game.c存放游戏实现部分的函数,game.h声明在game.c中定义的函数。
text.c(菜单、主函数)
game.c(游戏内容)
game.h(声明游戏内容中所包含的函数)
为了避免有输入错误的情况,以及玩完一遍游戏还可以玩第二遍,这里整个游戏框架用do while循环,保证整个流程至少可以运行一次。
- #include"game.h"
-
- void menu()
- {
- printf("******************\n");
- printf("***** 1.paly *****\n");
- printf("***** 0.exit *****\n");
- printf("******************\n");
- }
-
- int main()
- {
- int input = 0;
- do
- {
- menu();
- printf("请选择(1/0):>");
- scanf("%d", &input);
- } while (input);
- return 0;
- }
随后,针对输入的值不同,来判定是进行游戏还是退出游戏,或是输入错误。这里可以用switch 选择语句完成该部分的判断。
- switch (input)
- {
- case 0:
- printf("退出游戏\n");
- break;
- case 1:
- printf("开始游戏\n");
- game();//游戏
- Sleep(500);//等500毫秒
- system("cls");//清空屏幕
- break;
- default:
- printf("输入错误,请重新输入\n");
- }
当我们完成一次游戏后,为了方便进行下一次玩,用system("cls")将我们的屏幕清空,但cls清空的速度非常快,这里用Sleep(500)让系统休眠500毫秒,方便我们看到游戏的结果。system和Sleep的使用均需要包含头文件<windows.h>。
当完成了这部分的内容,整个游戏的大致框架就完成了,将game()先打上注释,这里测试一下能否正常运行。
游戏的部分整体流程如下:
三子棋的棋盘一共三行三列,共有九个空可以下棋,这用一个三行三列的数组来存放每个空的数据。
为了代码的灵活性,这里不要把行和列的值写死,用define定义的常量来写,方便以后代码的更新。
- //game.h
-
- #define ROW 3
- #define COL 3
-
- //text.c
-
- #include"game.h"
-
- void game()
- {
- char board[ROW][COL] = { 0 };
- }
定义完数组后,为了更加贴合棋盘的样式,我们需要对数组进行初始化,将空格赋给每个元素。这里定义一个初始化棋盘的函数InitBoard,将数组,行和列的参数传给函数,这个函数的部分拿到game.c中完成,并且在game.h中声明一下。
- //text.c
-
- #include"game2.h"
-
- void game()
- {
- char board[ROW][COL] = { 0 };
- InitBoard(board, ROW, COL);//初始化
-
- }
-
-
- //game.h
-
- //初始化部分
- void InitBoard(char board[ROW][COL], int row, int col);
-
-
- //game.c
-
- void InitBoard(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++)
- {
- board[i][j] = ' ';//将空格赋给每个元素
- }
- }
- }
打印棋盘时注意也不要把棋盘写死,这里我们定义DisPaly_Board函数,用for循环来打印棋盘,打印“ | | | ”时,将“ |”设定为一组,遍历到最后一列时,只打印“ ”;同理,打印“---|---|---”时,将“---|”设定为一组进行打印,遍历到最后一列时,只打印“---”,遍历到最后一行时都不打印。
- //text.c
-
- void game()
- {
- char board[ROW][COL] = {0};
- //初始化
- InitBoard(board, ROW, COL);
- //打印棋盘
- DisPaly_Board(board, ROW, COL);
- }
-
-
- //game.h
-
- void DisPaly_Board(char board[ROW][COL], int row, int col);
-
-
- //game.c
-
- void DisPaly_Board(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++)
- {
- printf(" %c ", board[i][j]);
- if (j < col - 1)
- printf("|");
- }
- printf("\n");
-
- if (i < row - 1)
- {
- int j = 0;
- for (j = 0; j < col; j++)
- {
- printf("---");
- if (j < col - 1)
- {
- printf("|");
- }
- }
- printf("\n");
- }
- }
- }
这样写的好处是:当我们改变define定义的常量时,棋盘的大小也会随之改变。
例如将ROW和COL都改成10:
在玩家下棋这部分中,我们定义玩家通过输入坐标来进行下棋。需要注意的是:数组所定义的下标是从0开始的,而玩家认知的坐标是从1开始的(例如:玩家想输入第一行第一列的坐标是,输入的是(1,1),而不是(0,0)),这里需要将玩家输入的横、纵坐标减1,转换成数组的坐标。
在玩家输入完坐标后,我们还需要判断该坐标是否合法,如果玩家输入的坐标超限,或者该坐标已经下过棋了,那么需要重新输入坐标。
这里玩家下的棋,用 * 表示。定义PlayerMove用来实现玩家下棋的这部分。
- //text.c
-
- void game()
- {
- int ret = 0;
- char board[ROW][COL] = {0};
- //初始化
- InitBoard(board, ROW, COL);
- //打印棋盘
- DisPaly_Board(board, ROW, COL);
- while (1)
- {
- //玩家下棋
- PlayerMove(board, ROW, COL);
- //打印棋盘
- DisPaly_Board(board, ROW, COL);
- }
-
- }
-
-
- //game.h
-
- void PlayerMove(char board[ROW][COL], int row, int col);
-
-
- //game.c
-
- void PlayerMove(char board[ROW][COL], int row, int col)
- {
- int x = 0;
- int y = 0;
- while (1)
- {
- printf("玩家下棋:>\n");
- scanf("%d%d", &x, &y);
- if (x > 0 && x <= row && y > 0 && y <= col)
- {
- if (board[x - 1][y - 1] == ' ')
- {
- board[x - 1][y - 1] = '*';
- break;
- }
- }
- printf("坐标非法,请重新输入\n");
- }
- }
当玩家下完棋后,我们需要再将棋盘打印一下。并且下棋是一个有来有回的过程,这里用一个循环将整个下棋的过程包含起来,直到分出胜负或平局跳出循环。
电脑下棋分为三个部分:赢棋、堵棋、自由落子。
这里的优先级是赢棋>堵棋>自由落子,在写代码的时候要遵循:能赢则赢,不能赢则堵,以上情况都不符合的时候下新棋的原则。(自由落子不等于下废棋,不能彻底随机)
定义三个函数:
赢棋函数:TryWin(如果函数执行并落子,返回0;否则返回1)
堵棋函数:Cut(如果函数执行并落子,返回0;否则返回1)
自由落子函数:ComputerMove(构建整体框架,根据前两个函数的返回值来判断是否落子)
需要注意的是,在下新棋的时候,为了不让该棋变成废棋,需要判定电脑落子附近的8个单元格内至少有一个棋子是玩家的落子。
具体方法如下:先让电脑随机生成一个坐标,然后判定该坐标是否同时满足条件1和条件2,如果满足就落子,如果不满足则重新生成新的坐标,所以这里需要用到while循环,根据优先度的排序,while循环的表达式则是Cut函数的返回值。
条件1:该位置是空地。
条件2:(x,y)相邻的8个元素中,至少有一个格子有玩家的落子。(条件2是为了防止电脑随机下棋下到角落里,该棋便成了废棋)
ComputerMove代码如下:
- //game.h
- //电脑下棋
- void ComputerMove(char board[ROW][COL], int row, int col);
-
- //电脑尝试三连
- int TryWin(char board[ROW][COL], int row, int col);
-
- //堵棋
- int Cut(char board[ROW][COL], int row, int col);
-
-
- //text.c
- srand((unsigned int)time(NULL));//使用rand()需要在主函数中用srand修饰,提供随机标准。
-
-
- //game.c
- //电脑下棋
- void ComputerMove(char board[ROW][COL], int row, int col)
- {
- printf("电脑下棋:>\n");
- if (TryWin(board, ROW, COL)==1)//先尝试三连
- {
- while (Cut(board, ROW, COL))//不能三连尝试堵棋
- {
- int x = rand() % row;
- int y = rand() % col;
- //控制范围,不要下的太偏了,新棋下的位置要在玩家落子的位置附近,以免变成废棋
- if (board[x][y] == ' ' && (board[x - 1][y - 1] == '*' || board[x - 1][y] == '*' || board[x - 1][y - 1] == '*' || board[x][y - 1] == '*' || board[x][y + 1] == '*' || board[x + 1][y - 1] == '*' || board[x + 1][y] == '*' || board[x + 1][y + 1] == '*'))
- {
- board[x][y] = '#';
- break;
- }
- }
- }
-
- }
堵棋的优先度在下新棋之上,在赢棋的优先度之下。
堵棋共有四种堵法:横着堵、竖着堵、左斜堵、右斜堵。
将堵棋的大致框架写出,并且为了代码的适用性,这里的代码也不要写死,既要适用三行三列的棋盘,也要同样适用于更大的棋盘。(五行五列、十行十列等等)
当棋盘中不需要堵棋时,返回1;而只要当其中一种情况成立,电脑便落子堵棋,返回0。
堵棋框架:
- //game.c
- //堵棋
-
- int Cut(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++)
- {
- //堵横着
-
- //堵竖着的
-
- //右斜(左上右下)
-
- //左斜(左下右上)
-
- }
- }
- return 1;
- }
横着堵:
横着堵有四种情况:XXO、OXX,XOX,以及OXXO(在大棋盘中)。
这里采用for循环遍历来寻找棋盘中是否存在以上四种情况,针对不同的情况,坐标的搜索范围也不一致,这里注意数组的访问不要超出限制。
- //堵横着
-
- if (j < col - 2)//XOX型
- {
- if (board[i][j] == board[i][j + 2] && board[i][j + 1] == ' ' && board[i][j] == '*')
- {
- board[i][j + 1] = '#';
- return 0;
- }
- }
- if (j < col - 1)//XXO或OXX型
- {
- if (board[i][j] == board[i][j + 1] && board[i][j] == '*')
- {
- if (j < col - 2 && board[i][j + 2] == ' ' && board[i][j - 1] == ' ')//两种情况都符合
- {
- int ch = rand() % 2;//两种情况都符合时,随机下
- switch (ch)
- {
- case 0:
- board[i][j + 2] = '#';
- return 0;
- case 1:
- board[i][j - 1] = '#';
- return 0;
- }
- }
- //只符合其中一种情况
- if (j < col - 2 && board[i][j + 2] == ' ')//XXO
- {
- board[i][j + 2] = '#';
- return 0;
- }
- if (board[i][j - 1] == ' ')//OXX
- {
- board[i][j - 1] = '#';
- return 0;
- }
- }
- }
竖着堵:
竖着堵与横着堵的情况一致,也是四种情况,将横纵坐标的要求对调一下即可。
- //堵竖着的
- //X
- //0
- //X型
- if (i < row - 2)
- {
- if (board[i][j] == board[i + 2][j] && board[i + 1][j] == ' ' && board[i][j] == '#')
- {
- board[i + 1][j] = '#';
- return 0;
- }
- }
- //X O
- //X X
- //O型 或 X型
- if (i < row - 1)
- {
- if (board[i][j] == board[i + 1][j] && board[i][j] == '#')
- {
- if (i < row - 2 && board[i + 2][j] == ' ' && board[i - 1][j] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j] = '#';
- return 0;
-
- case 1:
- board[i - 1][j] = '#';
- return 0;
-
- }
- }
- if (i < row - 2 && board[i + 2][j] == ' ')//只有下方可以堵
- {
- board[i + 2][j] = '#';
- return 0;
- }
- if (board[i - 1][j] == ' ')//只有上方可以堵
- {
- board[i - 1][j] = '#';
- return 0;
- }
- }
- }
左斜堵:
左斜堵的情况也是四种,但是需要注意的是,这里符合条件的横纵坐标范围与右斜有很大区别。
- //堵斜着(左下右上)
- // X
- // 0
- //X 型
- if (i < row - 2 && j > 1)
- {
- if (board[i][j] == board[i + 2][j - 2] && board[i + 1][j - 1] == ' ' && board[i][j] == '#')
- {
- board[i + 1][j - 1] = '#';
- return 0;
- }
- }
- // X O
- // X X
- //O 型 或 X 型
- if (i < row - 1 && j > 0)
- {
- if (board[i][j] == board[i + 1][j - 1] && board[i][j] == '#')
- {
- if (i < row - 2 && j > 1 && board[i + 2][j - 2] == ' ' && board[i - 1][j + 1] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j - 2] = '#';
- return 0;
- case 1:
- board[i - 1][j + 1] = '#';
- return 0;
- }
- }
- if (i < row - 2 && j > 1 && board[i + 2][j - 2] == ' ')//只有下方可以堵
- {
- board[i + 2][j - 2] = '#';
- return 0;
- }
- if (board[i - 1][j + 1] == ' ')//只有上方可以堵
- {
- board[i - 1][j + 1] = '#';
- return 0;
- }
- }
- }
右斜堵:
- //堵斜着(左上右下)
-
- //X
- // 0
- // X型
- if (i < row - 2 && j < col - 2)
- {
- if (board[i][j] == board[i + 2][j + 2] && board[i + 1][j + 1] == ' ' && board[i][j] == '#')
- {
- board[i + 1][j + 1] = '#';
- return 0;
- }
- }
- // X O
- // X X
- // O型 或 X型
- if (i < row - 1 && j < col - 1)
- {
- if (board[i][j] == board[i + 1][j + 1] && board[i][j] == '#')
- {
- if (i < row - 2 && j < col - 2 && board[i + 2][j + 2] == ' ' && board[i - 1][j - 1] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j + 2] = '#';
- return 0;
- case 1:
- board[i - 1][j - 1] = '#';
- return 0;
- }
- }
- if (i < row - 2 && j < col - 2 && board[i + 2][j + 2] == ' ')//只有下方可以堵
- {
- board[i + 2][j + 2] = '#';
- return 0;
- }
-
- if (board[i - 1][j - 1] == ' ')//只有上方可以堵
- {
- board[i - 1][j - 1] = '#';
- return 0;
- }
- }
- }
当我们把上述共十六种的情况都罗列出来后,电脑便会逢棋必堵,并且堵棋函数中所运用到的知识和方法都是C语言初阶的内容,并没有很高深的内容。
为了让我们的电脑更进一步,可以抓住玩家露出的破绽,我们再赋予电脑一个赢棋的函数。
而赢棋的优先级也是最高的,毕竟如果游戏都赢了,那就没有堵棋和下棋的事儿了。
赢棋的函数其实和堵棋的函数十分类似,可以说是99.9%都是一致的。
想象一下:在判定是否需要堵棋时,我们其实判定的是玩家是否会赢,如果出现玩家赢的可能,那么我们就要执行堵棋。我们只需要改变一下条件,改成判定电脑是否会赢,如果出现电脑赢的可能,那么就执行赢棋。
例如:
- //堵棋
-
- if (j < col - 2)//XOX型
- {
- if (board[i][j] == board[i][j + 2] && board[i][j + 1] == ' ' && board[i][j] == '*')
- {
- board[i][j + 1] = '#';
- return 0;
- }
- }
-
-
- //赢棋
- if (j < col - 2)//XOX型
- {
- if (board[i][j] == board[i][j + 2] && board[i][j + 1] == ' ' && board[i][j] == '#')
- {
- board[i][j + 1] = '#';
- return 0;
- }
- }
将堵棋的代码完全拷贝下来,并将表达式中的*改成#即可。
三子棋的输赢判定方式很简单,横、竖、斜三棋相连,为了不把代码写死,这里将分别对横、竖、斜(左下右上)、斜(右下左上)这四种进行分别判定。
将框架写好,随后在for循环的内部加入判定部分即可。
- //game.c
-
- int Judgment(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 (i < row && j < col - 2)//三子棋,所以靠边界的两行没必要判断
- {
- if (board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2] && board[i][j] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
竖着三个相等且不等于空格,将第一个元素返回。
- if (i < row - 2 && j < col)
- {
- if (board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
斜三连的情况稍微复杂一点,正斜和反斜针对i和j的取值范围不同,并且坐标的变换也不一样。
- if (i < row-2 && j > 1)
- {
- if (board[i][j] == board[i + 1][j - 1] && board[i + 1][j - 1] == board[i + 2][j - 2] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
- if (i < row-2 && j < col-2 )
- {
- if (board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
除了输赢,还会有平局的情况发生,判断平局的方法很简单,遍历一遍整个数组,当发现没有‘ ’时,即平局了。这里如果平局则返回q,没有平局则返回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;
- }
- }
- }
- return 1;
- }
-
-
- if (IsFull(board, row, col))
- {
- return 'q';
- }
- return 'c';
当完成了所有代码后,这里我们用5×5的棋盘可以更好的测试一下。
当玩家先手下棋后,电脑在堵截玩家这方面还是咬的很死的,现在电脑有一个三连的机会,我们卖一个破绽,看看电脑能否抓住。
电脑不负众望抓住了机会,踏上了人工智能面向世界的第一步。我们这里设定的是玩家先手,玩家的优势稍微大一点。
如果想要设置电脑先手的话,还需要对电脑下棋部分的代码再加一点东西:即当整个棋盘是空的时候,电脑在中心区域随机下棋。(我们之前的代码一直是电脑负责对玩家的落子进行围追堵截,没有先手的情况)
其实整个代码还是有很大的优化空间的:
1、受到三子棋的限制,在3×3的棋盘太过无趣,但当三子棋在大棋盘中进行游玩时,先手一方必赢。
这里可以将三子棋改成五子棋,本质框架不变,但是需要堵棋的情况会比三子棋要多出一些。
2、电脑在堵棋时,如果是10×10的大棋盘,那么会出现可以两头堵的情况,在最初设定时,我们设置的是两头随机堵,但是现实中这样往往会错失良机。电脑的下棋也是如此,我们只是设置了一个随机坐标,保证在玩家落子的附近,但不能保证该坐标就是最优的选择。那么如何改进呢?
我们可以对整个棋盘设置权重P
当该坐标周围一圈附近8个元素中每多一颗电脑的棋子,P+10;
附近8个元素中每多一颗玩家的棋子,P+5;
该坐标周围第二圈附近16个元素中每多一颗电脑的棋子,P+5;
该坐标周围第二圈附近16个元素中每多一颗玩家的棋子,P+2。
以此类推,离坐标越远权重越低,每次电脑下棋前,遍历一遍棋盘,选择权重最高的方法下,如果有权重相同的情况,再选择周围空位最多的坐标。
除以上两点之外,其实还有更多细节之处可以优化,有兴趣的小伙伴可以自己尝试一下。
这里为了方便大家的测试与改进,我把全部的代码放在下面。如果有不足之处,还请大家多多指点。
- //三子棋
- #include"game.h"
-
-
-
- void menu()
- {
- printf("******************\n");
- printf("***** 1.paly *****\n");
- printf("***** 0.exit *****\n");
- printf("******************\n");
- }
-
- void game()
- {
- int ret = 0;
- char board[ROW][COL] = {0};
- //初始化
- InitBoard(board, ROW, COL);
- //打印棋盘
- DisPaly_Board(board, ROW, COL);
- while (1)
- {
- //玩家下棋
- PlayerMove(board, ROW, COL);
- //打印棋盘
- DisPaly_Board(board, ROW, COL);
- //判断输赢
- ret = Judgment(board, ROW, COL);
- if (ret != 'c')
- {
- break;
- }
- //电脑下棋
- ComputerMove(board, ROW, COL);
- //打印棋盘
- DisPaly_Board(board, ROW, COL);
- //判断输赢
- ret = Judgment(board, ROW, COL);
- if (ret != 'c')
- {
- break;
- }
- }
- if (ret == '*')
- {
- printf("玩家赢了\n");
- }
- else if (ret == '#')
- {
- printf("电脑赢了\n");
- }
- else
- printf("平局\n");
- }
-
-
- int main()
- {
- int input = 0;
- srand((unsigned int)time(NULL));
- do
- {
- menu();
- printf("请选择(1/0):>");
- scanf("%d", &input);
- switch (input)
- {
- case 0:
- printf("退出游戏\n");
- break;
- case 1:
- printf("开始游戏\n");
- game();//游戏
- Sleep(500);
- system("cls");//清空屏幕
- break;
- default:
- printf("输入错误,请重新输入\n");
- }
- } while (input);
- return 0;
- }
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
- #include<windows.h>
-
-
- #define ROW 5//在测试的时候,建议将行和列设置稍微大一点
- #define COL 5
-
- //初始化
- void InitBoard(char board[ROW][COL], int row,int col);
-
- //打印棋盘
- void DisPaly_Board(char board[ROW][COL], int row, int col);
-
- //玩家下棋
- void PlayerMove(char board[ROW][COL], int row, int col);
-
- //电脑尝试三连
- int TryWin(char board[ROW][COL], int row, int col);
-
- //堵棋
- int Cut(char board[ROW][COL], int row, int col);
-
- //电脑下棋
- void ComputerMove(char board[ROW][COL], int row, int col);
-
- //判断输赢
- int Judgment(char board[ROW][COL], int row, int col);
- #include"game.h"
-
-
- //初始化
- void InitBoard(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++)
- {
- board[i][j] = ' ';
- }
- }
- }
-
- //打印棋盘
- void DisPaly_Board(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++)
- {
- printf(" %c ", board[i][j]);
- if (j < col - 1)
- printf("|");
- }
- printf("\n");
-
- if (i < row - 1)
- {
- int j = 0;
- for (j = 0; j < col; j++)
- {
- printf("---");
- if (j < col - 1)
- {
- printf("|");
- }
- }
- printf("\n");
- }
- }
- }
-
- //玩家下棋
- void PlayerMove(char board[ROW][COL], int row, int col)
- {
- int x = 0;
- int y = 0;
- while (1)
- {
- printf("玩家下棋:>\n");
- scanf("%d%d", &x, &y);
- if (x > 0 && x <= row && y > 0 && y <= col)
- {
- if (board[x - 1][y - 1] == ' ')
- {
- board[x - 1][y - 1] = '*';
- break;
- }
- }
- printf("坐标非法,请重新输入\n");
- }
- }
-
-
- //电脑下棋(堵棋)
- int Cut(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 (j < col - 2)//XOX型
- {
- if (board[i][j] == board[i][j + 2] && board[i][j + 1] == ' ' && board[i][j] == '*')
- {
- board[i][j + 1] = '#';
- return 0;
- }
- }
- if (j < col - 1)//XXO或OXX型
- {
- if (board[i][j] == board[i][j + 1] && board[i][j] == '*')
- {
- if (j < col - 2 && board[i][j + 2] == ' ' && board[i][j - 1] == ' ')//两种情况都符合
- {
- int ch = rand() % 2;//两种情况都符合时,随机下
- switch (ch)
- {
- case 0:
- board[i][j + 2] = '#';
- return 0;
- case 1:
- board[i][j - 1] = '#';
- return 0;
- }
- }
- //只符合其中一种情况
- if (j < col - 2 && board[i][j + 2] == ' ')//XXO
- {
- board[i][j + 2] = '#';
- return 0;
- }
- if (board[i][j - 1] == ' ')//OXX
- {
- board[i][j - 1] = '#';
- return 0;
- }
- }
- }
-
- //堵竖着的
- //X
- //0
- //X型
- if (i < row - 2)
- {
- if (board[i][j] == board[i + 2][j] && board[i + 1][j] == ' ' && board[i][j] == '*')
- {
- board[i + 1][j] = '#';
- return 0;
- }
- }
- //X O
- //X X
- //O型 或 X型
- if (i < row - 1)
- {
- if (board[i][j] == board[i + 1][j] && board[i][j] == '*')
- {
- if (i < row - 2 && board[i + 2][j] == ' ' && board[i - 1][j] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j] = '#';
- return 0;
-
- case 1:
- board[i - 1][j] = '#';
- return 0;
- }
- }
- if (i < row - 2 && board[i + 2][j] == ' ')//只有下方可以堵
- {
- board[i + 2][j] = '#';
- return 0;
- }
- if (board[i - 1][j] == ' ')//只有上方可以堵
- {
- board[i - 1][j] = '#';
- return 0;
- }
- }
- }
- //堵斜着(左上右下)
- //X
- // 0
- // X型
- if (i < row - 2 && j < col - 2)
- {
- if (board[i][j] == board[i + 2][j + 2] && board[i + 1][j + 1] == ' ' && board[i][j] == '*')
- {
- board[i + 1][j + 1] = '#';
- return 0;
- }
- }
- // X O
- // X X
- // O型 或 X型
- if (i < row - 1 && j < col - 1)
- {
- if (board[i][j] == board[i + 1][j + 1] && board[i][j] == '*')
- {
- if (i < row - 2 && j < col - 2 && board[i + 2][j + 2] == ' ' && board[i - 1][j - 1] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j + 2] = '#';
- return 0;
- case 1:
- board[i - 1][j - 1] = '#';
- return 0;
- }
- }
- if (i < row - 2 && j < col - 2 && board[i + 2][j + 2] == ' ')//只有下方可以堵
- {
- board[i + 2][j + 2] = '#';
- return 0;
- }
-
- if (board[i - 1][j - 1] == ' ')//只有上方可以堵
- {
- board[i - 1][j - 1] = '#';
- return 0;
- }
- }
- }
-
- //堵斜着(左下右上)
- // X
- // 0
- //X 型
- if (i < row - 2 && j > 1)
- {
- if (board[i][j] == board[i + 2][j - 2] && board[i + 1][j - 1] == ' ' && board[i][j] == '*')
- {
- board[i + 1][j - 1] = '#';
- return 0;
- }
- }
- // X O
- // X X
- //O 型 或 X 型
- if (i < row - 1 && j > 0)
- {
- if (board[i][j] == board[i + 1][j - 1] && board[i][j] == '*')
- {
- if (i < row - 2 && j > 1 && board[i + 2][j - 2] == ' ' && board[i - 1][j + 1] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j - 2] = '#';
- return 0;
- case 1:
- board[i - 1][j + 1] = '#';
- return 0;
- }
- }
- if (i < row - 2 && j > 1 && board[i + 2][j - 2] == ' ')//只有下方可以堵
- {
- board[i + 2][j - 2] = '#';
- return 0;
- }
- if (board[i - 1][j + 1] == ' ')//只有上方可以堵
- {
- board[i - 1][j + 1] = '#';
- return 0;
- }
-
- }
- }
- }
- }
- return 1;
- }
-
- //电脑下棋(尝试三连)
- int TryWin(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 (j < col - 2)
- {
- if (board[i][j] == board[i][j + 2] && board[i][j + 1] == ' ' && board[i][j] == '#')//XOX型
- {
- board[i][j + 1] = '#';
- return 0;
- }
- }
- if (j < col - 1)//XXO或OXX型
- {
- if (board[i][j] == board[i][j + 1] && board[i][j] == '#')
- {
- if (j < col - 2 && board[i][j + 2] == ' ' && board[i][j - 1] == ' ')//两种情况都符合
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i][j + 2] = '#';
- return 0;
- case 1:
- board[i][j - 1] = '#';
- return 0;
- }
- }
- if (j < col - 2 && board[i][j + 2] == ' ')
- {
- board[i][j + 2] = '#';
- return 0;
- }
- if (board[i][j - 1] == ' ')
- {
- board[i][j - 1] = '#';
- return 0;
- }
- }
- }
-
- //堵竖着的
- //X
- //0
- //X型
- if (i < row - 2)
- {
- if (board[i][j] == board[i + 2][j] && board[i + 1][j] == ' ' && board[i][j] == '#')
- {
- board[i + 1][j] = '#';
- return 0;
- }
- }
- //X O
- //X X
- //O型 或 X型
- if (i < row - 1)
- {
- if (board[i][j] == board[i + 1][j] && board[i][j] == '#')
- {
- if (i < row - 2 && board[i + 2][j] == ' ' && board[i - 1][j] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j] = '#';
- return 0;
-
- case 1:
- board[i - 1][j] = '#';
- return 0;
-
- }
- }
- if (i < row - 2 && board[i + 2][j] == ' ')//只有下方可以堵
- {
- board[i + 2][j] = '#';
- return 0;
- }
- if (board[i - 1][j] == ' ')//只有上方可以堵
- {
- board[i - 1][j] = '#';
- return 0;
- }
- }
- }
- //堵斜着(左上右下)
- //X
- // 0
- // X型
- if (i < row - 2 && j < col - 2)
- {
- if (board[i][j] == board[i + 2][j + 2] && board[i + 1][j + 1] == ' ' && board[i][j] == '#')
- {
- board[i + 1][j + 1] = '#';
- return 0;
- }
- }
- // X O
- // X X
- // O型 或 X型
- if (i < row - 1 && j < col - 1)
- {
- if (board[i][j] == board[i + 1][j + 1] && board[i][j] == '#')
- {
- if (i < row - 2 && j < col - 2 && board[i + 2][j + 2] == ' ' && board[i - 1][j - 1] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j + 2] = '#';
- return 0;
- case 1:
- board[i - 1][j - 1] = '#';
- return 0;
- }
- }
- if (i < row - 2 && j < col - 2 && board[i + 2][j + 2] == ' ')//只有下方可以堵
- {
- board[i + 2][j + 2] = '#';
- return 0;
- }
-
- if (board[i - 1][j - 1] == ' ')//只有上方可以堵
- {
- board[i - 1][j - 1] = '#';
- return 0;
- }
- }
- }
-
- //堵斜着(左下右上)
- // X
- // 0
- //X 型
- if (i < row - 2 && j > 1)
- {
- if (board[i][j] == board[i + 2][j - 2] && board[i + 1][j - 1] == ' ' && board[i][j] == '#')
- {
- board[i + 1][j - 1] = '#';
- return 0;
- }
- }
- // X O
- // X X
- //O 型 或 X 型
- if (i < row - 1 && j > 0)
- {
- if (board[i][j] == board[i + 1][j - 1] && board[i][j] == '#')
- {
- if (i < row - 2 && j > 1 && board[i + 2][j - 2] == ' ' && board[i - 1][j + 1] == ' ')//符合两种情况随机堵
- {
- int ch = rand() % 2;
- switch (ch)
- {
- case 0:
- board[i + 2][j - 2] = '#';
- return 0;
- case 1:
- board[i - 1][j + 1] = '#';
- return 0;
- }
- }
- if (i < row - 2 && j > 1 && board[i + 2][j - 2] == ' ')//只有下方可以堵
- {
- board[i + 2][j - 2] = '#';
- return 0;
- }
- if (board[i - 1][j + 1] == ' ')//只有上方可以堵
- {
- board[i - 1][j + 1] = '#';
- return 0;
- }
- }
- }
- }
- }
- return 1;
- }
-
-
-
- //电脑下棋
- void ComputerMove(char board[ROW][COL], int row, int col)
- {
- printf("电脑下棋:>\n");
- if (TryWin(board, ROW, COL) == 1)//先尝试三连
- {
- while (Cut(board, ROW, COL))//不能三连尝试堵棋
- {
- int x = rand() % row;
- int y = rand() % col;
- //控制范围,不要下的太偏了
- if (board[x][y] == ' ' && (board[x - 1][y - 1] == '*' || board[x - 1][y] == '*' || board[x - 1][y - 1] == '*' || board[x][y - 1] == '*' || board[x][y + 1] == '*' || board[x + 1][y - 1] == '*' || board[x + 1][y] == '*' || board[x + 1][y + 1] == '*'))
- {
- board[x][y] = '#';
- break;
- }
-
- }
- }
- }
-
-
-
-
-
-
-
- //判断平局
- 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;
- }
- }
- }
- return 1;
- }
-
-
- //判断输赢
- int Judgment(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 (i < row && j < col - 2)//三子棋,所以靠边界的两行没必要判断
- {
- if (board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2] && board[i][j] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
- //判断竖行是否相连
- if (i < row - 2 && j < col)
- {
- if (board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
- //判断斜行是否相连(左下右上)
- if (i < row - 2 && j > 1)
- {
- if (board[i][j] == board[i + 1][j - 1] && board[i + 1][j - 1] == board[i + 2][j - 2] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
- //判断斜行是否相连(右下左上)
- if (i < row - 2 && j < col - 2)
- {
- if (board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2] && board[i][j] != ' ')
- {
- return board[i][j];
- }
- }
- }
- }
- if (IsFull(board, row, col))//判断平局
- {
- return 'q';
- }
- return 'c';
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。