SebLague / Chess-Challenge

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

Error with the PieceList #231

Closed Nexoduck198 closed 1 year ago

Nexoduck198 commented 1 year ago

Have been getting an error whenever I let my bot play agaisnt evil bot for a while

System.IndexOutOfRangeException
  HResult=0x80131508
  Message=Index was outside the bounds of the array.
  Source=Chess-Challenge
  StackTrace:
   at ChessChallenge.Chess.PieceList.AddPieceAtSquare(Int32 square) in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\Framework\Chess\Board\PieceList.cs:line 29
   at ChessChallenge.Chess.Board.UndoMove(Move move, Boolean inSearch) in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\Framework\Chess\Board\Board.cs:line 344
   at ChessChallenge.API.Board.UndoMove(Move move) in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\API\Board.cs:line 76
   at MyBot.Think(Board b, Timer timer) in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\My Bot\MyBot.cs:line 135
   at ChessChallenge.Application.ChallengeController.GetBotMove() in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\Framework\Application\Core\ChallengeController.cs:line 148
--- End of stack trace from previous location ---
   at ChessChallenge.Application.ChallengeController.Update() in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\Framework\Application\Core\ChallengeController.cs:line 374
   at ChessChallenge.Application.Program.Main() in C:\Users\Batotin\Desktop\Chess-Challenge-main\Chess-Challenge\src\Framework\Application\Core\Program.cs:line 40
Moonwalker316 commented 1 year ago

Please show ur code btw. But my best guess would be that you're using a global Move variable which causes all sorts of issues

Nexoduck198 commented 1 year ago

Heres my code (also what do you mean by global Move? a bit new to coding)


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
struct MoveEval
{
    public Move move = new Move();
    public int eval;

    public MoveEval (Move m, int val)
    {
        move = m;
        eval = val;
    }
}

//latrunculorum
public class MyBot : IChessBot
{
    int prevMovesCount = 2;
    // Piece values: null, pawn, horsie, bishop, rook, queen, king
    List<Square> prevMoves = new List<Square>();
    int[] pieceValues = { 0, 100, 300, 300, 500, 900, 10000 };
    Board board;
    Move bestMove;

    /*
        * Note: 3900 = total amount of points on a starting board
        * 
        * Range of of points for pieces to start moving
        * Pieces won't move if the value is above the min
        * and won't be affected if below the max
        * kinda reversed
        * 
        * Ex:
        * 0 Move | | | 1000 | | Dont move | | | 3900
    */
    //P.s. valu must be within 0 - 3900
    int[][] pieceMoveRange = new int[][]
    {
        new int [] { 3700, 3950 }, //Horsie
        new int [] { 3700, 3950 }, //Bishop
        new int [] { 3000, 3700 }, //Rook
        new int [] { 3400, 3700 }, //Queen
        new int [] { 1000, 2000 }  //King
    };
    //Max amount of Discourage
    public Move Think(Board b, Timer timer)
    {
        board = b;
        Move[] allMoves = board.GetLegalMoves();

        // Pick a random move to play if nothing better is found
        Random rng = new();
        bestMove = allMoves[rng.Next(allMoves.Length)];
        int highestEval = 0;

        foreach (Move move in allMoves)
        {
            int eval = EvaluateGuess(move);

            if (eval > highestEval)
            {
                Console.WriteLine("**Best Move: " + move + " : "  + eval + "** \n");
                bestMove = move;
                highestEval = eval;
            }

            board.UndoMove(move);
        }

        prevMoves.Add(bestMove.StartSquare);
        if(prevMoves.Count > prevMovesCount)
        {
            prevMoves.RemoveAt(prevMovesCount - 1);
        }
        return bestMove;
    }

   /* int Search(int depth, int alpha, int beta)
    {

    }*/

    /*
    int Search(int depth, bool isWhite)
    {
        //Console.WriteLine(move + " : depth-" + depth + " : eval-" + Evaluate(isWhite));
        if (depth <= 0)
        {
            return Evaluate(isWhite);
        }

        Move[] moves = board.GetLegalMoves();
        int maxEval = int.MinValue;
        int minEval = int.MaxValue;
        int eval = 0;
        if(isWhite)
        {
            for (int i = 0; i < moves.Length; i++)
            {
                board.MakeMove(moves[i]);
                eval = -Search(depth - 1, false);
                if(eval > maxEval)
                {
                    maxEval = eval;
                }
                board.UndoMove(moves[i]);
            }

            return maxEval;
        }
        else
        {
            for (int i = 0; i < moves.Length; i++)
            {
                board.MakeMove(moves[i]);
                eval = -Search(depth - 1, true);
                if(eval < minEval)
                {
                    minEval = eval;
                }
                board.UndoMove(moves[i]);
            }

            return minEval;
        }
    }
    */
    int Evaluate()
    {
        int whiteEval = CountMaterial(true);
        int blackEval = CountMaterial(false);

        int evaluation = whiteEval - blackEval;
        int perception = board.IsWhiteToMove ? 1 : -1;
        return evaluation * perception;
    }

    //Evaluate with additional modifiers
    //ex. not sacking its pieces
    int EvaluateGuess(Move move)
    {
        double evalMod = 0;

        //Makes pieces want to move more depending on the pieces left on the board
        int max = 0;
        int min = 0;
        switch (move.MovePieceType)
        {
            case PieceType.Pawn:
                min = 3990;
                max = 4000;
                break;

            case PieceType.Knight:
                max = pieceMoveRange[0][1];
                min = pieceMoveRange[0][0];
                break;

            case PieceType.Bishop:
                max = pieceMoveRange[1][1];
                min = pieceMoveRange[1][0];
                break;

            case PieceType.Rook:
                max = pieceMoveRange[2][1];
                min = pieceMoveRange[2][0];
                break;

            case PieceType.Queen:
                max = pieceMoveRange[3][1];
                min = pieceMoveRange[3][0];
                break;

            case PieceType.King:
                max = pieceMoveRange[4][1];
                min = pieceMoveRange[4][0];
                break;
        }
        double discouragePercent = (Math.Clamp(CountMaterial(board.IsWhiteToMove), min, max) - min) / (max - min);

        //Discourages sacking pieces
        if (board.SquareIsAttackedByOpponent(move.TargetSquare))
        {
            evalMod -= pieceValues[(int)move.MovePieceType];
        }

        board.MakeMove(move);

        //IsSquareAttacked(BitboardHelper.GetPawnAttacks(move.StartSquare, board.IsWhiteToMove));

        // Always play checkmate in one
        if (board.IsInCheckmate())
        {
            evalMod += 99999;
        }

        //Prioritize checks using lower value pieces
        if (board.IsInCheck())
        {
            evalMod += Math.Clamp(1000 - pieceValues[(int)move.MovePieceType], 150, 1000);
        }

        //Encourages moving in protected squares
        if (board.SquareIsAttackedByOpponent(move.TargetSquare))
        {
            evalMod += Math.Clamp(pieceValues[(int)move.MovePieceType], 100, 600);
        }

        if (prevMoves.Count > 0)
        {
            if (prevMoves.Contains(move.TargetSquare))
            {
                evalMod *= .3;
            }
        }

        Console.WriteLine(move + " : " + Evaluate() + "** \n");

        board.UndoMove(move);

        //Discourages moves late game/early game depending on the piece type
        evalMod *= (1 - discouragePercent) + .2;

        return Evaluate() + (int)evalMod;
    }

    int CountMaterial(bool isWhite)
    {
        int material = 0;
        material += board.GetPieceList(PieceType.Pawn, isWhite).Count * pieceValues[1];
        material += board.GetPieceList(PieceType.Knight, isWhite).Count * pieceValues[2];
        material += board.GetPieceList(PieceType.Bishop, isWhite).Count * pieceValues[3];
        material += board.GetPieceList(PieceType.Rook, isWhite).Count * pieceValues[4];
        material += board.GetPieceList(PieceType.Queen, isWhite).Count * pieceValues[5];
        return material;
    }

    bool IsSquareAttacked(ulong bitboard)
    {
       // Console.WriteLine(bitboard);
        return false;
    }

}```
Nexoduck198 commented 1 year ago

Nevermind, turns out it was a lone UndoMove