五子棋c语言代码 人机

99ANYc3cd6
预计阅读时长 56 分钟
位置: 首页 C语言 正文

环境准备

在运行代码之前,你需要安装 EasyX 图形库。

五子棋c语言代码 人机
(图片来源网络,侵删)
  • 如果你使用 Visual Studio:

    1. 下载 EasyX 安装包:http://www.easyx.cn/
    2. 运行安装程序,它会自动配置好 Visual Studio。
  • 如果你使用 Dev-C++ (较新的版本):

    1. 下载支持 EasyX 的 Dev-C++ 版本或手动配置。
    2. 在编译选项中加入 -lgraphics -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
  • 如果你使用 MinGW / GCC:

    1. 下载 EasyX 的 MinGW 版本。
    2. 将头文件和库文件路径正确配置到你的编译器中。
    3. 编译时链接必要的库:gcc -o gomoku gomoku.c -lgraphics -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32

C语言代码 (Gomoku.c)

#include <graphics.h>      // EasyX 图形库头文件
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>         // 用于 _kbhit() 和 _getch()
#include <time.h>          // 用于 srand() 和 time()
// --- 游戏常量定义 ---
#define GRID_SIZE 40      // 棋盘格子大小
#define BOARD_WIDTH 15    // 棋盘宽度 (格子数)
#define BOARD_HEIGHT 15   // 棋盘高度 (格子数)
#define MARGIN 50         // 棋盘边距
#define WINDOW_WIDTH (BOARD_WIDTH * GRID_SIZE + 2 * MARGIN)
#define WINDOW_HEIGHT (BOARD_HEIGHT * GRID_SIZE + 2 * MARGIN + 50) // 额外空间用于显示信息
// --- 棋子类型 ---
#define EMPTY 0
#define BLACK 1
#define WHITE 2
// --- 全局变量 ---
int board[BOARD_HEIGHT][BOARD_WIDTH]; // 棋盘数组
int currentPlayer = BLACK;           // 当前玩家 (黑棋先手)
int gameOver = 0;                   // 游戏是否结束
int aiThinking = 0;                 // AI是否正在思考
// --- 函数声明 ---
void initGame();
void drawBoard();
void drawPieces();
void drawGameInfo();
void handleClick(int x, int y);
int makeMove(int row, int col, int player);
int checkWin(int row, int col, int player);
void playerMove();
void aiMove();
int evaluatePosition(int player);
int minimax(int depth, int alpha, int beta, int maximizingPlayer);
void findBestMove(int *bestRow, int *bestCol);
// --- 主函数 ---
int main() {
    // 初始化图形窗口
    initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
    randomize(); // 初始化随机数种子
    // 初始化游戏
    initGame();
    // 游戏主循环
    while (1) {
        // 清空屏幕
        cleardevice();
        // 绘制游戏界面
        drawBoard();
        drawPieces();
        drawGameInfo();
        // 处理玩家输入
        if (!gameOver && currentPlayer == BLACK && !aiThinking) {
            MOUSEMSG msg;
            if (MouseHit()) {
                msg = GetMouseMsg();
                if (msg.uMsg == WM_LBUTTONDOWN) {
                    handleClick(msg.x, msg.y);
                }
            }
        }
        // AI回合
        if (!gameOver && currentPlayer == WHITE) {
            aiThinking = 1;
            aiMove();
            aiThinking = 0;
        }
        // 检查游戏是否结束 (通过按键)
        if (_kbhit()) {
            if (_getch() == 27) { // ESC键退出
                break;
            }
            if (_getch() == 'r' || _getch() == 'R') { // R键重新开始
                initGame();
            }
        }
        // 延迟,避免CPU占用过高
        Sleep(10);
    }
    // 关闭图形窗口
    closegraph();
    return 0;
}
// --- 函数实现 ---
/**
 * @brief 初始化游戏状态
 */
void initGame() {
    // 清空棋盘
    for (int i = 0; i < BOARD_HEIGHT; i++) {
        for (int j = 0; j < BOARD_WIDTH; j++) {
            board[i][j] = EMPTY;
        }
    }
    currentPlayer = BLACK;
    gameOver = 0;
    aiThinking = 0;
}
/**
 * @brief 绘制棋盘
 */
void drawBoard() {
    setbkcolor(LIGHTGRAY); // 设置背景色
    cleardevice();
    setlinecolor(BLACK);
    setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 1);
    // 绘制横线
    for (int i = 0; i < BOARD_HEIGHT; i++) {
        line(MARGIN, MARGIN + i * GRID_SIZE, MARGIN + (BOARD_WIDTH - 1) * GRID_SIZE, MARGIN + i * GRID_SIZE);
    }
    // 绘制竖线
    for (int i = 0; i < BOARD_WIDTH; i++) {
        line(MARGIN + i * GRID_SIZE, MARGIN, MARGIN + i * GRID_SIZE, MARGIN + (BOARD_HEIGHT - 1) * GRID_SIZE);
    }
    // 绘制天元和星位
    setfillcolor(BLACK);
    fillcircle(MARGIN + 7 * GRID_SIZE, MARGIN + 7 * GRID_SIZE, 5);
    fillcircle(MARGIN + 3 * GRID_SIZE, MARGIN + 3 * GRID_SIZE, 5);
    fillcircle(MARGIN + 11 * GRID_SIZE, MARGIN + 3 * GRID_SIZE, 5);
    fillcircle(MARGIN + 3 * GRID_SIZE, MARGIN + 11 * GRID_SIZE, 5);
    fillcircle(MARGIN + 11 * GRID_SIZE, MARGIN + 11 * GRID_SIZE, 5);
}
/**
 * @brief 绘制所有棋子
 */
void drawPieces() {
    for (int i = 0; i < BOARD_HEIGHT; i++) {
        for (int j = 0; j < BOARD_WIDTH; j++) {
            if (board[i][j] != EMPTY) {
                int x = MARGIN + j * GRID_SIZE;
                int y = MARGIN + i * GRID_SIZE;
                if (board[i][j] == BLACK) {
                    setfillcolor(BLACK);
                } else {
                    setfillcolor(WHITE);
                }
                fillcircle(x, y, GRID_SIZE / 2 - 2);
                setlinecolor(BLACK);
                circle(x, y, GRID_SIZE / 2 - 2);
            }
        }
    }
}
/**
 * @brief 绘制游戏信息 (当前玩家、游戏结果)
 */
void drawGameInfo() {
    setbkmode(TRANSPARENT);
    settextstyle(24, 0, "微软雅黑");
    if (gameOver) {
        if (currentPlayer == BLACK) {
            outtextxy(WINDOW_WIDTH / 2 - 60, WINDOW_HEIGHT - 40, "白棋胜利!");
        } else {
            outtextxy(WINDOW_WIDTH / 2 - 60, WINDOW_HEIGHT - 40, "黑棋胜利!");
        }
        outtextxy(WINDOW_WIDTH / 2 - 80, WINDOW_HEIGHT - 20, "按 R 重新开始");
    } else {
        if (currentPlayer == BLACK) {
            outtextxy(WINDOW_WIDTH / 2 - 40, WINDOW_HEIGHT - 40, "轮到黑棋");
        } else {
            if(aiThinking) {
                outtextxy(WINDOW_WIDTH / 2 - 60, WINDOW_HEIGHT - 40, "AI思考中...");
            } else {
                outtextxy(WINDOW_WIDTH / 2 - 40, WINDOW_HEIGHT - 40, "轮到白棋");
            }
        }
        outtextxy(WINDOW_WIDTH / 2 - 40, WINDOW_HEIGHT - 20, "按 ESC 退出");
    }
}
/**
 * @brief 处理鼠标点击事件
 * @param x 鼠标x坐标
 * @param y 鼠标y坐标
 */
void handleClick(int x, int y) {
    // 计算点击的棋盘格子坐标
    int col = (x - MARGIN + GRID_SIZE / 2) / GRID_SIZE;
    int row = (y - MARGIN + GRID_SIZE / 2) / GRID_SIZE;
    // 检查点击是否在有效范围内
    if (row >= 0 && row < BOARD_HEIGHT && col >= 0 && col < BOARD_WIDTH && board[row][col] == EMPTY) {
        if (makeMove(row, col, currentPlayer)) {
            if (checkWin(row, col, currentPlayer)) {
                gameOver = 1;
            } else {
                currentPlayer = (currentPlayer == BLACK) ? WHITE : BLACK; // 切换玩家
            }
        }
    }
}
/**
 * @brief 在指定位置落子
 * @param row 行
 * @param col 列
 * @param player 玩家
 * @return 落子是否成功
 */
int makeMove(int row, int col, int player) {
    if (board[row][col] == EMPTY) {
        board[row][col] = player;
        return 1;
    }
    return 0;
}
/**
 * @brief 检查是否获胜
 * @param row 最后落子的行
 * @param col 最后落子的列
 * @param player 玩家
 * @return 是否获胜
 */
int checkWin(int row, int col, int player) {
    // 检查方向: 水平、垂直、两个对角线
    int directions[4][2] = {{0, 1}, {1, 0}, {1, 1}, {1, -1}};
    for (int i = 0; i < 4; i++) {
        int count = 1; // 从当前棋子开始计数
        // 正向检查
        for (int j = 1; j < 5; j++) {
            int r = row + j * directions[i][0];
            int c = col + j * directions[i][1];
            if (r >= 0 && r < BOARD_HEIGHT && c >= 0 && c < BOARD_WIDTH && board[r][c] == player) {
                count++;
            } else {
                break;
            }
        }
        // 反向检查
        for (int j = 1; j < 5; j++) {
            int r = row - j * directions[i][0];
            int c = col - j * directions[i][1];
            if (r >= 0 && r < BOARD_HEIGHT && c >= 0 && c < BOARD_WIDTH && board[r][c] == player) {
                count++;
            } else {
                break;
            }
        }
        if (count >= 5) {
            return 1; // 赢了
        }
    }
    return 0; // 未赢
}
/**
 * @brief AI下棋逻辑
 */
void aiMove() {
    int bestRow, bestCol;
    findBestMove(&bestRow, &bestCol);
    if (makeMove(bestRow, bestCol, WHITE)) {
        if (checkWin(bestRow, bestCol, WHITE)) {
            gameOver = 1;
        } else {
            currentPlayer = BLACK; // 切换到玩家回合
        }
    }
}
/**
 * @brief 使用Minimax算法寻找最佳落子点
 * @param bestRow 最佳落子行指针
 * @param bestCol 最佳落子列指针
 */
void findBestMove(int *bestRow, int *bestCol) {
    int bestScore = -1000000;
    int depth = 3; // 搜索深度,可根据性能调整
    // 遍历棋盘,寻找所有空位
    for (int i = 0; i < BOARD_HEIGHT; i++) {
        for (int j = 0; j < BOARD_WIDTH; j++) {
            if (board[i][j] == EMPTY) {
                // 模拟落子
                board[i][j] = WHITE;
                int score = minimax(depth, -1000000, 1000000, 0); // AI是最大化玩家
                board[i][j] = EMPTY; // 撤销模拟
                // 如果是第一步,随机选择一个靠近中心的位置
                if (score > bestScore && (board[7][7] == EMPTY && score > 0)) {
                    bestScore = score;
                    *bestRow = i;
                    *bestCol = j;
                } else if (score > bestScore) {
                    bestScore = score;
                    *bestRow = i;
                    *bestCol = j;
                }
            }
        }
    }
}
/**
 * @brief Minimax算法的Alpha-Beta剪枝实现
 * @param depth 当前搜索深度
 * @param alpha Alpha值
 * @param beta Beta值
 * @param maximizingPlayer 是否是最大化玩家 (AI)
 * @return 当前局面的评分
 */
int minimax(int depth, int alpha, int beta, int maximizingPlayer) {
    // 检查终止条件: 深度为0 或有一方获胜
    if (depth == 0) {
        return evaluatePosition(WHITE) - evaluatePosition(BLACK);
    }
    // 简单的胜负判断,提高效率
    for(int i = 0; i < BOARD_HEIGHT; i++){
        for(int j = 0; j < BOARD_WIDTH; j++){
            if(board[i][j] != EMPTY){
                if(checkWin(i, j, WHITE)) return 1000000 + depth;
                if(checkWin(i, j, BLACK)) return -1000000 - depth;
            }
        }
    }
    if (maximizingPlayer) { // AI的回合 (最大化玩家)
        int maxEval = -1000000;
        for (int i = 0; i < BOARD_HEIGHT; i++) {
            for (int j = 0; j < BOARD_WIDTH; j++) {
                if (board[i][j] == EMPTY) {
                    board[i][j] = WHITE;
                    int eval = minimax(depth - 1, alpha, beta, 0);
                    board[i][j] = EMPTY;
                    maxEval = (eval > maxEval) ? eval : maxEval;
                    alpha = (alpha > eval) ? alpha : eval;
                    if (beta <= alpha) {
                        return maxEval; // Alpha-Beta剪枝
                    }
                }
            }
        }
        return maxEval;
    } else { // 玩家的回合 (最小化玩家)
        int minEval = 1000000;
        for (int i = 0; i < BOARD_HEIGHT; i++) {
            for (int j = 0; j < BOARD_WIDTH; j++) {
                if (board[i][j] == EMPTY) {
                    board[i][j] = BLACK;
                    int eval = minimax(depth - 1, alpha, beta, 1);
                    board[i][j] = EMPTY;
                    minEval = (eval < minEval) ? eval : minEval;
                    beta = (beta < eval) ? beta : eval;
                    if (beta <= alpha) {
                        return minEval; // Alpha-Beta剪枝
                    }
                }
            }
        }
        return minEval;
    }
}
/**
 * @brief 评估当前局面对AI的分数
 * @param player 要评估的玩家
 * @return 分数
 */
int evaluatePosition(int player) {
    int score = 0;
    int opponent = (player == WHITE) ? BLACK : WHITE;
    // 定义连子分数
    int scores[5] = {0, 10, 100, 1000, 10000}; // 1, 2, 3, 4, 5连
    // 检查所有方向
    for (int i = 0; i < BOARD_HEIGHT; i++) {
        for (int j = 0; j < BOARD_WIDTH; j++) {
            if (board[i][j] == player || board[i][j] == opponent) {
                int p = (board[i][j] == player) ? player : opponent;
                int o = (p == player) ? opponent : player;
                // 水平方向
                int count_p = 1, count_o = 0;
                if (j + 4 < BOARD_WIDTH) {
                    for (int k = 1; k < 5; k++) {
                        if (board[i][j + k] == p) count_p++;
                        else if (board[i][j + k] == o) { count_o++; break; }
                    }
                    if (count_o == 0) score += scores[count_p];
                }
                // 垂直方向
                count_p = 1; count_o = 0;
                if (i + 4 < BOARD_HEIGHT) {
                    for (int k = 1; k < 5; k++) {
                        if (board[i + k][j] == p) count_p++;
                        else if (board[i + k][j] == o) { count_o++; break; }
                    }
                    if (count_o == 0) score += scores[count_p];
                }
                // 对角线方向 (左上到右下)
                count_p = 1; count_o = 0;
                if (i + 4 < BOARD_HEIGHT && j + 4 < BOARD_WIDTH) {
                    for (int k = 1; k < 5; k++) {
                        if (board[i + k][j + k] == p) count_p++;
                        else if (board[i + k][j + k] == o) { count_o++; break; }
                    }
                    if (count_o == 0) score += scores[count_p];
                }
                // 对角线方向 (右上到左下)
                count_p = 1; count_o = 0;
                if (i + 4 < BOARD_HEIGHT && j - 4 >= 0) {
                    for (int k = 1; k < 5; k++) {
                        if (board[i + k][j - k] == p) count_p++;
                        else if (board[i + k][j - k] == o) { count_o++; break; }
                    }
                    if (count_o == 0) score += scores[count_p];
                }
            }
        }
    }
    return score;
}

如何运行和操作

  1. 保存代码: 将上面的代码保存为 Gomoku.c 文件。
  2. 编译运行: 在你的IDE(如Visual Studio)中打开并编译运行此文件。
  3. 游戏操作:
    • 黑棋 (你): 使用鼠标点击棋盘的交叉点来落子。
    • 白棋 (AI): AI会自动进行思考并落子。
    • 获胜: 任意一方在横、竖、斜方向上连成五子即获胜。
    • 退出游戏: 按键盘上的 ESC 键。
    • 重新开始: 游戏结束后,按 R 键可以重新开始一局新游戏。

代码核心逻辑解析

  1. 棋盘与界面 (initGame, drawBoard, drawPieces):

    五子棋c语言代码 人机
    (图片来源网络,侵删)
    • 使用一个二维数组 board[BOARD_HEIGHT][BOARD_WIDTH] 来存储棋盘状态,0代表空,1代表黑棋,2代表白棋。
    • EasyX库负责绘制图形。drawBoard() 画出网格线,drawPieces() 遍历数组,根据数组状态画出黑棋和白棋。
  2. 游戏流程 (main, handleClick):

    • main 函数是主循环,不断重绘界面并处理事件。
    • handleClick 将鼠标的屏幕坐标转换为棋盘的格子坐标,并调用 makeMove 尝试落子。
  3. 胜负判定 (checkWin):

    在每次落子后,从当前落子点出发,检查四个方向(水平、垂直、两个对角线)上是否有连续五个同色棋子,这是一个非常直接且高效的实现。

  4. AI算法 (aiMove, findBestMove, minimax, evaluatePosition):

    五子棋c语言代码 人机
    (图片来源网络,侵删)
    • 这是整个程序的核心,AI采用了 Minimax算法 结合 Alpha-Beta剪枝 来进行决策。
    • evaluatePosition (评估函数): 这是AI的“眼睛”,它扫描整个棋盘,为AI(白棋)的每一个潜在落子位置打分,评分标准是连子数,比如连成2个子得10分,连成3个子得100分,以此类推,这个函数会同时考虑AI的进攻和防守(即评估对手的得分)。
    • minimax (极小化极大算法): 这是一个递归算法,AI会模拟未来几步(depth)的对弈。
      • Maximizing Player (AI): 在它的回合,它会选择一个能让得分最高的走法。
      • Minimizing Player (玩家): 在玩家的回合,它会假设玩家会选择一个能让AI得分最低(即对玩家最有利)的走法。
    • findBestMove: 它是AI的决策入口,它遍历所有空位,对每一个空位调用 minimax 来模拟走完这一步之后,经过几层递归推演,最终得到的分数,它选择那个能让最终分数最高的空位作为最佳落子点。
    • Alpha-Beta剪枝: 这是 minimax 的一个优化,它通过维护 alpha(AI当前能得到的最大分)和 beta(玩家能强加给AI的最小分)两个值,来“剪掉”那些明显不会成为最优解的分支,极大地减少了计算量,使得AI可以搜索得更深。

这个AI虽然不是最强的,但足以提供不错的挑战性,并且很好地展示了游戏AI的基本原理,你可以通过调整 minimax 的搜索深度来改变AI的难度(深度越深,AI越强,但计算时间也越长)。

-- 展开阅读全文 --
头像
织梦如何搭建购物系统?
« 上一篇 04-30
dede如何调用消费金币?
下一篇 » 04-30

相关文章

取消
微信二维码
支付宝二维码

目录[+]