Open chavchilon opened 1 year ago
hi, have you watched Sebastian's videos? He covers this stuff in depth, however i managed to come up with a function that did just what you want.
I would replace foreach() with for() or added a counter variable to track index of which Move from moves is being evaluated. I would add a variable containing index of current best move. I would replace Math.Max with if(evaluation > bestEvaluation) and if it is true, I would set best move index to current index. Then instead of bestEvaluation I would return best move index.
I would also like to add that my code definitely isn't efficient as i got stuck on looping through the pieces. I first used foreach loop to loop through all the pieces in the board.GetAllPieceLists() and the looped through each piece in each of those lists. Not very good but does the job.
EDIT: This is catastrophically wrong. Closer to working, but still horribly wrong!
Move[] moves = board.GetLegalMoves();
int Search(int depth)
{
if (depth == 0)
{
return Evaluate(board);
}
if (moves.Length == 0)
{
if (board.IsInCheck())
{
return int.MinValue; ;
}
return 0;
}
int bestEvaluation = int.MinValue;
int bestMoveIndex = 0;
for(int i = 0; i < moves.Length; i++)
{
board.MakeMove(moves[i]);
int evaluation = -Search(depth - 1);
if(evaluation > bestEvaluation)
{
bestEvaluation = evaluation;
bestMoveIndex = i;
}
board.UndoMove(moves[i]);
}
return bestMoveIndex;
}
// A few lines later...
board.MakeMove(move[Search(5)]);
Move[] moves = board.GetLegalMoves(); int Search(int depth) { if (depth == 0) { return Evaluate(board); } if (moves.Length == 0) { if (board.IsInCheck()) { return int.MinValue; ; } return 0; } int bestEvaluation = int.MinValue; int bestMoveIndex = 0; for(int i = 0; i < moves.Length; i++) { board.MakeMove(moves[i]); int evaluation = -Search(depth - 1); if(evaluation > bestEvaluation) { bestEvaluation = evaluation; bestMoveIndex = i; } board.UndoMove(moves[i]); } return bestMoveIndex; } // A few lines later... board.MakeMove(move[Search(5)]);
thank you for this, this is exactly what i was looking for however for some reason i am getting this error right at the start of a game
i think this has to do with the move not being undone properly or an illegal move is attempted but i dont see what in the code could have caused this issue.
hi, have you watched Sebastian's videos? He covers this stuff in depth, however i managed to come up with a function that did just what you want.
- Initialize a integer called score
- loop through the pieces on the board
- check what color the pieces are and add if white, take if black
- return score
please could you tell me which video you mean
this video is good for the basic algorithm: ### https://www.youtube.com/watch?v=U4ogK0MIzqk&t=1058s
while this one goes into more depth on different strategies of optimisation: ### https://www.youtube.com/watch?v=_vqlIPDR2TU&t=2565s
I'd watch the top link first, and implement it off of that
I'd watch the top link first, and implement it off of that
that is were i got the code for the search and evaluation algorithm but i cant seem to see what he does with the search function outpur at 15:05
I see, its pretty simple, something like this: `private int Evaluate(Board board) { int score = 0;
foreach (PieceList pieceList in board.GetAllPieceLists())
{
foreach (Piece piece in pieceList)
{
if (piece.IsWhite)
{
if (piece.IsPawn) score += 1;
else if (piece.IsKnight) score += 3;
else if (piece.IsBishop) score += 3;
else if (piece.IsRook) score += 5;
else if (piece.IsQueen) score += 9;
else if (piece.IsKing) score += 20000;
}
else
{
if (piece.IsPawn) score -= 1;
else if (piece.IsKnight) score -= 3;
else if (piece.IsBishop) score -= 3;
else if (piece.IsRook) score -= 5;
else if (piece.IsQueen) score -= 9;
else if (piece.IsKing) score -= 20000;
}
}
}
return score;
}`
And in the video he shows the if statement saying if depth == 0: evaluate(board)
Okay, I made a terrible mistake. Search function must return evaluations when run recursively, but I made it return move indexes. So the logic is utter nonsense inside it.
Exception happens when evaluation returns an int which is out of bounds for moves[] array, but still gets plugged into it.
So if Evaluate returns something like -1, Think function tries to
return moves[-1];
Which causes a crash.
I see, its pretty simple, something like this: `private int Evaluate(Board board) { int score = 0;
foreach (PieceList pieceList in board.GetAllPieceLists()) { foreach (Piece piece in pieceList) { if (piece.IsWhite) { if (piece.IsPawn) score += 1; else if (piece.IsKnight) score += 3; else if (piece.IsBishop) score += 3; else if (piece.IsRook) score += 5; else if (piece.IsQueen) score += 9; else if (piece.IsKing) score += 20000; } else { if (piece.IsPawn) score -= 1; else if (piece.IsKnight) score -= 3; else if (piece.IsBishop) score -= 3; else if (piece.IsRook) score -= 5; else if (piece.IsQueen) score -= 9; else if (piece.IsKing) score -= 20000; } } } return score; }`
i have somthing very similar to this in my code:
public static int Evaluate(Board board)
{
const int pawnValue = 100;
const int knightValue = 250;
const int bishopValue = 300;
const int rookValue = 500;
const int queenValue = 900;
PieceList[] pieces = board.GetAllPieceLists();
int whitePoints = 0;
whitePoints += pieces[0].Count * pawnValue;
whitePoints += pieces[1].Count * knightValue;
whitePoints += pieces[2].Count * bishopValue;
whitePoints += pieces[3].Count * rookValue;
whitePoints += pieces[4].Count * queenValue;
int blackPoints = 0;
blackPoints += pieces[5].Count * pawnValue;
blackPoints += pieces[6].Count * knightValue;
blackPoints += pieces[7].Count * bishopValue;
blackPoints += pieces[8].Count * rookValue;
blackPoints += pieces[9].Count * queenValue;
int eval = whitePoints - blackPoints;
int mapRange = (board.IsWhiteToMove) ? 1: -1;
Console.WriteLine(eval * mapRange);
return eval * mapRange;
}
but i use this in the score function but what would i do with the best evaluation value in order to get the move from it?
Okay, I made a terrible mistake. Search function must return evaluations when run recursively, but I made it return move indexes. So the logic is utter nonsense inside it.
Exception happens when evaluation returns an int which is out of bounds for moves[] array, but still gets plugged into it. So if Evaluate returns something like -1, Think function tries to
return moves[-1];
Which causes a crash.
would you happen to know a solution to this??
No, unfortunately but would you be able to post your whole code so its easier to understand?
No, unfortunately but would you be able to post your whole code so its easier to understand?
ok, here is the whole piece of code:
using ChessChallenge.API; using System; using System.Collections; using System.Collections.Generic;
public class MyBot : IChessBot { public Move Think(Board board, Timer timer) { Move[] captures = board.GetLegalMoves(true); Move[] moves = board.GetLegalMoves();
Move mov = new();
System.Random rnd = new();
//Move[] LondonSystem = {new Move("d2d4",board),new Move("g1f3",board),new Move("c1f4",board),new Move("e2e3",board),new Move("f1d3",board),new Move("b1d2",board),new Move("c2c3",board),};
Move BestMove = mov;
int Search(int depth)
{
if (depth == 0)
{
return Evaluate(board);
}
if (moves.Length == 0)
{
if (board.IsInCheck())
{
return int.MinValue; ;
}
return 0;
}
int bestEvaluation = int.MinValue;
int bestMoveIndex = 0;
for(int i = 0; i < moves.Length; i++)
{
board.MakeMove(moves[i]);
int evaluation = -Search(depth - 1);
if(evaluation > bestEvaluation)
{
bestEvaluation = evaluation;
bestMoveIndex = i;
}
board.UndoMove(moves[i]);
}
return bestMoveIndex;
}
Move bestMove = moves[Search(4)];
return bestMove;
}
public static int Evaluate(Board board)
{
const int pawnValue = 100;
const int knightValue = 250;
const int bishopValue = 300;
const int rookValue = 500;
const int queenValue = 900;
PieceList[] pieces = board.GetAllPieceLists();
int whitePoints = 0;
whitePoints += pieces[0].Count * pawnValue;
whitePoints += pieces[1].Count * knightValue;
whitePoints += pieces[2].Count * bishopValue;
whitePoints += pieces[3].Count * rookValue;
whitePoints += pieces[4].Count * queenValue;
Console.WriteLine("White Score: ", whitePoints);
int blackPoints = 0;
blackPoints += pieces[5].Count * pawnValue;
blackPoints += pieces[6].Count * knightValue;
blackPoints += pieces[7].Count * bishopValue;
blackPoints += pieces[8].Count * rookValue;
blackPoints += pieces[9].Count * queenValue;
Console.WriteLine("Black Score: ", blackPoints);
int eval = whitePoints - blackPoints;
int mapRange = (board.IsWhiteToMove) ? 1: -1;
Console.WriteLine("Evaluation: ", eval * mapRange);
return eval * mapRange;
}
}
there are a couple of things from previous experiments left behind but they do not effect any of the code to my knowledge.
Search returns evaluation of a current position, affected by evaluations of positions that can be created from it to the selected depth. So, to get evaluations for all legal moves of current side, one must make each of the moves, run Search and save the results, in an Array, for example. Then, find the largest evaluation for a move and play corresponding move.
public Move Think(Board board, Timer timer)
{
Move[] moves = board.GetLegalMoves();
// array of the same size as moves array
// index i of evals array contains evaluation of a move at index i in moves array
int[] evals = new int[moves.Length];
// Make move, evaluate the board
// Save evaluation at the same index as played move's index
for(int i = 0; i < moves.Length; i++)
{
board.MakeMove(moves[i]);
evals[i] = Search(1);
board.UndoMove(moves[i]);
}
int Search(int depth)
{
// Legal moves for different board states are different
// Therefore each cycle of recursion must use its own move array
Move[] s_moves = board.GetLegalMoves();
if (depth == 0)
{
return Evaluate(board);
}
int bestEvaluation = int.MinValue;
for(int i = 0; i < s_moves.Length; i++)
{
board.MakeMove(s_moves[i]);
int evaluation = -Search(depth - 1);
bestEvaluation = Math.Max(evaluation, bestEvaluation);
board.UndoMove(s_moves[i]);
}
return bestEvaluation;
}
// Evaluation function
int Evaluate(Board board)
{
// Create your function here
return 0;
}
// Array.IndexOf(some_value) returns index of element with provided value
// evals.Max() returns value of largest element
// I did it like this instead of a simple loop
// to save some tokens
return moves[Array.IndexOf(evals, evals.Max())];
}
@WhiteMouse1 I just tested that on my end and it works. @chavchilon you should be good, here is the full working code:
`using ChessChallenge.API; using System; using System.Collections; using System.Collections.Generic; using System.Linq;
public class MyBot : IChessBot { public Move Think(Board board, Timer timer) { Move[] moves = board.GetLegalMoves();
// array of the same size as moves array
// index i of evals array contains evaluation of a move at index i in moves array
int[] evals = new int[moves.Length];
// Make move, evaluate the board
// Save evaluation at the same index as played move's index
for (int i = 0; i < moves.Length; i++)
{
board.MakeMove(moves[i]);
evals[i] = Search(1);
board.UndoMove(moves[i]);
}
int Search(int depth)
{
// Legal moves for different board states are different
// Therefore each cycle of recursion must use its own move array
Move[] s_moves = board.GetLegalMoves();
if (depth == 0)
{
return Evaluate(board);
}
int bestEvaluation = int.MinValue;
for (int i = 0; i < s_moves.Length; i++)
{
board.MakeMove(s_moves[i]);
int evaluation = -Search(depth - 1);
bestEvaluation = Math.Max(evaluation, bestEvaluation);
board.UndoMove(s_moves[i]);
}
return bestEvaluation;
}
// Evaluation function
int Evaluate(Board board)
{
// Create your function here
return 0;
}
// Array.IndexOf(some_value) returns index of element with provided value
// evals.Max() returns value of largest element
// I did it like this instead of a simple loop
// to save some tokens
return moves[Array.IndexOf(evals, evals.Max())];
}
public static int Evaluate(Board board)
{
const int pawnValue = 100;
const int knightValue = 250;
const int bishopValue = 300;
const int rookValue = 500;
const int queenValue = 900;
PieceList[] pieces = board.GetAllPieceLists();
int whitePoints = 0;
whitePoints += pieces[0].Count * pawnValue;
whitePoints += pieces[1].Count * knightValue;
whitePoints += pieces[2].Count * bishopValue;
whitePoints += pieces[3].Count * rookValue;
whitePoints += pieces[4].Count * queenValue;
Console.WriteLine("White Score: ", whitePoints);
int blackPoints = 0;
blackPoints += pieces[5].Count * pawnValue;
blackPoints += pieces[6].Count * knightValue;
blackPoints += pieces[7].Count * bishopValue;
blackPoints += pieces[8].Count * rookValue;
blackPoints += pieces[9].Count * queenValue;
Console.WriteLine("Black Score: ", blackPoints);
int eval = whitePoints - blackPoints;
int mapRange = (board.IsWhiteToMove) ? 1 : -1;
Console.WriteLine("Evaluation: ", eval * mapRange);
return eval * mapRange;
}
}`
@chavchilon You probably want to look into adding some more in depth (pun intended) alpha beta pruning as i don't think you have any, just minimax at the moment. That is also outlined in his video and he made a whole video on alpha beta pruning with the minimax algorithm here: ### https://www.youtube.com/watch?v=l-hh51ncgDI
@WhiteMouse1 @CaptainDeathead, Thank you guys so much the code now works fine with a couple of tweaks to check whether the bot is white or black. @CaptainDeathead i am now thinking of adding alpha beta tweaking and eval ordering but i just wanted to get the raw min max working. i cant thank you guys enough you have saved me multiple hours or potentially days of head scratching.
I will add a strong warning about signs. Don't mix 'em up or you will end up like my bot, with King always rushing into enemy pieces.
Start with depth 0, "run" code by eyes, then add one depth and make sure everything that must flip, flips correctly.
About Kamikaze King situation: I used "avoid moves that end with checkmate to us" rule lifted and slightly edited from EvilBot, but flipped signs for
if (board.IsInCheckmate()) intEval = int.MinValue;
resulted in potentially fatal moves ending up evaluated as best.
Yeah i seemed to get lucky with mine and it worked, but after seeing Sebastian's second chess video i tried making a chess ai in python c# java and c++ so i kind of flew through this as I'd already done one in C# but my python one was troublesome, it got stuck a lot blundering pieces and not caring about king safety but practice has helped a lot so just keep trying new things out and exploring new ideas. (Also you probably want to close the thread, and good luck!)
i have been trying to implement a simple evaluation into my chess bot and right now i have the search for the best move function return a integer of the highest evaluation possible from each move however i cant quite seem to figure out how to turn that evaluation into the corosponding move that got that evaluation to then play the move.
here is the code for the search function:
any help would be appreciated