我正在尝试为跳棋游戏(AI vs AI)编写一个使用阿尔法-贝塔修剪的算法。你可以看到代码
游戏本身运行良好,但人工智能(阿尔法-贝塔修剪算法)似乎有一个错误,因为机器人基本上是互相喂食跳棋(根本没有计算显示)。代码包含2个不同版本的alpha-beta算法函数(更详细和不太详细)。
我试过在alphabeta()
中跟踪tmp
的值,它似乎有正常值(在深度=5的情况下范围为-3到3)。
我也尝试过在我的代码中实现此代码,但得到了相同的结果。
我最好的猜测是问题出在bool White
第二个最佳猜测-
移动最好移动
。我不确定将其从递归函数中删除是否正确。
错误是什么?
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Move
{
public:
pair<int, int> start;
pair<int, int> end;
bool lethal;
Move(int x, int y, int x1, int y1, bool kill)
{
start.first = x; start.second = y;
end.first = x1; end.second = y1;
lethal = kill;
}
};
char **initBoard(int size)
{
char **board = new char*[size];
for (int count = 0; count < size; count++)
board[count] = new char[size];
return board;
}
void newGame(char **board, int size)
{
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
{
board[i][j] = '-';
if ((i == 0 || i == 2) && j % 2 == 1) board[i][j] = 'O';
if (i == 1 && j % 2 == 0) board[i][j] = 'O';
if ((i == size - 3 || i == size - 1) && j % 2 == 0) board[i][j] = 'X';
if (i == size - 2 && j % 2 == 1) board[i][j] = 'X';
}
}
void printBoard(char **board, int size)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
cout << board[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
void do_move(char **board, Move play)
{
char temp = board[play.start.first][play.start.second];
board[play.start.first][play.start.second] = board[play.end.first][play.end.second];
board[play.end.first][play.end.second] = temp;
if (play.lethal)
board[(play.end.first + play.start.first) / 2][(play.end.second + play.start.second) / 2] = '-';
}
void undo_move(char **board, Move play)
{
board[play.start.first][play.start.second] = board[play.end.first][play.end.second];
board[play.end.first][play.end.second] = '-';
if (play.lethal)
{
if (board[play.start.first][play.start.second] == 'X')
board[(play.end.first + play.start.first) / 2][(play.end.second + play.start.second) / 2] = 'O';
if (board[play.start.first][play.start.second] == 'O')
board[(play.end.first + play.start.first) / 2][(play.end.second + play.start.second) / 2] = 'X';
}
}
vector<Move> findMoves(char **board, int size, bool whiteTurn)
{
vector<Move> moves;
//first jump (if possible)
for (int x = 0; x < size; x++)
{
for (int y = 0; y < size; y++)
{
if (whiteTurn && board[x][y] == 'X')
{
if (x > 1 && y > 1 && board[x - 1][y - 1] == 'O' && board[x - 2][y - 2] == '-')
moves.push_back(Move(x, y, x - 2, y - 2, true));
if (x > 1 && y < size - 2 && board[x - 1][y + 1] == 'O' && board[x - 2][y + 2] == '-')
moves.push_back(Move(x, y, x - 2, y + 2, true));
if (x < size - 2 && y > 1 && board[x + 1][y - 1] == 'O' && board[x + 2][y - 2] == '-')
moves.push_back(Move(x, y, x + 2, y - 2, true));
if (x < size - 2 && y < size - 2 && board[x + 1][y + 1] == 'O' && board[x + 2][y + 2] == '-')
moves.push_back(Move(x, y, x + 2, y + 2, true));
}
if (!whiteTurn && board[x][y] == 'O')
{
if (x > 1 && y > 1 && board[x - 1][y - 1] == 'X' && board[x - 2][y - 2] == '-')
moves.push_back(Move(x, y, x - 2, y - 2, true));
if (x > 1 && y < size - 2 && board[x - 1][y + 1] == 'X' && board[x - 2][y + 2] == '-')
moves.push_back(Move(x, y, x - 2, y + 2, true));
if (x < size - 2 && y > 1 && board[x + 1][y - 1] == 'X' && board[x + 2][y - 2] == '-')
moves.push_back(Move(x, y, x + 2, y - 2, true));
if (x < size - 2 && y < size - 2 && board[x + 1][y + 1] == 'X' && board[x + 2][y + 2] == '-')
moves.push_back(Move(x, y, x + 2, y + 2, true));
}
}
}
//then move
for (int x = 0; x < size; x++)
{
for (int y = 0; y < size; y++)
{
if (whiteTurn && board[x][y] == 'X')
{
if (x > 0 && y > 0 && board[x - 1][y - 1] == '-')
moves.push_back(Move(x, y, x - 1, y - 1, false));
if (x > 0 && y < size - 1 && board[x - 1][y + 1] == '-')
moves.push_back(Move(x, y, x - 1, y + 1, false));
}
if (!whiteTurn && board[x][y] == 'O')
{
if (x < size - 1 && y > 0 && board[x + 1][y - 1] == '-')
moves.push_back(Move(x, y, x + 1, y - 1, false));
if (x < size - 1 && y < size - 1 && board[x + 1][y + 1] == '-')
moves.push_back(Move(x, y, x + 1, y + 1, false));
}
}
}
return moves;
}
//plain score calculation function
int getScore(char **board, int size, bool whiteTurn)
{
int whiteNum = 0, blackNum = 0;
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
if (board[i][j] == 'X') whiteNum++;
if (board[i][j] == 'O') blackNum++;
}
}
if (whiteTurn)
return whiteNum - blackNum;
else
return blackNum - whiteNum;
}
//old function, doesnt work as intended too
/*Move getBestMove(char **board, int size, bool whiteTurn)
{
int score, tmp;
Move bestMove(0, 0, 0, 0, false);
vector<Move> movelist = findMoves(board, size, whiteTurn);
score = getScore(board, size, whiteTurn);
for (unsigned int i = 0; i < movelist.size(); i++)
{
do_move(board, movelist.at(i));
tmp = getScore(board, size, whiteTurn);
undo_move(board, movelist.at(i));
if (tmp >= score)
{
score = tmp;
bestMove = movelist.at(i);
}
}
return bestMove;
}*/
//made this global - no idea how to avoid it being global with recursion in alphabeta
Move bestMove(0, 0, 0, 0, false);
//alphabeta function with more detailed calculations
/*int AlphaBeta(char **board, int size, bool whiteTurn, int depth, int alpha, int beta)
{
if (depth == 0) return getScore(board, size, whiteTurn);
int score = -100;
vector<Move> movelist = findMoves(board, size, whiteTurn);
for (unsigned int i = 0; i < movelist.size(); i++)
{
do_move(board, movelist.at(i));
int tmp = -AlphaBeta(board, size, !whiteTurn, depth - 1, alpha, beta);
undo_move(board, movelist.at(i));
if (tmp > score)
{
if (whiteTurn)
{
if (score > alpha)
{
alpha = score;
}
if (-alpha <= beta)
{
return alpha;
}
}
else
{
if (score > beta)
{
beta = score;
}
if (-beta <= alpha)
{
return beta;
}
}
}
}
return score;
}*/
//generic alphabeta function
int alphabeta(char **board, int size, bool whiteTurn, int depth, int alpha, int beta)
{
if (depth == 0) return getScore(board, size, whiteTurn);
vector<Move> movelist = findMoves(board, size, whiteTurn);
for (const Move &move : movelist)
{
do_move(board, move);
int tmp = -alphabeta(board, size, !whiteTurn, depth - 1, -beta, -alpha);
undo_move(board, move);
if (tmp > alpha)
{
if (depth == 5)
bestMove = move;
alpha = tmp;
}
}
return alpha;
}
//main game loop
void game(char **board, int size, bool &whiteTurn)
{
newGame(board, size);
printBoard(board, size);
system("PAUSE");
int a = -std::numeric_limits<int>::max();
int b = std::numeric_limits<int>::max();
do
{
alphabeta(board, size, whiteTurn, 5, a, b);
do_move(board, bestMove);
whiteTurn = !whiteTurn;
system("cls");
printBoard(board, size);
system("PAUSE");
} while (!findMoves(board, size, whiteTurn).empty());
}
int main()
{
int n = 8;
bool whTurn = true;
char **board=initBoard(n);
game(board, n, whTurn);
return 0;
}
文献中通常描述α-β截止的方式是不必要的复杂。你不需要两个限制,从相同的玩家角度评估移动也不是一个好主意。以下是更清晰的描述:
for all moves: evaluate the move give it a temporary score from the point of view of the player at move for all counter moves: evaluate the move give is a temporary score from the point of view of the player at move for all counter-counter moves: evaluate the move give it a score from the point of view of the player at move undo the counter-counter move update best counter-counter move if better if the counter-counter move is so good, that current counter move cant be best, (other is better) then break subtract best counter-counter-move score from temporary counter score undo the counter move update best counter move if better if the counter move is so good, that current move cant be best, (other is better) then break subtract best counter-move score from temporary score undo the move update best move if better
逻辑是这样的:
假设你已经评估了几次招式,目前为止最好的招式值3
当前招式的临时得分是5。
你当前评估的反招式值4。
这意味着当前招式最多值1
我的程序中有一个有效的negamax算法。然而,我需要程序在时间内找到最佳移动。我做了一些研究,似乎用我的negamax算法进行迭代深化是最好的方法。现在,我启动搜索的函数如下所示: 我想我也应该重新排序之前的最佳移动到儿童列表的前面,但是,我在等待实现,直到我得到基本版本的工作。实际的阿尔法-贝塔函数是这样的: 当我尝试调试时,一切似乎都在按预期工作。然而,当我将迭代深化版本与常规的alphab
我目前正在从事我的第一个C项目,并选择使用基于Minimax的AI编写一个Connect Four(又名Score 4),更具体地说是基于Alpha-Beta修剪方法。 到目前为止,我了解到AB修剪包含在一个递归算法中,该算法考虑了一个alpha和一个beta参数,这是您在游戏树中找不到的“极限”。此外,我们定义了最大化和最小化玩家,前者是第一个开始玩游戏的玩家。最后,还有一个“深度”,我把它理解
我有一个Tic Tac Toe游戏,它使用了极大极小算法。我想通过添加alpha-beta修剪来改进这一点。然而,阿尔法-贝塔法似乎无法有效地计算移动。它只是把它的一块放在下一个可用的空间里,不管它是不是最佳的移动。我对极小极大法没有这个问题。我确信这是我一直忽略的一些简单的事情,所以请原谅我。我用这个教程来学习minimax,用这个教程来学习alpha-beta修剪。 这是极小极大类。它包括阿尔
我正在用Java做一个国际象棋游戏,并且(我认为)已经成功地为AI玩家实现了Negamax。我在添加阿尔法贝塔剪枝来改进算法时遇到了一些麻烦。我已经尝试了下面的教程和示例代码,但就是不明白它是如何工作的。 以下是我目前必须获得最佳移动的代码: 这是我尝试将aplha-beta修剪添加到我的(工作)内切方法中: 最后是控制台的外观 任何帮助都将不胜感激。提前感谢。
我试图在我的negamax中实现转置表。但首先我想理解伪代码中的所有思想: ' 函数 negamax(节点, 深度, α, β, 颜色) 是 alphaOrig := α 但我想知道的一件事是旗帜是什么?喜欢、和?
我试图让Alpha-beta修剪工作,但与我的Minimax函数相比,它给了我完全错误的动作。这是我的极大极小函数,它现在工作得很好。 这是我的Alphabeta修剪函数 两者都使用相同的评估,不确定这里出了什么问题。谢谢你的帮助。