在我的方法newminimax49中,我有一个minimax算法,它利用了本文中建议给我的记忆和其他一般性改进。该方法使用一个简单的启发式电路板评估函数。我的问题基本上是关于alpha-beta修剪,即我的minimax方法是否使用alpha-beta修剪。据我所知,我相信这是真的,然而,我用来实现它的东西似乎太简单了,不可能是真的。此外,其他人建议我使用alpha-beta剪枝,正如我所说的,我认为我的minimax方法已经做到了,这让我相信我在这里做的是其他事情。这是我的新minimax49:
//This method returns a 2 element int array containing the position of the best possible
//next move and the score it yields. Utilizes memoization and supposedly alpha beta
//pruning to achieve better performance. Alpha beta pruning can be seen in lines such as:
/*if(bestScore==-10)
break;*/
//This basically means that if the best score achieved is the best possible score
//achievable then stop exploring the other available moves. Doing thing I believe
//I'm applying the same principle of alpha beta pruning.
public int[] newminimax49(){
int bestScore = (turn == 'O') ? +9 : -9; //X is minimizer, O is maximizer
int bestPos=-1;
int currentScore;
//boardShow();
String stateString = "";
for (int i=0; i<state.length; i++)
stateString += state[i];
int[] oldAnswer = oldAnswers.get(stateString);
if (oldAnswer != null)
return oldAnswer;
if(isGameOver2()!='N'){
//s.boardShow();
bestScore= score();
}
else{
//s.boardShow();
int i=0;
for(int x:getAvailableMoves()){
if(turn=='X'){ //X is minimizer
setX(x);
//boardShow();
//System.out.println(stateID++);
currentScore = newminimax49()[0];
revert(x);
if(i==0){
bestScore = currentScore;
bestPos=x;
if(bestScore==-10)
break;
}
else if(currentScore<bestScore){
bestScore = currentScore;
bestPos=x;
if(bestScore==-10)
break;
}
}
else { //O is maximizer
setO(x);
//boardShow();
//System.out.println(stateID++);
currentScore = newminimax49()[0];
revert(x);
//boardShow();
if(i==0){
bestScore = currentScore;
bestPos=x;
if(bestScore==10)
break;
}
else if(currentScore>bestScore){
bestScore = currentScore;
bestPos = x;
if(bestScore==10)
break;
}
}
i++;
}
}
int[] answer = {bestScore, bestPos};
oldAnswers.put (stateString, answer);
return answer;
}
在我的类State2中使用的字段和构造函数:
private char [] state; //Actual content of the board
private char turn; //Whose turn it is
private Map<String,int[]> oldAnswers; //Used for memoization. It saves every state along with the score it yielded which allows us to stop exploring the children of a certain node if a similar node's score has been previously calculated. The key is the board state(i.e OX------X for example), the int array is a 2 element array containing the score and position of last placed seed of the state.
private Map<Integer, int []> RowCol; //A mapping of positions from a board represented as a normal array to a board represented as a 2d array. For example: The position 0 maps to 0,0 on a 2d array board, 1 maps to 0,1 and so on.
private static int n; //Size of the board
private static int stateID; //An simple incrementer used to show number of recursive calls in the newminiax49 method.
private static int countX, countO; //Number of placed Xs and Os
private static int lastAdded; //Position of last placed seed
private char [][] DDState; //A 2d array representing the board. Contains the same values as state[]. Used for simplicity in functions that check the state of the board.
public State2(int n){
int a=0;
State2.n=n;
state=new char[n*n];
RowCol=new HashMap<Integer, int []>();
countX=0;
countO=0;
//Initializing the board with empty slots
for(int i = 0; i<state.length; i++){
state[i]='-';
}
//Mapping
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
RowCol.put(a, new int[]{i, j});
a++;
}
}
a=0;
DDState=new char[n][n];
//Initializing the 2d array with the values from state[](empty slots)
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
DDState[i][j]=state[a];
a++;
}
}
oldAnswers = new HashMap<String,int[]>();
}
补充方法:
getAvailableMoves,返回一个带有板上空插槽的数组(即可能的下一步移动)。
public int[] getAvailableMoves(){
int count=0;
int i=0;
for(int j=0; j<state.length; j++){
if(state[j]=='-')
count++;
}
int [] availableSlots = new int[count];
for(int j=0; j<state.length; j++){
if(state[j]=='-')
availableSlots[i++]=j;
}
return availableSlots;
}
isGameOver2(),只需检查棋盘的当前状态以确定游戏是否结束。返回字符“X”、“O”、“D”和“N”,分别代表X赢、O赢、平局和非gameover。
public char isGameOver2(){
char turnOpp;
int count;
if(turn=='X'){
count=countO;
turnOpp='O';
}
else {
count=countX;
turnOpp='X';
}
if(count>=n){
//^No win available if each player has less than n seeds on the board
//Checking begins
//DDState[RowCol.get(lastAdded)[0]][RowCol.get(lastAdded)[1]]=turn;
//Check column for win
for(int i=0; i<n; i++){
if(DDState[i][RowCol.get(lastAdded)[1]]!=turnOpp)
break;
if(i==(n-1)){
//DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
return turnOpp;
}
}
//Check row for win
for(int i=0; i<n; i++){
if(DDState[RowCol.get(lastAdded)[0]][i]!=turnOpp)
break;
if(i==(n-1)){
//DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
return turnOpp;
}
}
//Check diagonal for win
if(RowCol.get(lastAdded)[0] == RowCol.get(lastAdded)[1]){
//we're on a diagonal
for(int i = 0; i < n; i++){
if(DDState[i][i] != turnOpp)
break;
if(i == n-1){
//DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
return turnOpp;
}
}
}
//check anti diagonal
for(int i = 0; i<n; i++){
if(DDState[i][(n-1)-i] != turnOpp)
break;
if(i == n-1){
//DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
return turnOpp;
}
}
//check for draw
if((countX+countO)==(n*n))
return 'D';
}
return 'N';
}
BoardShow,返回板子当前状态的矩阵显示:
public void boardShow(){
if(n==3){
System.out.println(stateID);
for(int i=0; i<=6;i+=3)
System.out.println("["+state[i]+"]"+" ["+state[i+1]+"]"+" ["+state[i+2]+"]");
System.out.println("***********");
}
else {
System.out.println(stateID);
for(int i=0; i<=12;i+=4)
System.out.println("["+state[i]+"]"+" ["+state[i+1]+"]"+" ["+state[i+2]+"]"+" ["+state[i+3]+"]");
System.out.println("***********");
}
}
分数是一个简单的评估函数,O赢返回10,X赢返回10,平局返回0:
public int score(){
if(isGameOver2()=='X')
return -10;
else if(isGameOver2()=='O')
return +10;
else
return 0;
}
种子设定者:
//Sets an X at a certain location and updates the turn, countX and lastAdded variables
public void setX(int i){
state[i]='X';
DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='X';
turn='O';
countX++;
lastAdded=i;
}
//Sets an O at a certain location and updates the turn, countO and lastAdded variables
public void setO(int i){
state[i]='O';
DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='O';
turn='X';
countO++;
lastAdded=i;
}
恢复,简单地恢复移动。例如,如果一个X被放置在位置0还原(0)设置一个'-'在它的地方,并更新由setX更改的变量:
public void revert(int i){
state[i]='-';
DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='-';
if(turn=='X'){
turn = 'O';
countO--;
}
else {
turn = 'X';
countX--;
}
}
这看起来像是阿尔法-贝塔修剪吗?如果不是的话,我该怎么做?
你已经在使用某种“简化”的Alpha-Beta:目前,每当一个玩家找到一个获胜的位置时,你都在删减。
一个合适的AB会给自己传递一个阿尔法和贝塔值,以分别确定玩家将达到的最小最大值。在那里,每当分数比对方玩家当前的“最坏情况”更差或相等时,你就会修剪。
在您的情况下,您将不仅能够删减获胜分数(如您目前所做的),还可以删减某些分数为0的分数。
我试图在我的极小值中添加阿尔法贝塔修剪,但我不明白我哪里出错了。 目前,我正在经历5000次迭代,根据一个朋友的说法,我应该经历大约16000次迭代。当选择第一个位置时,它返回-1(一个损失),而它应该能够在这一点上肯定返回0(一个平局),因为它应该能够从一个空的棋盘中抽签,但是我看不到我哪里出错了,因为我跟着我的代码走似乎没问题 奇怪的是,如果我在我的检查中切换返回α和β(以实现返回0),计算机
我正在用Java做一个国际象棋游戏,并且(我认为)已经成功地为AI玩家实现了Negamax。我在添加阿尔法贝塔剪枝来改进算法时遇到了一些麻烦。我已经尝试了下面的教程和示例代码,但就是不明白它是如何工作的。 以下是我目前必须获得最佳移动的代码: 这是我尝试将aplha-beta修剪添加到我的(工作)内切方法中: 最后是控制台的外观 任何帮助都将不胜感激。提前感谢。
我很难让Alpha-beta修剪正常工作。我有一个函数Minimax算法,我试着去适应,但没有用。我在维基百科上用了这个例子 目前,该算法似乎在大多数情况下都按预期运行,但不管怎样,它都会选择第一个测试节点。 这可能是因为缺乏理解,但我已经花了数小时阅读了这篇文章。让我困惑的是,在零和博弈中,算法如何知道当达到深度极限时哪个节点是最佳选择;在哪一点上,我们还不能确定哪位球员会从这样的举动中受益最大
我试图在我的negamax中实现转置表。但首先我想理解伪代码中的所有思想: ' 函数 negamax(节点, 深度, α, β, 颜色) 是 alphaOrig := α 但我想知道的一件事是旗帜是什么?喜欢、和?
我已经实现了一个带有alpha beta修剪的NegaMax算法(这只是一个较短版本的极小值算法)。现在我想实现迭代深化,这样我就可以为每个深度找到最佳移动,然后根据之前层的分数重新排序树下的节点,以便我的alphabeta修剪工作更有效。 以下是我迄今为止所做的工作: 这里gs是随每一步移动而变化的游戏属性,包含了所有关于游戏在t点的信息,比如是否可以施法或者是否有可能的内移。我的egamax算
我有一个Tic Tac Toe游戏,它使用了极大极小算法。我想通过添加alpha-beta修剪来改进这一点。然而,阿尔法-贝塔法似乎无法有效地计算移动。它只是把它的一块放在下一个可用的空间里,不管它是不是最佳的移动。我对极小极大法没有这个问题。我确信这是我一直忽略的一些简单的事情,所以请原谅我。我用这个教程来学习minimax,用这个教程来学习alpha-beta修剪。 这是极小极大类。它包括阿尔