3
已解决
包思远
新手启示者
新手启示者
心疼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
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
陈俊霖
新手天翼
新手天翼
我建议改下兼容性,手写itos,stoi,把所有的Player::全部去掉,把enum class改成enum,你的写法应该更为规范,但是去掉之后可以满足兼容。
0
0
0
0
0
0
0
0