问题标题: 【最新】五子棋人机对战2.0-β版正式发布

3
0
已解决
包思远
包思远
新手启示者
新手启示者

心疼10豆(划掉)

我发现酷町问答和酷丁编程平台里的五子棋游戏大都分为以下三种类型:

1. 不支持人机对战

2. 支持人机对战,但人机只会随机下棋

3. 支持人机对战,但人机常常盲目进攻

对于第2、3种情况,即使没下过五子棋的人也能够很容易赢。

因此我通过数学计算与推理,研究出一个不那么愚 蠢的五子棋AI,它能够相对较好地防守,因为众所周知,在五子棋中,黑棋主要是要发展和进攻,而白棋主要是要防守和反击,在五子棋人机对战中,通常是玩家持黑棋先走,人机持白棋后走。

五子棋人机对战1.0版的代码已经完成,预计8.11发布。

此代码仍然不够完善,AI部分也有待改进,敬请期待!

包思远在2024-08-11 20:00:54追加了内容

五子棋人机对战1.0版正式发布了!

获取代码

exe就不发了,大家自己编译运行吧!

包思远在2024-08-11 21:12:08追加了内容

1.0-β版:修复了数组越界错误。

获取代码

包思远在2024-08-12 10:52:52追加了内容

1.0-γ版:修复了另一个数组越界问题

获取代码

包思远在2024-08-12 15:50:22追加了内容

五子棋人机对战1.0-δ版正式发布!

更新了五子棋AI对棋局情况的判断。

获取代码

包思远在2024-08-12 21:18:36追加了内容

五子棋人机对战2.0版正式发布!

2.0版:1.修复了数组、变量取值越界的问题

           2.加强了五子棋AI的攻击性

获取代码

包思远在2024-08-13 11:09:47追加了内容

2.0-β版:略微优化了AI决策策略

获取代码

包思远在2024-08-19 10:29:22追加了内容

即将暂停更新(因为有新项目了,且这个AI已经相对比较完善了,还没有想到更新方法,如果我后面想到了,可能会随缘重新更新)


0
2
陈俊霖
陈俊霖
新手天翼
新手天翼

轻松击败。

你的代码好像不判双三,并且不支持C++98。然而这个代码很不错。

送你一个兼容C++98的版本:

#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
const int BOARD_SIZE = 15;

enum Player {
    NONE,
    BLACK,
    WHITE
};

class Gobang {
    private:
        vector<vector<Player> > board;
        Player currentPlayer;
        long long f[19];
        int pb[15][15][5];
        int pw[15][15][5];
        static int num[15 * 15];
        struct node {
            int l, r;
            long long value;
        };
        void init() {
            f[0] = 0;
            for (int i = 1; i <= 18; i++) {
                f[i] = 4 * f[i - 1] + 1;
            }
        }
        static bool cmp(const node& x, const node& y) {
            if (x.value != y.value)return x.value > y.value;
            return num[x.l * 15 + x.r] > num[y.l * 15 + y.r];
        }

    public:
        Gobang() : board(BOARD_SIZE, vector<Player>(BOARD_SIZE, NONE)), currentPlayer(BLACK) {}

        void printBoard() {
            cout << "  ";
            for (int i = 0; i < BOARD_SIZE; ++i) {
                cout << (i < 10 ? " " : "") << i;
            }
            cout << endl;
            for (int i = 0; i < BOARD_SIZE; ++i) {
                cout << (i < 10 ? " " : "") << i << " ";
                for (int j = 0; j < BOARD_SIZE; ++j) {
                    switch (board[i][j]) {
                        case NONE:
                            cout << ".";
                            break;
                        case BLACK:
                            cout << "X";
                            break;
                        case WHITE:
                            cout << "O";
                            break;
                    }
                    cout << " ";
                }
                cout << endl;
            }
        }

        bool placePiece(int row, int col) {
            if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE || board[row][col] != NONE) {
                return false;
            }
            board[row][col] = currentPlayer;
            return true;
        }

        bool checkWin(int row, int col) {
            Player player = board[row][col];
            int count = 1;
            for (int i = col - 1; i >= 0 && board[row][i] == player; --i) count++;
            for (int i = col + 1; i < BOARD_SIZE && board[row][i] == player; ++i) count++;
            if (count >= 5) return true;

            count = 1;
            for (int i = row - 1; i >= 0 && board[i][col] == player; --i) count++;
            for (int i = row + 1; i < BOARD_SIZE && board[i][col] == player; ++i) count++;
            if (count >= 5) return true;

            count = 1;
            for (int i = row - 1, j = col - 1; i >= 0 && j >= 0 && board[i][j] == player; --i, --j) count++;
            for (int i = row + 1, j = col + 1; i < BOARD_SIZE && j < BOARD_SIZE && board[i][j] == player; ++i, ++j) count++;
            if (count >= 5) return true;

            count = 1;
            for (int i = row - 1, j = col + 1; i >= 0 && j < BOARD_SIZE && board[i][j] == player; --i, ++j) count++;
            for (int i = row + 1, j = col - 1; i < BOARD_SIZE && j >= 0 && board[i][j] == player; ++i, --j) count++;
            if (count >= 5) return true;

            return false;
        }

        void switchPlayer() {
            currentPlayer = (currentPlayer == BLACK) ? WHITE : BLACK;
        }

        int check(int row, int col) {
            int cnt = 0;
            for (int i = max(0, row - 1); i <= min(14, row + 1); i++) {
                for (int j = max(0, col - 1); j <= min(14, col + 1); j++) {
                    if (board[i][j] == WHITE)cnt++;
                }
            }
            return cnt;
        }

        int cal(int num, int x) {
            if (x == 1)return num * 2;
            if (x == 0)return num * 2 - 1;
            return 0;
        }

        string AI() {
            node v[15 * 15];
            for (int i = 0; i < 15; i++) {
                for (int j = 0; j < 15; j++) {
                    v[i * 15 + j].l = i;
                    v[i * 15 + j].r = j;
                    v[i * 15 + j].value = 0;
                    num[i * 15 + j] = 0;
                    if (board[i][j] != NONE)continue;
                    int left = (j > 0) ? pb[i][j - 1][1] : 0;
                    int right = (j < BOARD_SIZE - 1) ? pb[i][j + 1][1] : 0;
                    int up = (i > 0) ? pb[i - 1][j][2] : 0;
                    int down = (i < BOARD_SIZE - 1) ? pb[i + 1][j][2] : 0;
                    int upLeft = (i > 0 && j > 0) ? pb[i - 1][j - 1][3] : 0;
                    int downRight = (i < BOARD_SIZE - 1 && j < BOARD_SIZE - 1) ? pb[i + 1][j + 1][3] : 0;
                    int upRight = (i > 0 && j < BOARD_SIZE - 1) ? pb[i - 1][j + 1][4] : 0;
                    int downLeft = (i < BOARD_SIZE - 1 && j > 0) ? pb[i + 1][j - 1][4] : 0;
                    int d[5] = {0, 0, 0, 0, 0};
                    if (j - left > 0 && board[i][j - left - 1] == NONE)d[1] += 0x3f3f3f3f;
                    else d[1] += left;
                    if (j + right < BOARD_SIZE - 1 && board[i][j + right + 1] == NONE)d[1] += 0x3f3f3f3f;
                    else d[1] += right;
                    if (i - up > 0 && board[i - up - 1][j] == NONE)d[2] += 0x3f3f3f3f;
                    else d[2] += up;
                    if (i + down < BOARD_SIZE - 1 && board[i + down + 1][j] == NONE)d[2] += 0x3f3f3f3f;
                    else d[2] += down;
                    if (i - upLeft > 0 && j - upLeft > 0 && board[i - upLeft - 1][j - upLeft - 1] == NONE)d[3] += 0x3f3f3f3f;
                    else d[3] += upLeft;
                    if (i + downRight < BOARD_SIZE - 1 && j + downRight < BOARD_SIZE - 1 && board[i + downRight + 1][j + downRight + 1] == NONE)d[3] += 0x3f3f3f3f;
                    else d[3] += down;
                    if (i - upRight > 0 && j + upRight < BOARD_SIZE - 1 && board[i - upRight - 1][j + upRight + 1] == NONE)d[4] += 0x3f3f3f3f;
                    else d[4] += upLeft;
                    if (i + downLeft < BOARD_SIZE - 1 && j - downLeft > 0 && board[i + downLeft + 1][j - downLeft - 1] == NONE)d[4] += 0x3f3f3f3f;
                    else d[4] += down;
                    for (int i = 1; i <= 4; i++) {
                        if (d[i] > 2 * 0x3f3f3f3f)d[i] = 1;
                        else if (d[i] >= 5)d[i] = 0;
                        else d[i] = -1;
                    }
                    v[i * 15 + j].value = f[cal(left + right + 1, d[1])] + f[cal(up + down + 1, d[2])] + f[cal(upLeft + downRight + 1, d[3])] + f[cal(upRight + downLeft + 1, d[4])];
                    left = (j > 0) ? pw[i][j - 1][1] : 0;
                    right = (j < BOARD_SIZE - 1) ? pw[i][j + 1][1] : 0;
                    up = (i > 0) ? pw[i - 1][j][2] : 0;
                    down = (i < BOARD_SIZE - 1) ? pw[i + 1][j][2] : 0;
                    upLeft = (i > 0 && j > 0) ? pw[i - 1][j - 1][3] : 0;
                    downRight = (i < BOARD_SIZE - 1 && j < BOARD_SIZE - 1) ? pb[i + 1][j + 1][3] : 0;
                    upRight = (i > 0 && j < BOARD_SIZE - 1) ? pw[i - 1][j + 1][4] : 0;
                    downLeft = (i < BOARD_SIZE - 1 && j > 0) ? pw[i + 1][j - 1][4] : 0;
                    memset(d, 0, sizeof(d));
                    if (j - left > 0 && board[i][j - left - 1] == NONE)d[1] += 0x3f3f3f3f;
                    else d[1] += left;
                    if (j + right < BOARD_SIZE - 1 && board[i][j + right + 1] == NONE)d[1] += 0x3f3f3f3f;
                    else d[1] += right;
                    if (i - up > 0 && board[i - up - 1][j] == NONE)d[2] += 0x3f3f3f3f;
                    else d[2] += up;
                    if (i + down < BOARD_SIZE - 1 && board[i + down + 1][j] == NONE)d[2] += 0x3f3f3f3f;
                    else d[2] += down;
                    if (i - upLeft > 0 && j - upLeft > 0 && board[i - upLeft - 1][j - upLeft - 1] == NONE)d[3] += 0x3f3f3f3f;
                    else d[3] += upLeft;
                    if (i + downRight < BOARD_SIZE - 1 && j + downRight < BOARD_SIZE - 1 && board[i + downRight + 1][j + downRight + 1] == NONE)d[3] += 0x3f3f3f3f;
                    else d[3] += down;
                    if (i - upRight > 0 && j + upRight < BOARD_SIZE - 1 && board[i - upRight - 1][j + upRight + 1] == NONE)d[4] += 0x3f3f3f3f;
                    else d[4] += upLeft;
                    if (i + downLeft < BOARD_SIZE - 1 && j - downLeft > 0 && board[i + downLeft + 1][j - downLeft - 1] == NONE)d[4] += 0x3f3f3f3f;
                    else d[4] += down;
                    for (int i = 1; i <= 4; i++) {
                        if (d[i] > 2 * 0x3f3f3f3f)d[i] = 1;
                        else if (d[i] >= 5)d[i] = 0;
                        else d[i] = -1;
                    }
                    v[i * 15 + j].value += f[cal(left + right + 1, d[1])] + f[cal(up + down + 1, d[2])] + f[cal(upLeft + downRight + 1, d[3])] + f[cal(upRight + downLeft + 1, d[4])];
                    num[i * 15 + j] = check(i, j);
                }
            }
            sort(v, v + 15 * 15, cmp);
            int x = v[0].l, y = v[0].r;
            stringstream ss,ss2;
            ss<<x;
            string s;
            ss>>s;
            ss2<<y;
            string s2;
            ss2>>s2;
            s = s + " " + s2;
            return s;
        }

        void updatePArrayB(int row, int col) {
            int leftLength = (col > 0) ? pb[row][col - 1][1] : 0;
            int rightLength = (col < BOARD_SIZE - 1) ? pb[row][col + 1][1] : 0;
            if (col - leftLength >= 0) {
                pb[row][col - leftLength][1] = leftLength + rightLength + 1;
            }
            if (col + rightLength < BOARD_SIZE) {
                pb[row][col + rightLength][1] = leftLength + rightLength + 1;
            }

            int upLength = (row > 0) ? pb[row - 1][col][2] : 0;
            int downLength = (row < BOARD_SIZE - 1) ? pb[row + 1][col][2] : 0;
            if (row - upLength >= 0) {
                pb[row - upLength][col][2] = upLength + downLength + 1;
            }
            if (row + downLength < BOARD_SIZE) {
                pb[row + downLength][col][2] = upLength + downLength + 1;
            }

            int upLeftLength = (row > 0 && col > 0) ? pb[row - 1][col - 1][3] : 0;
            int downRightLength = (row < BOARD_SIZE - 1 && col < BOARD_SIZE - 1) ? pb[row + 1][col + 1][3] : 0;
            if (row - upLeftLength >= 0 && col - upLeftLength >= 0) {
                pb[row - upLeftLength][col - upLeftLength][3] = upLeftLength + downRightLength + 1;
            }
            if (row + downRightLength < BOARD_SIZE && col + downRightLength < BOARD_SIZE) {
                pb[row + downRightLength][col + downRightLength][3] = upLeftLength + downRightLength + 1;
            }

            int upRightLength = (row > 0 && col < BOARD_SIZE - 1) ? pb[row - 1][col + 1][4] : 0;
            int downLeftLength = (row < BOARD_SIZE - 1 && col > 0) ? pb[row + 1][col - 1][4] : 0;
            if (row - upRightLength >= 0 && col + upRightLength < BOARD_SIZE) {
                pb[row - upRightLength][col + upRightLength][4] = upRightLength + downLeftLength + 1;
            }
            if (row + downLeftLength < BOARD_SIZE && col - downLeftLength >= 0) {
                pb[row + downLeftLength][col - downLeftLength][4] = upRightLength + downLeftLength + 1;
            }
        }

        void updatePArrayW(int row, int col) {
            int leftLength = (col > 0) ? pw[row][col - 1][1] : 0;
            int rightLength = (col < BOARD_SIZE - 1) ? pw[row][col + 1][1] : 0;
            if (col - leftLength >= 0) {
                pw[row][col - leftLength][1] = leftLength + rightLength + 1;
            }
            if (col + rightLength < BOARD_SIZE) {
                pw[row][col + rightLength][1] = leftLength + rightLength + 1;
            }

            int upLength = (row > 0) ? pw[row - 1][col][2] : 0;
            int downLength = (row < BOARD_SIZE - 1) ? pw[row + 1][col][2] : 0;
            if (row - upLength >= 0) {
                pw[row - upLength][col][2] = upLength + downLength + 1;
            }
            if (row + downLength < BOARD_SIZE) {
                pw[row + downLength][col][2] = upLength + downLength + 1;
            }

            int upLeftLength = (row > 0 && col > 0) ? pw[row - 1][col - 1][3] : 0;
            int downRightLength = (row < BOARD_SIZE - 1 && col < BOARD_SIZE - 1) ? pw[row + 1][col + 1][3] : 0;
            if (row - upLeftLength >= 0 && col - upLeftLength >= 0) {
                pw[row - upLeftLength][col - upLeftLength][3] = upLeftLength + downRightLength + 1;
            }
            if (row + downRightLength < BOARD_SIZE && col + downRightLength < BOARD_SIZE) {
                pw[row + downRightLength][col + downRightLength][3] = upLeftLength + downRightLength + 1;
            }

            int upRightLength = (row > 0 && col < BOARD_SIZE - 1) ? pw[row - 1][col + 1][4] : 0;
            int downLeftLength = (row < BOARD_SIZE - 1 && col > 0) ? pw[row + 1][col - 1][4] : 0;
            if (row - upRightLength >= 0 && col + upRightLength < BOARD_SIZE) {
                pw[row - upRightLength][col + upRightLength][4] = upRightLength + downLeftLength + 1;
            }
            if (row + downLeftLength < BOARD_SIZE && col - downLeftLength >= 0) {
                pw[row + downLeftLength][col - downLeftLength][4] = upRightLength + downLeftLength + 1;
            }
        }

        void play() {
            init();
            memset(pb, 0, sizeof(pb));
            memset(pw, 0, sizeof(pw));
            int row, col;
            while (true) {
                system("cls"); 
                printBoard();
                cout << "当前玩家: " << ((currentPlayer == BLACK) ? "你--黑棋 (X)" : "人机--白棋 (O)") << endl;
                if (currentPlayer == BLACK) {
                    cout << "输入行和列,输完换行 (例: 0 0 为 左上角): ";
                    cin >> row >> col;
                    if (placePiece(row, col)) {
                        if (checkWin(row, col)) {
                            system("cls");
                            printBoard();
                            cout << "玩家 " << ((currentPlayer == BLACK) ? "你--黑棋 (X)" : "人机--白棋 (O)") << " wins!" << endl;
                            break;
                        }
                        updatePArrayB(row, col);
                        switchPlayer();
                    } else {
                        cout << "无效移动!请再试一次." << endl;
                        system("pause"); 
                    }
                } else {
                    cout << "等待人机下棋......" << endl;
                    string s = AI();
                    stringstream ss1,ss2;
                    ss1<<s.substr(0, s.find(" "));
                    ss2<<s.substr(s.find(" ") + 1, s.size() - s.find(" ") - 1);
                    int row ,col;
                    ss1>>row,ss2>>col;
                    if (placePiece(row, col)) {
                        if (checkWin(row, col)) {
                            system("cls");
                            printBoard();
                            cout << "玩家 " << ((currentPlayer == BLACK) ? "你--黑棋 (X)" : "人机--白棋 (O)") << " wins!" << endl;
                            break;
                        }
                        updatePArrayW(row, col);
                        switchPlayer();
                    }
                }
            }
        }
};
int Gobang::num[15 * 15];
int main() {
    Gobang game;
    game.play();
    Sleep(5000);
    return 0;
}

 

2
包思远
包思远
新手启示者
新手启示者

此代码非常的短,还未到200行,AI下棋速度相对较快!

1
0
汪子阳
汪子阳
初级守护
初级守护

????????????????????????????????

0
0
包思远
包思远
新手启示者
新手启示者

@张书豪  给你推荐个新一点的编译器:https://f.ws28.cn/f/etjj9j5bru2

下载解压后双击打开就能用了

0
0
0
包思远
包思远
新手启示者
新手启示者

@周炜骐   你提出的问题已经修复完成

0
0
吴子轩
吴子轩
高级守护
高级守护

我比较菜,能不能设置简单一点的人机啊

0
0
包思远
包思远
新手启示者
新手启示者

反攻性增强的AI,一不留神输了。。。。。。

0
包思远
包思远
新手启示者
新手启示者

当我为AI没有挡我的活3高兴时,突然发现它已经活4了。。。。。。

又输了1局

0
包思远
包思远
新手启示者
新手启示者

@贾若曦   现在升级了,挺难赢的,我下了3局才赢一局

0
0
0
0
0
0
0
0
0
0
0
陈俊霖
陈俊霖
新手天翼
新手天翼

我建议改下兼容性,手写itos,stoi,把所有的Player::全部去掉,把enum class改成enum,你的写法应该更为规范,但是去掉之后可以满足兼容。

0
0
0
0
0
贾若曦
贾若曦
资深天翼
资深天翼

这太牛了阿,下了好几盘,才赢一盘

0
李宜和
李宜和
高级启示者
高级启示者

最新版14行报错

好久没用dev-c++了,可能版本太老了吧

0
0
包思远
包思远
新手启示者
新手启示者

@贾若曦   想赢不难啊,这个AI还不是特别高级啊

我要回答