本算法用于实现井字棋游戏电脑先手情况下的智能落子
关于井字棋的模块的搭建,分为棋盘的初始化、棋盘的展示、电脑落子、玩家落子、判断输/赢/平局/继续游戏这5个模块。
其中,我们着重介绍一下电脑落子的具体算法。本算法可以使得玩家永远赢不了棋局。(输/平局)
本算法的核心思想为 暴力枚举 和 迫使玩家进行指定位置的落子
注:
接下来,玩家落子一共有8个点可供选择,根据对称性,我们可以将其分分成5组。它们分别是:
以下对于每一类情况进行分组讨论,每组以其中一种情况进行举例
蓝色的数字表示落子的顺序
上图表示玩家第一手落于(1,2)的情况。此时,显然玩家必输。
同理,玩家落子于(2,1),显然必输。
上图表示玩家第一手落于(1,3)的情况。此时,显然玩家必输。
同理,玩家落子于(3,1),显然必输。
上图表示玩家第一手落于(2,3)的情况。此时,显然玩家必输。
同理,玩家落子于(3,2),显然必输。
上图表示玩家第一手落于(3,3)的情况。此时,显然玩家必输。
上图表示玩家第一手落于(2,2)的情况。此时,显然最终双方会握手言和。
以下展示电脑落子的代码
首先需定义棋盘大小
#define ROW 3
#define COL 3
首先创建一个3*3的二维数组
char board[3][3]={
0};
并将二维数组中每一个元素使用初始化模块初始化为’ ’ ,也就是空格,此处省略
我们另需封装一个CountNum函数用来统计棋盘中的9个格子已经有几个格子上落了子
int CountNum(char board[ROW][COL], int row, int col)
{
int count = 0;
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] != ' ')
count++;
}
}
return count;
}
以下代码为具体的电脑落子智能算法,其中参数board表示用来表示棋盘的二维数组的地址,row为ROW(3),col为COL(3)
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
if (CountNum(board, ROW, COL) == 0)
{
board[0][0] = '#';
}
if (CountNum(board, ROW, COL) == 2)
{
//5 types , 8 situations
// 1
if (board[0][1] == '*')
{
board[2][0] = '#';
}
// 2
if (board[1][0] == '*')
{
board[0][2] = '#';
}
// 3
if (board[0][2] == '*')
{
board[2][2] = '#';
}
// 4
if (board[2][0] == '*')
{
board[2][2] = '#';
}
// 5
if (board[2][1] == '*')
{
board[2][0] = '#';
}
// 6
if (board[1][2] == '*')
{
board[0][2] = '#';
}
// 7
if (board[1][1] == '*')
{
board[0][1] = '#';
}
// 8
if (board[2][2] == '*')
{
board[0][2] = '#';
}
}
if (CountNum(board, ROW, COL) == 4)
{
// 1@1
if (board[0][1] == '*' && board[2][0] == '#')
{
if (board[1][0] == '*')
{
board[2][2] = '#';
}
else
{
board[1][0] = '#';
}
}
// 2@1
if (board[1][0] == '*' && board[0][2] == '#')
{
if (board[0][1] == '*')
{
board[2][2] = '#';
}
else
{
board[0][1] = '#';
}
}
// 3@1
if (board[0][2] == '*' && board[2][2] == '#')
{
if (board[1][1] == '*')
{
board[2][0] = '#';
}
else
{
board[1][1] = '#';
}
}
// 4@1
if (board[2][0] == '*' && board[2][2] == '#')
{
if (board[1][1] == '*')
{
board[0][2] = '#';
}
else
{
board[1][1] = '#';
}
}
// 5@1
if (board[2][1] == '*' && board[2][0] == '#')
{
if (board[1][0] == '*')
{
board[0][2] = '#';
}
else
{
board[1][0] = '#';
}
}
// 6@1
if (board[1][2] == '*' && board[0][2] == '#')
{
if (board[0][1] == '*')
{
board[2][0] = '#';
}
else
{
board[0][1] = '#';
}
}
// 7@1
if (board[1][1] == '*' && board[0][1] == '#')
{
if (board[0][2] == '*')
{
board[2][0] = '#';
}
else
{
board[0][2] = '#';
}
}
// 8@1
if (board[2][2] == '*' && board[0][2] == '#')
{
if (board[0][1] == '*')
{
board[2][0] = '#';
}
else
{
board[0][1] = '#';
}
}
}
if (CountNum(board, ROW, COL) == 6)
{
// 1@1@1
if (board[0][0] == '#' && board[2][0] == '#' && board[2][2] == '#' && board[0][1] == '*' && board[1][0] == '*')
{
if (board[1][1] == ' ')
{
board[1][1] = '#';
}
else
{
board[1][1] = '#';
}
}
// 2@1@1
if (board[0][0] == '#' && board[0][2] == '#' && board[2][2] == '#' && board[0][1] == '*' && board[1][0] == '*')
{
if (board[1][1] == ' ')
{
board[1][1] = '#';
}
else
{
board[1][1] = '#';
}
}
// 3@1@1
if (board[0][0] == '#' && board[2][0] == '#' && board[2][2] == '#' && board[0][2] == '*' && board[1][1] == '*')
{
if (board[1][0] == ' ')
{
board[1][0] = '#';
}
else
{
board[2][1] = '#';
}
}
// 4@1@1
if (board[0][0] == '#' && board[0][2] == '#' && board[2][2] == '#' && board[2][0] == '*' && board[1][1] == '*')
{
if (board[0][1] == ' ')
{
board[0][1] = '#';
}
else
{
board[1][2] = '#';
}
}
// 5@1@1
if (board[0][0] == '#' && board[2][0] == '#' && board[0][2] == '#' && board[1][0] == '*' && board[2][1] == '*')
{
if (board[0][1] == ' ')
{
board[0][1] = '#';
}
else
{
board[1][1] = '#';
}
}
// 6@1@1
if (board[0][0] == '#' && board[0][2] == '#' && board[2][0] == '#' && board[0][1] == '*' && board[1][2] == '*')
{
if (board[1][0] == ' ')
{
board[1][0] = '#';
}
else
{
board[1][1] = '#';
}
}
// 7@1@1
if (board[0][0] == '#' && board[0][1] == '#' && board[2][0] == '#' && board[1][1] == '*' && board[0][2] == '*')
{
if (board[1][0] == '*')
{
board[1][2] = '#';
}
else
{
board[1][0] = '#';
}
}
// 8@1@1
if (board[0][0] == '#' && board[0][2] == '#' && board[2][0] == '#' && board[0][1] == '*' && board[2][2] == '*')
{
if (board[1][0] == ' ')
{
board[1][0] = '#';
}
else
{
board[1][1] = '#';
}
}
}
if (CountNum(board, ROW, COL) == 8)
{
//7@1@1@1
if (board[0][0] == '#' && board[0][1] == '#' && board[1][2] == '#' && board[2][0] == '#' && board[0][2] == '*' && board[1][0] == '*' && board[1][1] == '*')
{
if (board[2][1] == '*')
{
board[2][2] = '#';
}
else
{
board[2][1] = '#';
}
}
}
}
谢谢我自己能在大一下期中考前抽出半天来研究这样一个看似挺无聊的问题
谢谢小伙伴们能够看完这篇文章
附上GitHub完整代码
如果想要深入了解AI对弈,可以去看看极大极小算法(Minimax)
本人目前水平有限,如有错误,望指正
2021.4.24 姑苏阴雨绵绵