在極小的簡單版本,第一個玩家希望最大限度地發揮他的得分,第二個玩家希望最小化第一玩家的分數。 由於第一和第二的球員只關心第一個球員的得分,EvaluateStaticPosition
應該返回指示板狀態有多好,是第一個球員的值。這是不是相關的。
int EvaluateStaticPosition(stateT state)
{
if(CheckForWin(state, FIRST_PLAYER))
{
return WINNING_POSITION;
}
if(CheckForWin(state, Opponent(FIRST_PLAYER)))
{
return LOSING_POSITION;
}
return NEUTRAL_POSITION;
}
現在,當您想要對第一個玩家最好的移動時,請致電MaxMove。當你想要對第二個玩家最好的移動時,請致電MinMove。
moveT MiniMax(stateT state)
{
moveT bestMove;
int i = 0;
if (state.whoseTurn == FIRST_PLAYER){
i = MaxMove(state, bestMove);
}
else{
i = MinMove(state,bestMove);
}
cout<<"i is "<<i<<endl;
return bestMove;
}
最後,你有MinMove
和MaxMove
裏面的一些問題。當你在任何一個指定curRating
,你不應該在bestMove
傳遞的第二個參數來MaxMove
或MinMove
。然後它將把對手的最佳進軍bestMove
,這沒有任何意義。相反,聲明一個opponentsBestMove
對象並將其作爲第二個參數傳遞。 (你實際上不會使用這個對象,或者甚至沒有看到它的值,但沒關係)。隨着這一變化,你從來不會在MinMove
內分配任何東西給bestMove
,所以你應該在if(curRating < v)
塊內部這樣做。
int MaxMove(stateT state, moveT &bestMove)
{
if(GameIsOver(state))
{
return EvaluateStaticPosition(state);
}
vector<moveT> moveList;
GenerateMoveList(state, moveList);
int nMoves = moveList.size();
int v = -1000;
for(int i = 0 ;i<nMoves; i++)
{
moveT move = moveList[i];
MakeMove(state, move);
moveT opponentsBestMove;
int curRating = MinMove(state, opponentsBestMove);
if (curRating > v)
{
v = curRating;
bestMove = move;
}
RetractMove(state, move);
}
return v;
}
int MinMove(stateT state, moveT &bestMove)
{
if(GameIsOver(state))
{
return EvaluateStaticPosition(state);
}
vector<moveT>moveList;
GenerateMoveList(state, moveList);
int nMoves = moveList.size();
int v = 1000;
for(int i = 0 ; i<nMoves; i++)
{
moveT move = moveList[i];
MakeMove(state , move);
moveT opponentsBestMove;
int curRating = MaxMove(state,opponentsBestMove);
if(curRating < v)
{
v = curRating;
bestMove = move;
}
RetractMove(state, move);
}
return v;
}
在這一點上,你應該有一個無與倫比的AI!
The final position looks like this:
O | O | X
---+---+---
X | X | O
---+---+---
O | X | X
Cat's game.
一種替代方法利用這樣的事實即井字棋是一種零和博弈優勢。換句話說,在遊戲結束時,玩家的總分將等於零。對於雙人遊戲,這意味着一個玩家的得分永遠是其他玩家的負值。這對我們來說很方便,因爲最小化其他玩家的分數與最大化自己的分數相同。所以不是一個玩家最大化他的分數,而是一個玩家最小化其他玩家的分數,我們可以讓兩個玩家都試圖最大化他們自己的分數。
變化EvaluateStaticPosition
恢復到原來的形狀,所以,它給基於董事會狀態是當前球員多麼優秀的得分。
int EvaluateStaticPosition(stateT state)
{
if(CheckForWin(state, state.whoseTurn))
{
return WINNING_POSITION;
}
if(CheckForWin(state, Opponent(state.whoseTurn)))
{
return LOSING_POSITION;
}
return NEUTRAL_POSITION;
}
刪除MinMove
,因爲我們只關心最大化。 重寫MaxMove
,以便它選擇讓對手得到最差分數的動作。最佳動作的分數是其他玩家最差分數的負值。
int MaxMove(stateT state, moveT &bestMove)
{
if(GameIsOver(state))
{
return EvaluateStaticPosition(state);
}
vector<moveT> moveList;
GenerateMoveList(state, moveList);
int nMoves = moveList.size();
int v = -1000;
for(int i = 0 ;i<nMoves; i++)
{
moveT move = moveList[i];
MakeMove(state, move);
moveT opponentsBestMove;
int curRating = -MaxMove(state, opponentsBestMove);
if (curRating > v)
{
v = curRating;
bestMove = move;
}
RetractMove(state, move);
}
return v;
}
由於MaxMove
用於兩個球員,我們不再需要玩家在MiniMax
功能之間進行區分。
moveT MiniMax(stateT state)
{
moveT bestMove;
int i = 0;
i = MaxMove(state, bestMove);
cout<<"i is "<<i<<endl;
return bestMove;
}
是您發佈上面的代碼跟上時代的?因爲它看起來不像應該編譯。在'min_move'中,你用三個參數調用'max_move',但max_move只能有兩個參數。 – Kevin
@Kevin:糟糕,它現在已更新。我試圖在某個時候限制深度。 – motiur
感謝您的更新,但不正確的行仍然存在:'int curValue = max_move(state,depth + 1,bestMove);'這讓我擔心;它讓我懷疑你發佈的代碼不是你正在編譯的代碼。這使潛在的回答者發現問題變得更具挑戰性。我們將在發佈的代碼中確定實際代碼中不存在的錯誤,如果它們不在發佈的代碼中,我們將無法找到真實代碼中的錯誤。 – Kevin