N子棋是一种经典的棋类游戏,它在计算机科学领域有着广泛的应用。本文将介绍如何使用C语言编写N子棋游戏程序,并详细解析使用极小化极大算法实现AI对战的方法。
N子棋游戏的规则已经在前文提到,我们需要先定义一个设定大小的棋盘数组作为游戏的核心数据结构。使用C语言的二维数组可以很方便地表示棋盘状态。玩家和AI轮流落子,通过输入坐标来在空位上下子,并判断是否获胜。
极小化极大算法(Minimax Algorithm)是一种常用于博弈游戏的决策算法,在N子棋中也可以有效地应用。该算法通过递归搜索所有可能的落子情况,并给每个局面评分,以找到最优的走法。
为了给每个局面评分,我们需要定义一个评估函数。评估函数的设计需要考虑棋子的布局、攻防情况等因素,以尽可能准确地评估当前局面的优劣。
通过递归搜索所有可能的落子情况,每一步轮到AI时,它会选择对自己最有利的走法;每一步轮到玩家时,它会选择对AI最不利的走法。通过不断深入搜索并更新评分,AI可以找到最优的走法。
由于N子棋的搜索空间很大,为了减少搜索时间,我们可以使用剪枝技术,如Alpha-Beta剪枝。这种剪枝算法可以排除一些明显不会被选择的走法,从而加速搜索过程。
链接:https://live.csdn.net/v/314862?spm=1001.2014.3001.5501
AI三子棋
链接:https://live.csdn.net/v/314866?spm=1001.2014.3001.5501
AI 四子棋
链接:https://live.csdn.net/v/314872?spm=1001.2014.3001.5501
AI 五子棋
链接:https://live.csdn.net/v/314891?spm=1001.2014.3001.5501
AI 六子棋,我竟然被自己写的AI打败了
//game.h
#pragma once
#include
#include
#include
#include
//定义棋盘大小(可更改)
#define board_size 15
// 定义玩家和AI的棋子类型
#define PLAYER 'X'
#define AI 'O'
// 定义符号
#define EMPTY ' '
#define NOFULL '-'
#define FULL '*'
//初始化棋盘
void init_board(char board[board_size][board_size]);
//打印棋盘
void display_board(char board[board_size][board_size]);
//判断位置是否为空
int isPositionEmpty(char board[board_size][board_size], int x, int y);
//玩家下棋
void player_move(char board[board_size][board_size]);
// 判断当前位置是否为有效的落子点
bool isValidMove(int x, int y);
// 判断N子棋游戏加权连子数,以及棋型并给予相应的得分
char check_win(char board[board_size][board_size]);
// 判断N子棋游戏连子数
int evaluate_count(char board[board_size][board_size]);
//评估当前局面的得分
int evaluate(char board[board_size][board_size], int i, int j);
// 极小化极大算法
int minimax(char board[board_size][board_size], int depth, int isMaximizingPlayer, int i, int j);
// AI 下棋
void ai_move(char board[board_size][board_size]);
这段代码是一个用于判断五子棋游戏是否结束并返回胜利方的函数 check_win。函数接受一个二维字符数组 board 作为参数,表示棋盘,并返回一个字符表示游戏结果。
函数首先定义了四个方向:水平、垂直、左上到右下、右上到左下。然后通过两层循环遍历棋盘上的每个位置。对于每个位置,如果没有棋子,则跳过;否则,获取当前位置的颜色(玩家或AI)。
接下来,使用四个方向进行遍历并统计连续相同颜色的棋子数。遍历过程中,通过增量 dx 和 dy 来改变当前位置的坐标,不断在当前方向上移动。使用 isValidMove 函数判断新的位置是否合法(在棋盘范围内),并继续判断该位置的棋子颜色是否与当前颜色相同。如果是相同的颜色,则将计数器 count 自增,表示连续相同颜色的棋子数增加了一个。
如果任何一个方向上的连子数达到胜利条件(WinNum,即五子棋规则中连成一排的棋子数),则返回胜利方的颜色。
如果棋盘上没有空位置,表示平局,返回平局标志 FULL。
//game.c
// 判断N子棋游戏是否结束并返回胜利方
char check_win(char board[board_size][board_size])
{
// 定义四个方向:水平、垂直、左上到右下、右上到左下
int directions[4][2] = { {1, 0}, {0, 1}, {1, 1}, {1, -1} };
int n = board_size;// 棋盘大小
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == EMPTY) {
continue; // 当前位置没有棋子,跳过
}
char player = board[i][j];
for (int k = 0; k < 4; k++) { // 遍历四个方向
int dx = directions[k][0]; // 方向的 x 坐标增量
int dy = directions[k][1]; // 方向的 y 坐标增量
int count = 1; // 当前位置已有棋子,计数从1开始
int x = i + dx;
int y = j + dy;
while (isValidMove(x, y) && board[x][y] == player) { // 沿当前方向连续相同颜色的棋子数
count++;
x += dx;
y += dy;
}
if (count >= WinNum) {
return player; // 有一方获胜,返回胜利方
}
}
}
}
// 判断棋盘是否已满,即平局
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == EMPTY) {
return NOFULL; // 棋盘还未满
}
}
}
return FULL; // 棋盘已满平局
}
首先,判断游戏是否胜利的函数 check_win 使用了一个二维数组 board 来表示棋盘,其中 board_size 是棋盘的大小。函数通过遍历棋盘上的每个位置,并检查每个位置是否有棋子,如果当前位置没有棋子则跳过。如果当前位置有棋子,则使用四个固定方向(水平、垂直、左上到右下、右上到左下)进行遍历,统计连续相同颜色的棋子数。如果有任何一个方向上的连子数达到胜利条件(WinNum,即五子棋规则中连成一排的棋子数),则返回胜利方。如果棋盘上没有空位置,表示平局,返回平局标志。
其次,计算加权连子数以及棋型得分的函数 evaluate_count 也使用了一个二维数组 board 来表示棋盘。函数遍历棋盘上的每个位置,并检查每个位置是否有棋子,如果没有棋子则跳过。如果当前位置有棋子,则使用四个固定方向进行遍历,统计连续相同颜色的棋子数。根据当前棋子的颜色和连子数,给出相应的得分。AI得分的计算基于连子数的平方,而玩家得分则是减去连子数的平方。同时,根据不同的棋型(如活五、活四、活三等),给予特定的得分。
这两个函数的实现细节还依赖了其他辅助函数,如判断当前位置是否合法的 isValidMove 函数、判断目标位置是否为空的 isPositionEmpty 函数等(详细可见完整代码部分)。
//game.c
// 判断N子棋游戏是否结束并返回胜利方
char check_win(char board[board_size][board_size])
{
// 定义四个方向:水平、垂直、左上到右下、右上到左下
int directions[4][2] = { {1, 0}, {0, 1}, {1, 1}, {1, -1} };
int n = board_size;// 棋盘大小
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == EMPTY) {
continue; // 当前位置没有棋子,跳过
}
char player = board[i][j];
for (int k = 0; k < 4; k++) { // 遍历四个方向
int dx = directions[k][0]; // 方向的 x 坐标增量
int dy = directions[k][1]; // 方向的 y 坐标增量
int count = 1; // 当前位置已有棋子,计数从1开始
int x = i + dx;
int y = j + dy;
while (isValidMove(x, y) && board[x][y] == player) { // 沿当前方向连续相同颜色的棋子数
count++;
x += dx;
y += dy;
}
if (count >= WinNum) {
return player; // 有一方获胜,返回胜利方
}
}
}
}
// 判断棋盘是否已满,即平局
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == EMPTY) {
return NOFULL; // 棋盘还未满
}
}
}
return FULL; // 棋盘已满平局
}
// 判断N子棋游戏加权连子数,以及棋型并给予相应的得分
int evaluate_count(char board[board_size][board_size])
{
int directions[4][2] = { {1, 0}, {0, 1}, {1, 1}, {1, -1} };
// 定义四个方向的增量数组,分别表示横向、纵向、左上到右下斜向、右上到左下斜向
int n = board_size;
int count;
int sorce = 0; // 初始化计数器和得分
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (board[i][j] == EMPTY)
{
continue; // 当前位置没有棋子,跳过
}
char player = board[i][j];
for (int k = 0; k < 4; k++)
{
int dx = directions[k][0];
int dy = directions[k][1];
count = 1; // 当前位置已有棋子,计数从1开始
int x = i + dx;
int y = j + dy;
while (isValidMove(x, y) && board[x][y] == player)
{
count++;
x += dx;
y += dy;
} // 在当前方向上统计连续的棋子个数,直到边界或者不是同一种棋子
if (player == AI)
{
sorce += 20 * (int)pow(count, 2);// AI连成其他长度的连子,基于平方的得分增加
}
if (player == PLAYER)
{
sorce -= 30 * (int)pow(count, 2); // 玩家连成其他长度的连子,基于平方的得分减少
}
// 判断棋型并给予相应的得分
if (count == WinNum)
{
if (player == AI)
sorce += 1000; // “活五”,AI得分加1000
else if (player == PLAYER)
sorce -= 1500; // “活五”,玩家得分减1500
}
else if (count == WinNum - 1)
{
if ((player == AI && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, i - dx, j - dy)) ||
(player == PLAYER && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, i - dx, j - dy)))
{
if (player == AI)
sorce += 800; // “活四”,AI得分加800
else if (player == PLAYER)
sorce -= 1200; // “活四”,玩家得分减1200
}
}
else if (count == WinNum - 2)
{
if ((player == AI && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, x - 2 * dx, y - 2 * dy) &&
isPositionEmpty(board, i - dx, j - dy) && isPositionEmpty(board, i + 2 * dx, j + 2 * dy)) ||
(player == PLAYER && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, x - 2 * dx, y - 2 * dy) &&
isPositionEmpty(board, i - dx, j - dy) && isPositionEmpty(board, i + 2 * dx, j + 2 * dy)))
{
if (player == AI)
sorce += 300; // “活三”,AI得分加300
else if (player == PLAYER)
sorce -= 400; // “活三”,玩家得分减400
}
}
}
}
}
return sorce;
}
这段代码是一个使用极小化极大算法评估当前局面得分的函数 minimax,该函数接受一个二维字符数组 board 作为参数,表示棋盘状态,以及当前搜索深度 depth、当前玩家是否为最大化玩家 isMaximizingPlayer 以及当前位置 (i, j)。
函数首先判断是否达到指定深度 或 游戏已经结束,如果是则返回当前局面的得分,调用 evaluate 函数计算基本得分。如果游戏未结束,则根据当前玩家是最大化玩家还是最小化玩家进行不同的处理。
如果是最大化玩家(即AI),函数会初始化最大得分为负无穷大,并通过两层循环遍历棋盘上的每个位置。对于每个空格的位置,假设AI在该位置下棋,然后递归调用 minimax 函数切换到最小化玩家,继续搜索子节点的得分。在递归结束后,将该位置恢复为空格,并更新最大得分 maxEval。
如果是最小化玩家(即玩家),函数会初始化最小得分为正无穷大,并通过两层循环遍历棋盘上的每个位置。对于每个空格的位置,假设玩家在该位置下棋,然后递归调用 minimax 函数切换到最大化玩家,继续搜索子节点的得分。在递归结束后,将该位置恢复为空格,并更新最小得分 minEval。
最终,如果当前玩家为最大化玩家,则返回最大得分 maxEval;如果当前玩家为最小化玩家,则返回最小得分 minEval。
需要注意的是,这段代码中使用了一个评估函数 evaluate,用于计算每个局面的得分。在 evaluate 函数中,首先调用 evaluate_count 函数计算基本得分,然后根据棋盘中心的距离对基本得分进行调整,离中心越远得分越低。最后返回调整后的得分。
//game.c
//评估当前局面的得分
int evaluate(char board[board_size][board_size], int i, int j)
{
int sorce = evaluate_count(board);// 先调用evaluate_count函数计算基本得分
sorce += (1000/sqrt((i + 1 - board_size/2) * (i + 1 - board_size/2) + (j + 1 - board_size/2) * (j + 1 - board_size/2))/ board_size);
// 根据棋盘中心的距离对基本得分进行调整,离中心越远,得分越低
return sorce;
}
// 极小化极大算法
int minimax(char board[board_size][board_size], int depth, int isMaximizingPlayer, int i, int j)
{
// 如果达到指定深度或游戏结束,则返回当前局面的得分
if (depth == 0 || check_win(board) != NOFULL)
{
return evaluate(board, i, j); // 如果游戏结束,则返回当前局面的得分
}
if (isMaximizingPlayer) // 最大化玩家(AI)
{
int maxEval = -1000000;// 初始化最大得分为负无穷大
int i, j;
for (i = 0; i < board_size; i++) {
for (j = 0; j < board_size; j++) {
if (board[i][j] == EMPTY) { // 检查该位置是否为空格
board[i][j] = AI; // 假设AI在该位置下棋
int eval = minimax(board, depth - 1, 0, i, j); // 递归搜索子节点,切换到最小化玩家
board[i][j] = EMPTY; // 恢复该位置为空格
if (eval > maxEval) {
maxEval = eval; // 更新最大得分
}
}
}
}
return maxEval;
}
else { // 最小化玩家(玩家)
int minEval = 1000000; // 初始化最小得分为正无穷大
int i, j;
for (i = 0; i < board_size; i++) {
for (j = 0; j < board_size; j++) {
if (board[i][j] == EMPTY) { // 检查该位置是否为空格
board[i][j] = PLAYER; // 假设玩家在该位置下棋
int eval = minimax(board, depth - 1, 1, i, j); // 递归搜索子节点,切换到最小化玩家
board[i][j] = EMPTY; // 恢复该位置为空格
if (eval < minEval) {
minEval = eval; // 更新最小得分
}
}
}
}
return minEval;
}
}
//game.h
#pragma once
#include
#include
#include
#include
//定义棋盘大小(可更改)
#define board_size 15
// 定义玩家和AI的棋子类型
#define PLAYER 'X'
#define AI 'O'
// 定义符号
#define EMPTY ' '
#define NOFULL '-'
#define FULL '*'
//初始化棋盘
void init_board(char board[board_size][board_size]);
//打印棋盘
void display_board(char board[board_size][board_size]);
//判断位置是否为空
int isPositionEmpty(char board[board_size][board_size], int x, int y);
//玩家下棋
void player_move(char board[board_size][board_size]);
// 判断当前位置是否为有效的落子点
bool isValidMove(int x, int y);
// 判断N子棋游戏加权连子数,以及棋型并给予相应的得分
char check_win(char board[board_size][board_size]);
// 判断N子棋游戏连子数
int evaluate_count(char board[board_size][board_size]);
//评估当前局面的得分
int evaluate(char board[board_size][board_size], int i, int j);
// 极小化极大算法
int minimax(char board[board_size][board_size], int depth, int isMaximizingPlayer, int i, int j);
// AI 下棋
void ai_move(char board[board_size][board_size]);
//game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
extern int WinNum;
//初始化棋盘
void init_board(char board[board_size][board_size])
{
int i = 0;
int j = 0;
for (i = 0; i < board_size; i++)
{
for (j = 0; j < board_size; j++)
{
board[i][j] = EMPTY;
}
}
}
//打印棋盘
void display_board(char board[board_size][board_size])
{
int i = 0;
int j = 0;
int tmp = 0;
printf(" ");
for (i = 1; i <= board_size; i++)
{
if (10 == i)
{
printf(" ");
}
printf(" %2d", i);
}
printf("\n");
for (i = 0; i <= 2 * board_size; i++)
{
if (i % 2 == 0)
{
for (j = 0; j <= board_size; j++)
{
if (j != 0)
{
printf("---|");
}
else
{
printf(" |");
}
}
}
else
{
for (j = 0; j <= board_size; j++)
{
if (j == 0)
{
printf("%2d|", i / 2 + 1);
}
else
{
tmp = i / 2;
printf(" %c |", board[tmp][j - 1]);
}
}
}
printf("\n");
}
}
// 判断位置是否为空
int isPositionEmpty(char board[board_size][board_size], int x, int y)
{
return board[x][y] == EMPTY;
}
//玩家下棋
void player_move(char board[board_size][board_size])
{
int x = 0;
int y = 0;
while (1)
{
scanf("%d%d", &y, &x);
//判断输入是否合法
if ((x >= 1 && x <= board_size) && (y >= 1 && y <= board_size))
{
if (isPositionEmpty(board, x - 1, y - 1))
{
board[x - 1][y - 1] = PLAYER;
break;
}
else
{
printf("该位置已有棋子, 请重新输入!\n");
}
}
else
{
printf("输入非法,请重新输入!\n");
}
}
}
// 判断当前位置是否为有效的落子点
bool isValidMove(int x, int y)
{
// 判断 (x, y) 是否在棋盘范围内,这里假设棋盘大小为board_size
if (x < 0 || x >= board_size || y < 0 || y >= board_size)
{
return false;
}
return true;
}
// 判断五N子棋游戏是否结束并返回胜利方
char check_win(char board[board_size][board_size])
{
// 定义四个方向:水平、垂直、左上到右下、右上到左下
int directions[4][2] = { {1, 0}, {0, 1}, {1, 1}, {1, -1} };
int n = board_size;// 棋盘大小
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == EMPTY) {
continue; // 当前位置没有棋子,跳过
}
char player = board[i][j];
for (int k = 0; k < 4; k++) { // 遍历四个方向
int dx = directions[k][0]; // 方向的 x 坐标增量
int dy = directions[k][1]; // 方向的 y 坐标增量
int count = 1; // 当前位置已有棋子,计数从1开始
int x = i + dx;
int y = j + dy;
while (isValidMove(x, y) && board[x][y] == player) { // 沿当前方向连续相同颜色的棋子数
count++;
x += dx;
y += dy;
}
if (count >= WinNum) {
return player; // 有一方获胜,返回胜利方
}
}
}
}
// 判断棋盘是否已满,即平局
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == EMPTY) {
return NOFULL; // 棋盘还未满
}
}
}
return FULL; // 棋盘已满平局
}
// 判断五N子棋游戏加权连子数,以及棋型并给予相应的得分
int evaluate_count(char board[board_size][board_size])
{
int directions[4][2] = { {1, 0}, {0, 1}, {1, 1}, {1, -1} };
// 定义四个方向的增量数组,分别表示横向、纵向、左上到右下斜向、右上到左下斜向
int n = board_size;
int count;
int sorce = 0; // 初始化计数器和得分
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (board[i][j] == EMPTY)
{
continue; // 当前位置没有棋子,跳过
}
char player = board[i][j];
for (int k = 0; k < 4; k++)
{
int dx = directions[k][0];
int dy = directions[k][1];
count = 1; // 当前位置已有棋子,计数从1开始
int x = i + dx;
int y = j + dy;
while (isValidMove(x, y) && board[x][y] == player)
{
count++;
x += dx;
y += dy;
} // 在当前方向上统计连续的棋子个数,直到边界或者不是同一种棋子
if (player == AI)
{
sorce += 20 * (int)pow(count, 2);// AI连成其他长度的连子,基于平方的得分增加
}
if (player == PLAYER)
{
sorce -= 30 * (int)pow(count, 2); // 玩家连成其他长度的连子,基于平方的得分减少
}
// 判断棋型并给予相应的得分
if (count == WinNum)
{
if (player == AI)
sorce += 1000; // “活五”,AI得分加1000
else if (player == PLAYER)
sorce -= 1500; // “活五”,玩家得分减1500
}
else if (count == WinNum - 1)
{
if ((player == AI && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, i - dx, j - dy)) ||
(player == PLAYER && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, i - dx, j - dy)))
{
if (player == AI)
sorce += 800; // “活四”,AI得分加800
else if (player == PLAYER)
sorce -= 1200; // “活四”,玩家得分减1200
}
}
else if (count == WinNum - 2)
{
if ((player == AI && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, x - 2 * dx, y - 2 * dy) &&
isPositionEmpty(board, i - dx, j - dy) && isPositionEmpty(board, i + 2 * dx, j + 2 * dy)) ||
(player == PLAYER && isPositionEmpty(board, x + dx, y + dy) && isPositionEmpty(board, x - 2 * dx, y - 2 * dy) &&
isPositionEmpty(board, i - dx, j - dy) && isPositionEmpty(board, i + 2 * dx, j + 2 * dy)))
{
if (player == AI)
sorce += 300; // “活三”,AI得分加300
else if (player == PLAYER)
sorce -= 400; // “活三”,玩家得分减400
}
}
}
}
}
return sorce;
}
//评估当前局面的得分
int evaluate(char board[board_size][board_size], int i, int j)
{
int sorce = evaluate_count(board);// 先调用evaluate_count函数计算基本得分
sorce += (1000/sqrt((i + 1 - board_size/2) * (i + 1 - board_size/2) + (j + 1 - board_size/2) * (j + 1 - board_size/2))/ board_size);
// 根据棋盘中心的距离对基本得分进行调整,离中心越远,得分越低
return sorce;
}
// 极小化极大算法
int minimax(char board[board_size][board_size], int depth, int isMaximizingPlayer, int i, int j)
{
// 如果达到指定深度或游戏结束,则返回当前局面的得分
if (depth == 0 || check_win(board) != NOFULL)
{
return evaluate(board, i, j); // 如果游戏结束,则返回当前局面的得分
}
if (isMaximizingPlayer) // 最大化玩家(AI)
{
int maxEval = -1000000;// 初始化最大得分为负无穷大
int i, j;
for (i = 0; i < board_size; i++) {
for (j = 0; j < board_size; j++) {
if (board[i][j] == EMPTY) { // 检查该位置是否为空格
board[i][j] = AI; // 假设AI在该位置下棋
int eval = minimax(board, depth - 1, 0, i, j); // 递归搜索子节点,切换到最小化玩家
board[i][j] = EMPTY; // 恢复该位置为空格
if (eval > maxEval) {
maxEval = eval; // 更新最大得分
}
}
}
}
return maxEval;
}
else { // 最小化玩家(玩家)
int minEval = 1000000; // 初始化最小得分为正无穷大
int i, j;
for (i = 0; i < board_size; i++) {
for (j = 0; j < board_size; j++) {
if (board[i][j] == EMPTY) { // 检查该位置是否为空格
board[i][j] = PLAYER; // 假设玩家在该位置下棋
int eval = minimax(board, depth - 1, 1, i, j); // 递归搜索子节点,切换到最小化玩家
board[i][j] = EMPTY; // 恢复该位置为空格
if (eval < minEval) {
minEval = eval; // 更新最小得分
}
}
}
}
return minEval;
}
}
// AI 下棋
void ai_move(char board[board_size][board_size])
{
int bestEval = -1000000;// 初始化最优得分为负无穷大
int bestRow = -1;// 初始化最优位置的行号
int bestCol = -1;// 初始化最优位置的列号
int depth = 0;//递归深度,深度越高耗时越长
// 根据棋盘大小确定递归深度
if (board_size < 4){
depth = 6;
}
else if (board_size < 5){
depth = 5;
}
else if (board_size < 7){
depth = 4;
}
else if (board_size < 9){
depth = 3;
}
else{
depth = 2;
}
int i, j;
for (i = 0; i < board_size; i++) {
for (j = 0; j < board_size; j++) {
if (board[i][j] == EMPTY) {
board[i][j] = AI; // 假设AI在该位置下棋
int eval = minimax(board, depth, 0, i, j); // 调用极小化极大算法计算得分
board[i][j] = EMPTY; // 恢复该位置为空格
if (eval > bestEval) {
bestEval = eval; // 更新最优得分
bestRow = i;// 更新最优位置的行号
bestCol = j;// 更新最优位置的列号
}
}
}
}
// 在最优位置下棋
board[bestRow][bestCol] = AI;
}
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//定义胜利条件
int WinNum = 0;
void menu()
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
printf("请选择:>");
}
bool display_win(char board[board_size][board_size])
{
if (check_win(board) == PLAYER)
{
display_board(board); // 最终显示棋盘
printf("玩家胜利!\n");
return true;
}
else if (check_win(board) == AI)
{
display_board(board); // 最终显示棋盘
printf("AI 胜利!\n");
return true;
}
else if (check_win(board) == FULL)
{
display_board(board); // 最终显示棋盘
printf("平局!\n");
return true;
}
return false;
}
void game(char board[board_size][board_size])
{
int is_player_turn = 0; // 玩家先手
printf("************************\n");
printf("****** 1.玩家先手 ******\n");
printf("****** 0.AI先手 *******\n");
printf("************************\n");
printf("请输入:>");
scanf("%d", &is_player_turn);
int turn = 0; // 回合数
init_board(board);
// 游戏循环
while (1)
{
turn++;
if (is_player_turn)
{
system("cls");// 清屏
printf("***** %d子棋游戏!*****\n", WinNum);
display_board(board); // 显示棋盘
printf("请玩家输入坐标下棋:>");
player_move(board); // 玩家下棋
if (display_win(board))
break;
system("cls");
display_board(board); // 显示棋盘
printf("AI正在下棋!\n");
ai_move(board); // AI 下棋
if (display_win(board))
break;
}
else
{
system("cls");// 清屏
printf("***** %d子棋游戏!*****\n", WinNum);
display_board(board); // 显示棋盘
printf("AI正在下棋!\n");
if (turn == 1)
{
board[board_size/2][board_size / 2] = AI;
}
else
{
ai_move(board); // AI 下棋
if (display_win(board))
break;
}
system("cls");// 清屏
printf("***** %d子棋游戏!*****\n", WinNum);
display_board(board); // 显示棋盘
printf("请玩家输入坐标下棋:>");
player_move(board); // 玩家下棋
if (display_win(board))
break;
}
}
printf("总共进行了 %d 回合。\n", turn);
}
int main()
{
while (1)
{
menu();
int input = 0;
scanf("%d", & input);
if (input)
{
while (1)
{
printf("请输入连胜棋子个数:>");
scanf("%d", &WinNum);
if (WinNum > board_size)
{
printf("输入的连胜棋子个数已超过棋盘大小,请重新输入,或者调整棋盘大小\n");
}
else
{
printf("**** %d子棋游戏开始 ****\n", WinNum);
break;
}
}
}
else
{
printf("已退出%d子棋游戏!\n", WinNum);
break;
}
char board[board_size][board_size] = { 0 }; //创建棋盘
game(board);
}
return 0;
}