C++ 井字极小极大
C++ Tic tac toe minimax
我正在尝试创建一个 C++ 无与伦比的井字游戏 AI。看了几个关于这个主题的视频后,我想我已经明白了。屏幕上会弹出一条错误消息“表达式:矢量下标超出范围”。我相信错误来自 availableMoves() 函数。但是我不知道为什么。
游戏本身运行良好。任何帮助将不胜感激。
<pre>#include <iostream>
#include <vector>
#include <ctime>
bool in(std::vector<int> v, int element)
{
for (int i = 0; i < v.size(); i++)
{
if (element == v[i])
{
return true;
}
}
return false;
}
class Board
{
private:
char board[3][3] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'} };
public:
void displayBoard()
{
std::cout << "___________________" << std::endl;
for (int i = 0; i < 3; i++)
{
std::cout << "| ";
for (int j = 0; j < 3; j++)
{
std::cout << board[i][j] << " | ";
}
std::cout << std::endl;
}
std::cout << "___________________" << std::endl;
}
std::vector<int> availableMoves()
{
std::vector<int> moves;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (board[i][j] != 'X' && board[i][j] != 'O')
{
moves.push_back(i * 3 + j);
}
}
}
return moves;
}
void move(int choice, char mark)
{
int y = choice / 3;
int x = choice - y * 3;
board[y][x] = mark;
}
void revert(int choice)
{
int y = choice / 3;
int x = choice - y * 3;
board[y][x] = (char)choice + 48;
}
int checkWin()
{
for (int i = 0; i < 3; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2])
{
if (board[i][0] == 'X')
{
return 1;
}
else if (board[i][0] == 'O')
{
return -1;
}
}
}
for (int i = 0; i < 3; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i])
{
if (board[0][i] == 'X')
{
return 1;
}
else if (board[0][i] == 'O')
{
return -1;
}
}
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2])
{
if (board[0][0] == 'X')
{
return 1;
}
else if (board[0][0] == 'O')
{
return -1;
}
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0])
{
if (board[0][2] == 'X')
{
return 1;
}
else if (board[0][2] == 'O')
{
return -1;
}
}
return 0;
}
int evaluate()
{
return (checkWin() * -1) * (availableMoves().size() + 1);
}
Board& operator=(Board& b)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board[i][j] = b.board[i][j];
}
}
return (*this);
}
};
class TicTacToe
{
private:
Board board;
int turn;
int searches = 0;
public:
TicTacToe()
{
std::srand(time(0));
turn = std::rand() % 2;
}
int minimax(int depth, Board curBoard, bool is_max)
{
searches++;
if (depth == 0 || curBoard.checkWin() != 0)
{
return board.evaluate();
}
if (is_max)
{
int max_eval = -2147483647;
for (int i = 0; i < curBoard.availableMoves().size(); i++)
{
curBoard.move(curBoard.availableMoves()[i], 'O');
depth -= 1;
int eval = minimax(depth, curBoard, false);
curBoard.revert(curBoard.availableMoves()[i]);
if (eval > max_eval)
{
max_eval = eval;
}
}
return max_eval;
}
if (!is_max)
{
int min_eval = 2147483647;
for (int i = 0; i < curBoard.availableMoves().size(); i++)
{
curBoard.move(curBoard.availableMoves()[i], 'X');
depth -= 1;
int eval = minimax(depth, curBoard, true);
curBoard.revert(curBoard.availableMoves()[i]);
if (eval < min_eval)
{
min_eval = eval;
}
}
return min_eval;
}
}
void game()
{
while (board.checkWin() == 0 && board.availableMoves().size() != 0)
{
board.displayBoard();
if (turn % 2 == 0)
{
std::cout << std::endl;
int choice;
std::cout << "Enter Your Move: ";
std::cin >> choice;
choice -= 1;
while (!in(board.availableMoves(), choice))
{
std::cout << "Enter A Valid Move: ";
std::cin >> choice;
}
board.move(choice, 'X');
std::cout << std::endl;
turn++;
}
board.displayBoard();
if (board.checkWin() != 0)
{
break;
}
if (turn % 2 == 1)
{
int ai = minimax(9 - (turn % 2), board, true);
std::cout << searches;
std::cin.get();
turn++;
}
}
if (board.checkWin() == 1)
{
std::cout << "You Won" << std::endl;
}
else if (board.checkWin() == -1)
{
std::cout << "You Lost" << std::endl;
}
else
{
std::cout << "Tie" << std::endl;
}
std::cout << "Would You Like To Play Again Y/N: ";
char playAgain;
std::cin >> playAgain;
if (playAgain == 'Y')
{
Board newBoard;
board = newBoard;
game();
}
}
};
int main()
{
TicTacToe ticTacToe;
ticTacToe.game();
}
你知道如何debug吗?如果没有,你绝对应该学习这个,它非常有帮助。但这是我发现的一些事情。
问题不在availableMoves()
,而在minimax()
,更准确地说是在程序调用curBoard. revert(curBoard. availableMoves()[i])
.
的第215行
void revert(int choice)
{
int y = choice / 3;
int x = choice - y * 3;
board[y][x] = (char)choice + 48;
}
for (int i = 0; i < curBoard.availableMoves().size(); i++)
{
curBoard.move(curBoard.availableMoves()[i], 'X');
depth -= 1;
int eval = minimax(depth, curBoard, true);
curBoard.revert(curBoard.availableMoves()[i]);
if (eval < min_eval)
{
min_eval = eval;
}
}
错误发生在函数revert
中,但我不确定为什么。也许 availableMoves
也 returns 有问题。变量 i
在 for 循环中永久为 0。所以有可能向量 moves
的位置 0 有问题,revert
无法处理。尝试自己调试,也许你会发现问题。
我正在尝试创建一个 C++ 无与伦比的井字游戏 AI。看了几个关于这个主题的视频后,我想我已经明白了。屏幕上会弹出一条错误消息“表达式:矢量下标超出范围”。我相信错误来自 availableMoves() 函数。但是我不知道为什么。 游戏本身运行良好。任何帮助将不胜感激。
<pre>#include <iostream>
#include <vector>
#include <ctime>
bool in(std::vector<int> v, int element)
{
for (int i = 0; i < v.size(); i++)
{
if (element == v[i])
{
return true;
}
}
return false;
}
class Board
{
private:
char board[3][3] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'} };
public:
void displayBoard()
{
std::cout << "___________________" << std::endl;
for (int i = 0; i < 3; i++)
{
std::cout << "| ";
for (int j = 0; j < 3; j++)
{
std::cout << board[i][j] << " | ";
}
std::cout << std::endl;
}
std::cout << "___________________" << std::endl;
}
std::vector<int> availableMoves()
{
std::vector<int> moves;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (board[i][j] != 'X' && board[i][j] != 'O')
{
moves.push_back(i * 3 + j);
}
}
}
return moves;
}
void move(int choice, char mark)
{
int y = choice / 3;
int x = choice - y * 3;
board[y][x] = mark;
}
void revert(int choice)
{
int y = choice / 3;
int x = choice - y * 3;
board[y][x] = (char)choice + 48;
}
int checkWin()
{
for (int i = 0; i < 3; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2])
{
if (board[i][0] == 'X')
{
return 1;
}
else if (board[i][0] == 'O')
{
return -1;
}
}
}
for (int i = 0; i < 3; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i])
{
if (board[0][i] == 'X')
{
return 1;
}
else if (board[0][i] == 'O')
{
return -1;
}
}
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2])
{
if (board[0][0] == 'X')
{
return 1;
}
else if (board[0][0] == 'O')
{
return -1;
}
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0])
{
if (board[0][2] == 'X')
{
return 1;
}
else if (board[0][2] == 'O')
{
return -1;
}
}
return 0;
}
int evaluate()
{
return (checkWin() * -1) * (availableMoves().size() + 1);
}
Board& operator=(Board& b)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board[i][j] = b.board[i][j];
}
}
return (*this);
}
};
class TicTacToe
{
private:
Board board;
int turn;
int searches = 0;
public:
TicTacToe()
{
std::srand(time(0));
turn = std::rand() % 2;
}
int minimax(int depth, Board curBoard, bool is_max)
{
searches++;
if (depth == 0 || curBoard.checkWin() != 0)
{
return board.evaluate();
}
if (is_max)
{
int max_eval = -2147483647;
for (int i = 0; i < curBoard.availableMoves().size(); i++)
{
curBoard.move(curBoard.availableMoves()[i], 'O');
depth -= 1;
int eval = minimax(depth, curBoard, false);
curBoard.revert(curBoard.availableMoves()[i]);
if (eval > max_eval)
{
max_eval = eval;
}
}
return max_eval;
}
if (!is_max)
{
int min_eval = 2147483647;
for (int i = 0; i < curBoard.availableMoves().size(); i++)
{
curBoard.move(curBoard.availableMoves()[i], 'X');
depth -= 1;
int eval = minimax(depth, curBoard, true);
curBoard.revert(curBoard.availableMoves()[i]);
if (eval < min_eval)
{
min_eval = eval;
}
}
return min_eval;
}
}
void game()
{
while (board.checkWin() == 0 && board.availableMoves().size() != 0)
{
board.displayBoard();
if (turn % 2 == 0)
{
std::cout << std::endl;
int choice;
std::cout << "Enter Your Move: ";
std::cin >> choice;
choice -= 1;
while (!in(board.availableMoves(), choice))
{
std::cout << "Enter A Valid Move: ";
std::cin >> choice;
}
board.move(choice, 'X');
std::cout << std::endl;
turn++;
}
board.displayBoard();
if (board.checkWin() != 0)
{
break;
}
if (turn % 2 == 1)
{
int ai = minimax(9 - (turn % 2), board, true);
std::cout << searches;
std::cin.get();
turn++;
}
}
if (board.checkWin() == 1)
{
std::cout << "You Won" << std::endl;
}
else if (board.checkWin() == -1)
{
std::cout << "You Lost" << std::endl;
}
else
{
std::cout << "Tie" << std::endl;
}
std::cout << "Would You Like To Play Again Y/N: ";
char playAgain;
std::cin >> playAgain;
if (playAgain == 'Y')
{
Board newBoard;
board = newBoard;
game();
}
}
};
int main()
{
TicTacToe ticTacToe;
ticTacToe.game();
}
你知道如何debug吗?如果没有,你绝对应该学习这个,它非常有帮助。但这是我发现的一些事情。
问题不在availableMoves()
,而在minimax()
,更准确地说是在程序调用curBoard. revert(curBoard. availableMoves()[i])
.
void revert(int choice)
{
int y = choice / 3;
int x = choice - y * 3;
board[y][x] = (char)choice + 48;
}
for (int i = 0; i < curBoard.availableMoves().size(); i++)
{
curBoard.move(curBoard.availableMoves()[i], 'X');
depth -= 1;
int eval = minimax(depth, curBoard, true);
curBoard.revert(curBoard.availableMoves()[i]);
if (eval < min_eval)
{
min_eval = eval;
}
}
错误发生在函数revert
中,但我不确定为什么。也许 availableMoves
也 returns 有问题。变量 i
在 for 循环中永久为 0。所以有可能向量 moves
的位置 0 有问题,revert
无法处理。尝试自己调试,也许你会发现问题。