Harald wrote:I don't know C# and your ode looks like some ideas shuffled and written down
but I will make some notes. May be I don't see your main problems. However.
aberent wrote:In case someone has the time, here is my AlphaBeta. This is the second version that makes one move at a time, maybe you guys can see a bug?
Code: Select all
internal static int AlphaBetaFast(Board examineBoard, byte depth, int alpha, int beta, ChessPieceColor movingSide, ref int nodesSearched, bool quiescence, bool extended)
//// May be you copy too much boards. Try to use only one copy per call
//// or use a reference to ONE updated board. make_move()/undo_move()
{
nodesSearched++;
if (depth == 0)
//// Use a second function for quiescence. Don't mix it with your main alpha-beta search.
{
//If last move was a capture
if (examineBoard.LastMove.TakenPiece.PieceType != ChessPieceType.None && quiescence == false)
{
//Perform a Quiessence Search
//// I don't understand the connection between last-move-capture and qsearch.
return AlphaBetaFast(examineBoard, 6, alpha, beta, movingSide, ref nodesSearched, true, true);
//// Why is depth 6? Normally depth <= 0 is qsearch indicator and we use
//// depth = 0 or depth - 1 in the calls inside qsearch.
//// Again: dont mix search and qsearch: for better understanding.
}
if (extended == false)
//// What is this extension?
{
if ((examineBoard.BlackCheck || examineBoard.WhiteCheck) && examineBoard.EndGamePhase)
//// If you want to extend checks then only if you are in check
//// (or only when you are giving check) but not both
{
return AlphaBetaFast(examineBoard, 3, alpha, beta, movingSide, ref nodesSearched, false, true);
//// A typical extension would use depth + 1 and not 3.
}
}
//Evaluate Score
Evaluation.EvaluateBoardScore(examineBoard);
//Invert Score to support Negamax
examineBoard.Score = AbsoluteScore(examineBoard.Score, GetOppositeColor(movingSide));
//// AbsoluteScore is misleading. It sounds like abs(). Perhaps you mean
//// sideToMoveDependentScore when your evaluate function always
//// sees score > 0 as good for white?
return examineBoard.Score;
}
if (examineBoard.FiftyMove >= 50 || examineBoard.RepeatedMove >= 3 || examineBoard.StaleMate)
return 0;
//// Perhaps move50 and repetition should be done first in every node.
//// How do you know about stalemate without building a move list?
//// Where do you find out that you are mated and where is the mating score?
//Stand Pad
//// Stand pat.
if (quiescence)
{
//// Again why quiescence flag instead of depth <= 0?
//Evaluate Score
Evaluation.EvaluateBoardScore(examineBoard);
//Invert Score to support Negamax
examineBoard.Score = AbsoluteScore(examineBoard.Score, GetOppositeColor(movingSide));
if (examineBoard.Score >= beta)
return beta;
if (alpha > examineBoard.Score)
alpha = examineBoard.Score;
//// Where do you collect the best move so far?
}
List<Position> positions = EvaluateMoves(movingSide, examineBoard, quiescence);
//// Assuming this is the move generator.
//// Why a list of positions instead a list of moves?
if (positions.Count == 0)
{
//Evaluate Score
Evaluation.EvaluateBoardScore(examineBoard);
//Invert Score to support Negamax
examineBoard.Score = AbsoluteScore(examineBoard.Score, GetOppositeColor(movingSide));
//// See above.
return examineBoard.Score;
//// Is this your place to recognise mates or stalemates?
}
positions.Sort(Sort);
depth--;
//// The usual approach is not to change depth here but in the call below.
foreach (Position move in positions)
{
//Make a copy
Board board = examineBoard.FastCopy();
//// Is this the only board copy or just another one?
//// See comment above.
//Move Piece
Board.MovePiece(board, move.SrcPosition, move.DstPosition);
//// Once you do promotion you will need a promotion piece also.
//We Generate Valid Moves for Board
PieceValidMoves.GenerateValidMoves(board);
//// Ups! Why? This is not the typical place for move generation.
//// See comment on move generator above.
int value = -AlphaBetaFast(board, depth, -beta, -alpha, GetOppositeColor(movingSide), ref nodesSearched,
quiescence, extended);
//// I would expect depth-1 in the call.
if (value >= beta)
{
return beta;
}
if (value > alpha)
alpha = value;
//// Keep track on the best move.
}
//// Here would be a good place to recognise mate.
//// (No best move found and in check).
//// Here you can store something in a hash table when you implement one later.
return alpha;
}
I am sure I found only some obvious problems. When I guessed wrong
that is an indication for a strange way to do something in your code.
Please try to react to some of my comments and in an improved version
we may find more and more serious errors. Have fun.
Harald
Thank you Harald for giving me your comments,
Here are some answers to your questions:
I will sperate the quiescence search as you suggested. Also I only call quiescence when the last move was a capture. Is that wrong? Should I always be ending alpha beta with quiescence. Also I limit quiescence to 6 ply, is that bad, should I just let it go until there are no more captures?
I will also remove the extension for now to keep things easier.
My Eval Function sees >0 As good for White.
The Absolute Score Method that reverses the score looks like this:
Code: Select all
private static int AbsoluteScore(int Score, ChessPieceColor color)
{
if (color == ChessPieceColor.White)
return -Score;
return Score;
}
There is no mate logic yet. I am not sure how to implement it without examining all of the moves ahead of time, which we don't want as far as I understand?
The best move so far is handled outside of this method.
I only copy the board once in:
Code: Select all
Board board = examineBoard.FastCopy();
The following code generates all valid moves for all pieces after the move is made. Is that bad?
Code: Select all
PieceValidMoves.GenerateValidMoves(board);