admin管理员组文章数量:1402839
I'm implementing the Minimax algorithm for a two-player board game using Dart. My approach involves two nested for loops to simulate the maximizing and minimizing players' turns. I've also developed a heuristic evaluation function that considers several key factors to assess the board state. These factors include:
Pawn Count: Weighting the number of pawns each player possesses.
King Count: Prioritizing the acquisition of king pieces.
Center Control: Evaluating the degree to which a player controls the center of the board.
Threatened Pieces: Penalizing positions where a player's pieces are under threat.
Piece Mobility: Rewarding positions with greater piece movement options.
My intention is to create a Minimax implementation that can play reasonably well, aiming for at least a 50% win rate against a basic opponent. While the code executes without errors, the AI's performance is significantly below my expectations. It often makes seemingly poor moves, failing to capitalize on obvious advantages and falling into simple traps.
This method is used to call the minMax and find the best move to play:
void playBestMove(List<String> piecesPosCopy, int depth) {
int bestEval = -9999;
int bestMovePrev = -1;
int bestMoveIndex = -1;
// Save the current state before call
List<String> defaultState = List.from(piecesPos);
for (int i = 0; i < piecesPosCopy.length; i++) {
if (piecesPosCopy[i][0] == "B" || piecesPosCopy[i][0] == "O") {
List<int> possibleMoves = getPossibleMoves(piecesPosCopy, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPosCopy);
// Make the move
makeMove(piecesPosCopy, i, move);
// Evaluate the move
int eval = minMax(piecesPosCopy, depth - 1, false, -9999, 9999);
// Restore the state
piecesPosCopy = List.from(saveState);
// Update best move
if (eval > bestEval) {
bestEval = eval;
bestMovePrev = i;
bestMoveIndex = move;
}
}
}
}
//Restored state to original State before making best move
piecesPos = List.from(defaultState);
// Play the best move
if (bestMovePrev != -1 && bestMoveIndex != -1) {
//MakeMove
makeBotMove(bestMovePrev, bestMoveIndex);
}
}
This is the minmax code
int minMax(List<String> piecesPos, int depth, bool isMaximizing, int alpha, int beta) {
// Base case: if depth is 0 or the game is over, return the evaluation
if (depth == 0 || isGameOver(piecesPos)) {
return evaluateBoard(piecesPos);
}
if (isMaximizing) {
int maxEval = -9999; // Initialize to a very low value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "B" || piecesPos[i][0] == "O") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, false, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update maxEval
maxEval = max(maxEval, eval);
alpha = max(alpha, eval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return maxEval;
} else {
int minEval = 9999; // Initialize to a very high value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "W" || piecesPos[i][0] == "Q") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, true, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update minEval
minEval = min(minEval, eval);
beta = min(beta, eval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return minEval;
}
}
I'm implementing the Minimax algorithm for a two-player board game using Dart. My approach involves two nested for loops to simulate the maximizing and minimizing players' turns. I've also developed a heuristic evaluation function that considers several key factors to assess the board state. These factors include:
Pawn Count: Weighting the number of pawns each player possesses.
King Count: Prioritizing the acquisition of king pieces.
Center Control: Evaluating the degree to which a player controls the center of the board.
Threatened Pieces: Penalizing positions where a player's pieces are under threat.
Piece Mobility: Rewarding positions with greater piece movement options.
My intention is to create a Minimax implementation that can play reasonably well, aiming for at least a 50% win rate against a basic opponent. While the code executes without errors, the AI's performance is significantly below my expectations. It often makes seemingly poor moves, failing to capitalize on obvious advantages and falling into simple traps.
This method is used to call the minMax and find the best move to play:
void playBestMove(List<String> piecesPosCopy, int depth) {
int bestEval = -9999;
int bestMovePrev = -1;
int bestMoveIndex = -1;
// Save the current state before call
List<String> defaultState = List.from(piecesPos);
for (int i = 0; i < piecesPosCopy.length; i++) {
if (piecesPosCopy[i][0] == "B" || piecesPosCopy[i][0] == "O") {
List<int> possibleMoves = getPossibleMoves(piecesPosCopy, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPosCopy);
// Make the move
makeMove(piecesPosCopy, i, move);
// Evaluate the move
int eval = minMax(piecesPosCopy, depth - 1, false, -9999, 9999);
// Restore the state
piecesPosCopy = List.from(saveState);
// Update best move
if (eval > bestEval) {
bestEval = eval;
bestMovePrev = i;
bestMoveIndex = move;
}
}
}
}
//Restored state to original State before making best move
piecesPos = List.from(defaultState);
// Play the best move
if (bestMovePrev != -1 && bestMoveIndex != -1) {
//MakeMove
makeBotMove(bestMovePrev, bestMoveIndex);
}
}
This is the minmax code
int minMax(List<String> piecesPos, int depth, bool isMaximizing, int alpha, int beta) {
// Base case: if depth is 0 or the game is over, return the evaluation
if (depth == 0 || isGameOver(piecesPos)) {
return evaluateBoard(piecesPos);
}
if (isMaximizing) {
int maxEval = -9999; // Initialize to a very low value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "B" || piecesPos[i][0] == "O") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, false, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update maxEval
maxEval = max(maxEval, eval);
alpha = max(alpha, eval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return maxEval;
} else {
int minEval = 9999; // Initialize to a very high value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "W" || piecesPos[i][0] == "Q") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, true, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update minEval
minEval = min(minEval, eval);
beta = min(beta, eval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return minEval;
}
}
Share
Improve this question
edited Mar 23 at 15:17
desertnaut
60.5k32 gold badges155 silver badges181 bronze badges
asked Mar 21 at 8:05
Franklyn OrebenFranklyn Oreben
3083 silver badges15 bronze badges
1
- Disable the alpha-beta pruning entirely and let it run a pure brute force at modest search depths to establish that the fault isn't actually in the evaluation function that isn't shown here. I'm fairly convinced there is a serious error in the alpha-beta pruning code see below... – Martin Brown Commented Mar 29 at 19:28
2 Answers
Reset to default 0It don't speak Dart but I think I can follow the syntax well enough to have a good chance of suggesting a correction to the above minMax
code that I think ought to work. Obviously I can't test it.
I think the computation of alpha/beta is using the wrong eval
value at least some of the time. I think it should be testing against and replacing the cutoff values with the most recent maxEval/minEval
. Trivial modification and hard to spot but should make a big difference.
int minMax(List<String> piecesPos, int depth, bool isMaximizing, int alpha, int beta) {
// Base case: if depth is 0 or the game is over, return the evaluation
if (depth == 0 || isGameOver(piecesPos)) {
return evaluateBoard(piecesPos);
}
if (isMaximizing) {
int maxEval = -9999; // Initialize to a very low value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "B" || piecesPos[i][0] == "O") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, false, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update maxEval
maxEval = max(maxEval, eval);
// alpha = max(alpha, eval); // should be maxEval
alpha = max(alpha, maxEval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return maxEval;
} else {
int minEval = 9999; // Initialize to a very high value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "W" || piecesPos[i][0] == "Q") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, true, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update minEval
minEval = min(minEval, eval);
// beta = min(beta, eval); // should be minEval
beta = min(beta, minEval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return minEval;
}
}
Rather than have two copies of what is essentially the same code I prefer to use the convention that side = 1
or -1
and a variant of alpha-beta algorithm that is now called [Negascout](https://en.wikipedia./wiki/Negamax). Exploiting the simplification to have code that only implements maximise eval and then using the identity that min(a, b) = -max(-b, -a)
I had to save the board state for each move made when Minimax was called and analyze them individually. This allowed me to track the moves and notice that the board state was not being updated correctly. I’ve now resolved the issue. The problem was related to how I was passing my board state (piecesPos). I was retrieving and passing the wrong board state, which caused Minimax to make incorrect or suboptimal moves. Thank you all for your contributions; it is greatly appreciated.
Renaming to piecesPosCopy and using piecesPos
This was getting the actual board state to use when min max is called.
int minMax(List<String> piecesPosCopy, int depth, bool isMaximizing, int alpha, int beta) {
// Base case: if depth is 0 or the game is over, return the evaluation
if (depth == 0 || isGameOver(piecesPos)) {
return evaluateBoard(piecesPos);
}
if (isMaximizing) {
int maxEval = -9999; // Initialize to a very low value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "B" || piecesPos[i][0] == "O") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
performMultitakeAnim = false;
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, false, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update maxEval
maxEval = max(maxEval, eval);
alpha = max(alpha, eval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return maxEval;
} else {
int minEval = 9999; // Initialize to a very high value
for (int i = 0; i < piecesPos.length; i++) {
if (piecesPos[i][0] == "W" || piecesPos[i][0] == "Q") {
List<int> possibleMoves = getPossibleMoves(piecesPos, i);
for (int move in possibleMoves) {
// Save the current state
List<String> saveState = List.from(piecesPos);
// Make the move
performMultitakeAnim = false;
makeMove(piecesPos, i, move);
// Recursive call
int eval = minMax(piecesPos, depth - 1, true, alpha, beta);
// Restore the state
piecesPos = List.from(saveState);
// Update minEval
minEval = min(minEval, eval);
beta = min(beta, eval);
// Alpha-Beta Pruning
if (beta <= alpha) {
break;
}
}
}
}
return minEval;
}
}
本文标签: dartMinimax Implementation with Heuristic Unexpectedly Poor PerformanceStack Overflow
版权声明:本文标题:dart - Minimax Implementation with Heuristic: Unexpectedly Poor Performance - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744367138a2602835.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论