Negamax实现似乎不适用于井字游戏
Negamax implementation doesn't appear to work with tic-tac-toe
我已经实现了Negamax,它可以在维基百科上找到,其中包括alpha/beta修剪。
然而,它似乎倾向于失败的举动,据我所知,这是一个无效的结果。
这个游戏是Tic-Tac-Toe,我已经抽象了大部分游戏,所以应该很容易发现算法中的错误。
#include <list>
#include <climits>
#include <iostream>
//#define DEBUG 1
using namespace std;
struct Move {
int row, col;
Move(int row, int col) : row(row), col(col) { }
Move(const Move& m) { row = m.row; col = m.col; }
};
struct Board {
char player;
char opponent;
char board[3][3];
Board() { }
void read(istream& stream) {
stream >> player;
opponent = player == 'X' ? 'O' : 'X';
for(int row = 0; row < 3; row++) {
for(int col = 0; col < 3; col++) {
char playa;
stream >> playa;
board[row][col] = playa == '_' ? 0 : playa == player ? 1 : -1;
}
}
}
void print(ostream& stream) {
for(int row = 0; row < 3; row++) {
for(int col = 0; col < 3; col++) {
switch(board[row][col]) {
case -1:
stream << opponent;
break;
case 0:
stream << '_';
break;
case 1:
stream << player;
break;
}
}
stream << endl;
}
}
void do_move(const Move& move, int player) {
board[move.row][move.col] = player;
}
void undo_move(const Move& move) {
board[move.row][move.col] = 0;
}
bool isWon() {
if (board[0][0] != 0) {
if (board[0][0] == board[0][1] &&
board[0][1] == board[0][2])
return true;
if (board[0][0] == board[1][0] &&
board[1][0] == board[2][0])
return true;
}
if (board[2][2] != 0) {
if (board[2][0] == board[2][1] &&
board[2][1] == board[2][2])
return true;
if (board[0][2] == board[1][2] &&
board[1][2] == board[2][2])
return true;
}
if (board[1][1] != 0) {
if (board[0][1] == board[1][1] &&
board[1][1] == board[2][1])
return true;
if (board[1][0] == board[1][1] &&
board[1][1] == board[1][2])
return true;
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2])
return true;
if (board[0][2] == board [1][1] &&
board[1][1] == board[2][0])
return true;
}
return false;
}
list<Move> getMoves() {
list<Move> moveList;
for(int row = 0; row < 3; row++)
for(int col = 0; col < 3; col++)
if (board[row][col] == 0)
moveList.push_back(Move(row, col));
return moveList;
}
};
ostream& operator<< (ostream& stream, Board& board) {
board.print(stream);
return stream;
}
istream& operator>> (istream& stream, Board& board) {
board.read(stream);
return stream;
}
int evaluate(Board& board) {
int score = board.isWon() ? 100 : 0;
for(int row = 0; row < 3; row++)
for(int col = 0; col < 3; col++)
if (board.board[row][col] == 0)
score += 1;
return score;
}
int negamax(Board& board, int depth, int player, int alpha, int beta) {
if (board.isWon() || depth <= 0) {
#if DEBUG > 1
cout << "Found winner board at depth " << depth << endl;
cout << board << endl;
#endif
return player * evaluate(board);
}
list<Move> allMoves = board.getMoves();
if (allMoves.size() == 0)
return player * evaluate(board);
for(list<Move>::iterator it = allMoves.begin(); it != allMoves.end(); it++) {
board.do_move(*it, -player);
int val = -negamax(board, depth - 1, -player, -beta, -alpha);
board.undo_move(*it);
if (val >= beta)
return val;
if (val > alpha)
alpha = val;
}
return alpha;
}
void nextMove(Board& board) {
list<Move> allMoves = board.getMoves();
Move* bestMove = NULL;
int bestScore = INT_MIN;
for(list<Move>::iterator it = allMoves.begin(); it != allMoves.end(); it++) {
board.do_move(*it, 1);
int score = -negamax(board, 100, 1, INT_MIN + 1, INT_MAX);
board.undo_move(*it);
#if DEBUG
cout << it->row << ' ' << it->col << " = " << score << endl;
#endif
if (score > bestScore) {
bestMove = &*it;
bestScore = score;
}
}
if (!bestMove)
return;
cout << bestMove->row << ' ' << bestMove->col << endl;
#if DEBUG
board.do_move(*bestMove, 1);
cout << board;
#endif
}
int main() {
Board board;
cin >> board;
#if DEBUG
cout << "Starting board:" << endl;
cout << board;
#endif
nextMove(board);
return 0;
}
给出此输入:
O
X__
___
___
算法选择在0,1处放置棋子,造成一定的损失,对这个陷阱做什么(不能做任何事情来赢得或以平局结束):
XO_
X__
___
我确信游戏的实现是正确的,但算法也应该是正确的。
编辑:更新了evaluate
和nextMove
。
EDIT2:修复了第一个问题,尽管
您的evaluate
函数计算空位,并且不识别获胜板。
编辑:nextMove
中还有一个相对较小的问题。线路应为
int score = -negamax(board, 0, -1, INT_MIN + 1, INT_MAX);
修复该问题(以及evaluate
),然后代码选择正确的移动。
编辑:
这修复了它:
if (board.isWon() || depth <= 0) {
#if DEBUG > 1
cout << "Found winner board at depth " << depth << endl;
cout << board << endl;
#endif
return -100;
}
几乎所有这些问题都源于对score
含义的不清楚。这是从player
的角度出发的。如果negamax
正在评估玩家1的位置,而玩家1无法获胜,则得分应较低(例如-100);如果negamax
正在评估玩家-1的位置,而玩家-1无法获胜,则得分应较低(例如-100)。如果evaluate()
不能区分玩家,那么返回player * evaluate(board)
的分数就是错误的。
isWon
对于玩家的输赢都返回true。这无济于事。
使用播放器似乎有些有趣。
您的顶层循环调用"board.do_move(*it,1)",然后调用player=1的negamax。
然后negamax会调用"board.do_move(*it,player);",所以看起来第一个玩家实际上得到了2步。
相关文章:
- OpenGL - 在 NDC 中计算位置适用于着色器,但不适用于'regular'程序
- 使用模板参数重载C++方法:如何使其适用于模板的子类?
- 如何修复我的最大公约数代码?它适用于除零和零以外的所有数字
- 选择排序C++(已修改)并非适用于所有情况
- 无法让"std::enable_if"适用于无作用域枚举
- 请找出我的代码中的错误,它在提交得到错误答案的同时仍然适用于我的所有测试用例
- 确定夏令时是否适用于特定日期
- 是否有一种 STL 算法可以最后找到,但它也适用于指针?
- QT 样式表主题,适用于使用属性选择器的整个应用程序
- C++带有适用于左值和右值的引用参数的函数
- 代码适用于调试,但不适用于发布
- C++17 和更新的 std::分配器是否适用于动态数量的自定义堆?
- 适用于大型数组的无复制线程安全环形缓冲区
- NRVO 是否也适用于协程?
- 约束包容是否仅适用于概念?
- 程序只适用于包含(无副作用)cout声明
- 适用于频繁更改的大型数据集的最佳数据结构
- 相同的 for 循环适用于其他项目,但不适用于此项目。为什么?
- 无法让 FastNoise 适用于草地
- 其中列 = 值,仅适用于整数值