SebLague / Chess-Challenge

Create your own tiny chess bot!
https://www.youtube.com/watch?v=Ne40a5LkK6A
MIT License
1.77k stars 1.06k forks source link

pieceLists is null. #453

Closed HypixelLimbus closed 1 year ago

HypixelLimbus commented 1 year ago

Sometimes, when the first/last squares of the back rank is empty, the MakeMove function throws an error:

I am aware that other people have experienced the same issue, but i cant seem to find the same issue in my code.

This exception is thrown when the bot tries to think about this position: fen 4r1k1/ppp2pbp/8/5b1Q/4q3/N6P/PPP2P2/R2K1BR1 w KQkq - 0 1

image

using ChessChallenge.API;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;

public class MyBot : IChessBot
{

    IDictionary<PieceType, float> pieceValues = new Dictionary<PieceType, float>();
    float endgameWeight = 0.0F;

    public MyBot()
    {
        pieceValues.Add(PieceType.Pawn, 1.0F);
        pieceValues.Add(PieceType.Knight, 3.0F);
        pieceValues.Add(PieceType.Bishop, 3.1F);
        pieceValues.Add(PieceType.Rook, 5.0F);
        pieceValues.Add(PieceType.Queen, 9.0F);
        pieceValues.Add(PieceType.King, 100.0F);
        pieceValues.Add(PieceType.None, 0.0F);
    }

    public Move Think(Board board, Timer timer)
    {
        Move[] moves = board.GetLegalMoves();
        Move bestMove = Move.NullMove;
        float bestEval = board.IsWhiteToMove ? float.NegativeInfinity : float.PositiveInfinity;
        foreach (Move move in moves)
        {
            board.MakeMove(move);
            float eval = Minimax(board, 3, float.NegativeInfinity, float.PositiveInfinity);

            if ((eval >= bestEval && !board.IsWhiteToMove) || (eval <= bestEval && board.IsWhiteToMove))
            {
                bestEval = eval;
                bestMove = move;
            }
            board.UndoMove(move);
        }
        int count = 0;
        foreach (PieceList pieceList in board.GetAllPieceLists())
        {
            count += pieceList.Count;
        }
        this.endgameWeight = (16 - count) / 160.0F;

        float teval = Minimax(board, 4, float.NegativeInfinity, float.PositiveInfinity);
        Console.WriteLine("Eval: " + teval + " EndgameWeight: " + this.endgameWeight);
        return bestMove == Move.NullMove ? moves[0] : bestMove;
    }

    public float Minimax(Board board, int depth, float alpha, float beta) { 
        if (depth == 0 || board.IsInCheckmate() || board.IsDraw())
        {
            return StaticEvaluation(board);
        }
        if (board.IsWhiteToMove)
        {
            float maxEval = float.NegativeInfinity;
            foreach (Move move in board.GetLegalMoves())
            {
                board.MakeMove(move);
                float eval = Minimax(board, depth - 1, alpha, beta);
                alpha = Math.Max(eval, alpha);
                maxEval = Math.Max(eval, maxEval);
                board.UndoMove(move);
                if (beta <= alpha)
                {
                    break;
                }
            }
            return maxEval;
        }else
        {
            float minEval = float.PositiveInfinity;
            foreach (Move move in board.GetLegalMoves())
            { 
                board.MakeMove(move);
                float eval = Minimax(board, depth - 1, alpha, beta);
                beta = Math.Min(eval, beta);
                minEval = Math.Min(eval, minEval);
                board.UndoMove(move);
                if (beta <= alpha)
                {
                    break;
                }
            }
            return minEval;
        }
    }

    public float StaticEvaluation(Board board)
    {
        float eval = 0.0F;
        Move[] legalMoves = board.GetLegalMoves();
        foreach (Move m in legalMoves)
        {
            if (board.IsWhiteToMove)
            {
                if (board.GetPiece(m.StartSquare).PieceType == PieceType.Queen || board.GetPiece(m.StartSquare).PieceType == PieceType.King && this.endgameWeight < 0) {
                    continue;
                }
                eval += 0.05F / GetPieceValue(board.GetPiece(m.StartSquare).PieceType);
            }else
            {
                if (board.GetPiece(m.StartSquare).PieceType == PieceType.Queen || board.GetPiece(m.StartSquare).PieceType == PieceType.King && this.endgameWeight < 0)
                {
                    continue;
                }
                eval -= 0.05F / GetPieceValue(board.GetPiece(m.StartSquare).PieceType);
            }
        }
        double distanceX = Math.Abs(board.GetKingSquare(true).Rank - board.GetKingSquare(false).Rank);
        double distanceY = Math.Abs(board.GetKingSquare(true).File - board.GetKingSquare(false).File);
        double distanceKings = distanceX + distanceY;

        if (board.IsWhiteToMove)
        {
            eval += (float)distanceKings * endgameWeight;
        }else
        {
            eval -= (float)distanceKings * endgameWeight;
        }

        if (board.IsDraw())
        {
            return 0.0F;
        }
        if (board.IsInCheckmate())
        {
            if (board.IsWhiteToMove)
            {
                eval = -150.0F;
            }else
            {
                eval = 150.0F;
            }
        }
        foreach (PieceList pieceList in board.GetAllPieceLists())
        {
            for (int i = 0; i < pieceList.Count; i++)
            {
                Piece p = pieceList.GetPiece(i);
                eval += p.IsWhite ? GetPieceValue(p.PieceType) : -GetPieceValue(p.PieceType);
            }
        }

        return eval;
    }

    public float GetPieceValue(PieceType type)
    {
        return this.pieceValues[type];
    }

}
HypixelLimbus commented 1 year ago

Turns out the FEN has invalid castle rights...