Tiny Chess Bot Coding Challenge

Discussion of chess software programming and technical issues.

Moderator: Ras

Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

I am not a C# programmer and I already solved it a different way. Please feel free to post an improved version. It is quite snappy but not snappy enough. It was just done for NegaQ. Now I need to do some move ordering for NegaMax.

Code: Select all

using ChessChallenge.API;

public class MyBot : IChessBot
{
    //Implements the https://en.wikipedia.org/wiki/Negamax algorithm

    // Piece values: null, pawn, knight, bishop, rook, queen
    int[] PieceValues = { 0, 100, 320, 330, 500, 900, 10000 };
    int CheckmateScore = 9999;
    int Depth = 4;

    ulong[] pst = {
        0xE6F4B06438321400,0xEAF6B2643A341400,0xEAF2B2643A361400,0xEAF0B3653A361400,
        0xEAF0B3653A361400,0xEAF2B2643A361400,0xEAF6B2643A341400,0xE6F4B06438321400,
        0xEAF4B2633A341500,0xEAF4B4643D381600,0xF0F0B5643C3C1600,0xF0F0B4643C3D1000,
        0xF0F0B4643C3D1000,0xF0F0B4643C3C1600,0xEAF4B4643D381600,0xEAF4B2633A341500,
        0xEAEEB2633A361500,0xEEECB5643E3D1300,0xF4ECB5643E3E1200,0xF6ECB5643E3F1400,
        0xF6ECB5643E3F1400,0xF4ECB5643E3E1200,0xEEECB4643E3D1300,0xEAEEB2633A361500,
        0xEAECB4633A361400,0xEEEAB4643C3C1400,0xF6EAB5643E3F1400,0xF8E8B5643E401800,
        0xF8E8B5643E401800,0xF6EAB5643E3F1400,0xEEEAB4643C3C1400,0xEAECB3633A361400,
        0xEAEAB3633A361500,0xEEE8B4643D3D1500,0xF6E8B5643D3F1600,0xF8E6B5643E401900,
        0xF8E6B5643E401900,0xF6E8B5643D3F1600,0xEEE8B4643D3D1500,0xEAEAB3633A361500,
        0xEAEAB2633A361600,0xEEE8B4643C3C1600,0xF4E8B5643D3E1800,0xF6E6B5643E3F1A00,
        0xF6E6B5643E3F1A00,0xF4E8B5643D3E1800,0xEEE8B4643C3C1600,0xEAEAB2633A361600,
        0xEAEAB2653A341E00,0xECE8B4663C381E00,0xEEE8B4663C3C1E00,0xF0E6B4663C3C1E00,
        0xF0E6B4663C3C1E00,0xEEE8B4663C3C1E00,0xECE8B4663C381E00,0xEAEAB2653A341E00,
        0xE6EAB06438321400,0xE8E8B2643A341400,0xEAE8B2643A361400,0xECE6B3643A361400,
        0xECE6B3643A361400,0xEAE8B2643A361400,0xE8E8B2643A341400,0xE6EAB06438321400,
    };

    public Move Think(Board board, Timer timer)
    {
        Move bestMove = default;
        int bestScore = -CheckmateScore;
        int alpha = -CheckmateScore;
        int beta = CheckmateScore;
        foreach (Move move in board.GetLegalMoves())
        {
            board.MakeMove(move);
            int score = -NegaMax(-beta, -alpha, 1, board);
            board.UndoMove(move);

            if (score > bestScore)
            {
                bestMove = move;
                bestScore = alpha = score;
            }
        }
        return bestMove;
    }

    private int NegaQ(int alpha, int beta, Board board)
    {
        int eval = Eval(board);
        if (eval >= beta) return beta;
        if (eval > alpha) alpha = eval;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            if (move[i].IsCapture || move[i].IsPromotion || move[i].IsEnPassant)
            {
                Move tempMove;
                Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
                int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
                for (int j = i + 1; j < move.Length; j++)
                {
                    Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                    int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                    if (capturedPieceValue2 > capturedPieceValue1)
                    {
                        tempMove = move[i];
                        move[i] = move[j];
                        move[j] = tempMove;
                    }
                }

                board.MakeMove(move[i]);
                int score = -NegaQ(-beta, -alpha, board);
                board.UndoMove(move[i]);

                if (score > alpha)
                {
                    alpha = score;
                    if (score >= beta)
                        return beta;
                }
            }
        }
        return alpha;
    }

    private int NegaMax(int alpha, int beta, int depth, Board board)
    {
        if (depth == Depth)
            return NegaQ(alpha, beta, board);

        if (board.IsInCheckmate())
            return -CheckmateScore;

        int bestScore = -CheckmateScore;
        foreach (Move move in board.GetLegalMoves())
        {
            board.MakeMove(move);
            int score = -NegaMax(-beta, -alpha, depth + 1, board);
            board.UndoMove(move);

            if (score > alpha)
            {
                alpha = score;
                if (score >= beta)
                    return beta;
            }
        }
        return alpha;
    }

    int Eval(Board board)
    {
        int eval = 0, value;
        foreach (PieceList list in board.GetAllPieceLists())
            foreach (Piece piece in list)
            {
                value = (byte)(pst[piece.Square.Index ^ (piece.IsWhite ? 0 : 0x38)] >>
                    ((int)piece.PieceType << 3));
                eval -= (board.IsWhiteToMove ^ piece.IsWhite) ? value : -value;
            }
        return eval;
    }
}
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

This is now working really well. Fixed another bug. Added crude move ordering in NegaMax. Raised requested depth to 8. Added Iterative deepening. I feel some of the changes are kludges that can be done better. Wins every game against Evil Bot! And plays pretty damn good for what it is. If one thinks they can make it play better then get involved.

Code: Select all

// TalkChess Bot

// contributors:
// Thomas Jahn - Leorik
// Mike Sherwin - RomiChess
// Dmitry Shechtman - LeanChess
// (Get your name added here)
// Deadline October 1st

using ChessChallenge.API;

public class MyBot : IChessBot
{
    //Implements the https://en.wikipedia.org/wiki/Negamax algorithm

    // Piece values: null, pawn, knight, bishop, rook, queen
    int[] PieceValues = { 0, 100, 320, 330, 500, 900, 10000 };
    int CheckmateScore = 9999;
    int Depth = 8;

    ulong[] pst = {
        0xE6F4B06438321400,0xEAF6B2643A341400,0xEAF2B2643A361400,0xEAF0B3653A361400,
        0xEAF0B3653A361400,0xEAF2B2643A361400,0xEAF6B2643A341400,0xE6F4B06438321400,
        0xEAF4B2633A341500,0xEAF4B4643D381600,0xF0F0B5643C3C1600,0xF0F0B4643C3D1000,
        0xF0F0B4643C3D1000,0xF0F0B4643C3C1600,0xEAF4B4643D381600,0xEAF4B2633A341500,
        0xEAEEB2633A361500,0xEEECB5643E3D1300,0xF4ECB5643E3E1200,0xF6ECB5643E3F1400,
        0xF6ECB5643E3F1400,0xF4ECB5643E3E1200,0xEEECB4643E3D1300,0xEAEEB2633A361500,
        0xEAECB4633A361400,0xEEEAB4643C3C1400,0xF6EAB5643E3F1400,0xF8E8B5643E401800,
        0xF8E8B5643E401800,0xF6EAB5643E3F1400,0xEEEAB4643C3C1400,0xEAECB3633A361400,
        0xEAEAB3633A361500,0xEEE8B4643D3D1500,0xF6E8B5643D3F1600,0xF8E6B5643E401900,
        0xF8E6B5643E401900,0xF6E8B5643D3F1600,0xEEE8B4643D3D1500,0xEAEAB3633A361500,
        0xEAEAB2633A361600,0xEEE8B4643C3C1600,0xF4E8B5643D3E1800,0xF6E6B5643E3F1A00,
        0xF6E6B5643E3F1A00,0xF4E8B5643D3E1800,0xEEE8B4643C3C1600,0xEAEAB2633A361600,
        0xEAEAB2653A341E00,0xECE8B4663C381E00,0xEEE8B4663C3C1E00,0xF0E6B4663C3C1E00,
        0xF0E6B4663C3C1E00,0xEEE8B4663C3C1E00,0xECE8B4663C381E00,0xEAEAB2653A341E00,
        0xE6EAB06438321400,0xE8E8B2643A341400,0xEAE8B2643A361400,0xECE6B3643A361400,
        0xECE6B3643A361400,0xEAE8B2643A361400,0xE8E8B2643A341400,0xE6EAB06438321400,
    };

    public Move Think(Board board, Timer timer)
    {
        Move bestMove = default;
        int bestScore = -CheckmateScore;
        int alpha = -CheckmateScore;
        int beta = CheckmateScore;
        int score = 0;
        foreach (Move move in board.GetLegalMoves())
        {
            board.MakeMove(move);
            if (board.IsInCheckmate())
            {
                board.UndoMove(move);
                return move;
            }
            if (board.IsDraw())
            {
                score = 0;
            }
            else
            {
                for (int i = 8; i > 0; i--)
                {
                    score = -NegaMax(-beta, -alpha, i, board);
                    if (timer.MillisecondsElapsedThisTurn > 200) break;
                }
            }
            board.UndoMove(move);

            if (score > bestScore)
            {
                bestMove = move;
                bestScore = alpha = score;
            }
        }
        return bestMove;
    }

    private int NegaQ(int alpha, int beta, Board board)
    {
        int eval = Eval(board);
        if (eval >= beta) return beta;
        if (eval > alpha) alpha = eval;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            if (move[i].IsCapture || move[i].IsPromotion || move[i].IsEnPassant)
            {
                Move tempMove;
                Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
                int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
                for (int j = i + 1; j < move.Length; j++)
                {
                    Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                    int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                    if (capturedPieceValue2 > capturedPieceValue1)
                    {
                        tempMove = move[i];
                        move[i] = move[j];
                        move[j] = tempMove;
                    }
                }

                board.MakeMove(move[i]);
                if (board.IsInCheckmate())
                {
                    board.UndoMove(move[i]);
                    return CheckmateScore - board.PlyCount;
                }
                if (board.IsDraw())
                {
                    board.UndoMove(move[i]);
                    return 0;
                }
                int score = -NegaQ(-beta, -alpha, board);
                board.UndoMove(move[i]);

                if (score > alpha)
                {
                    alpha = score;
                    if (score >= beta)
                        return beta;
                }
            }
        }
        return alpha;
    }

    private int NegaMax(int alpha, int beta, int depth, Board board)
    {
        int score;
        if (depth >= Depth)
            return NegaQ(alpha, beta, board);

        int bestScore = -CheckmateScore;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            Move tempMove;
            Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
            int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
            for (int j = i + 1; j < move.Length; j++)
            {
                Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                if (capturedPieceValue2 > capturedPieceValue1)
                {
                    tempMove = move[i];
                    move[i] = move[j];
                    move[j] = tempMove;
                }
            }

            board.MakeMove(move[i]);
            if (board.IsInCheckmate())
            {
                board.UndoMove(move[i]);
                return CheckmateScore - board.PlyCount;
            }
            if (board.IsDraw()) {
                board.UndoMove(move[i]);
                return 0;
            }
           if (beta - alpha > 1)
            {
                score = -NegaMax(-alpha - 1, -alpha, depth + 2, board);
                if (score > alpha)
                    score = -NegaMax(-beta, -alpha, depth + 1, board);
            }
            else
            {
                score = -NegaMax(-beta, -alpha, depth + 1, board);
            }
            board.UndoMove(move[i]);

            if (score > alpha)
            {
                alpha = score;
                if (score >= beta)
                    return beta;
            }
        }
        return alpha;
    }

    int Eval(Board board)
    {
        int eval = 0, value;
        foreach (PieceList list in board.GetAllPieceLists())
            foreach (Piece piece in list)
            {
                value = (byte)(pst[piece.Square.Index ^ (piece.IsWhite ? 0 : 0x38)] >>
                    ((int)piece.PieceType << 3));
                eval -= (board.IsWhiteToMove ^ piece.IsWhite) ? value : -value;
            }
        return eval;
    }
}
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

[pgn][White "Mike Sherwin"] [Black "MyBot"] [Result "BlackIsMated"] 1. d4 Nc6 2. d5 Ne5 3. e4 Nf6 4. Nc3 d6 5. h3 e6 6. f4 Ng6 7. dxe6 Bxe6 8. Nf3 d5 9. e5 Ne4 10. Nxe4 dxe4 11. Qxd8+ Kxd8 12. Nd4 Bc5 13. Nxe6+ fxe6 14. Ke2 Kc8 15. Be3 Bxe3 16. Kxe3 Rf8 17. g3 Kb8 18. Bc4 Kc8 19. Bxe6+ Kd8 20. Rad1+ Ke8 21. Rd4 Rd8 22. Rhd1 Rxd4 23. Rxd4 Rh8 24. Kxe4 Kf8 25. f5 Ne7 26. Rd8# [/pgn]
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

I entered TalkChess bot into the contest. It can be updated upto the deadline Oct 1st. Help make TalkChess look good. Contribute! :D
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

Iterative search was not quite right. It is amazing it played so well. It is fixed and it now remembers the best move from the previous iteration and puts it in move[0] to be played first. That is if I did it correctly. The biggest problem with the code is I'm wasting tokens (829/1024). Someone that knows C# better than me needs to optimize the code to reduce the token count.

Code: Select all

using ChessChallenge.API;

public class MyBot : IChessBot
{
    //Implements the https://en.wikipedia.org/wiki/Negamax algorithm

    // Piece values: null, pawn, knight, bishop, rook, queen
    int[] PieceValues = { 0, 100, 320, 330, 500, 900, 10000 };
    int CheckmateScore = 9999;
    int Depth;

    ulong[] pst = {
        0xE6F4B06438321400,0xEAF6B2643A341400,0xEAF2B2643A361400,0xEAF0B3653A361400,
        0xEAF0B3653A361400,0xEAF2B2643A361400,0xEAF6B2643A341400,0xE6F4B06438321400,
        0xEAF4B2633A341500,0xEAF4B4643D381600,0xF0F0B5643C3C1600,0xF0F0B4643C3D1000,
        0xF0F0B4643C3D1000,0xF0F0B4643C3C1600,0xEAF4B4643D381600,0xEAF4B2633A341500,
        0xEAEEB2633A361500,0xEEECB5643E3D1300,0xF4ECB5643E3E1200,0xF6ECB5643E3F1400,
        0xF6ECB5643E3F1400,0xF4ECB5643E3E1200,0xEEECB4643E3D1300,0xEAEEB2633A361500,
        0xEAECB4633A361400,0xEEEAB4643C3C1400,0xF6EAB5643E3F1400,0xF8E8B5643E401800,
        0xF8E8B5643E401800,0xF6EAB5643E3F1400,0xEEEAB4643C3C1400,0xEAECB3633A361400,
        0xEAEAB3633A361500,0xEEE8B4643D3D1500,0xF6E8B5643D3F1600,0xF8E6B5643E401900,
        0xF8E6B5643E401900,0xF6E8B5643D3F1600,0xEEE8B4643D3D1500,0xEAEAB3633A361500,
        0xEAEAB2633A361600,0xEEE8B4643C3C1600,0xF4E8B5643D3E1800,0xF6E6B5643E3F1A00,
        0xF6E6B5643E3F1A00,0xF4E8B5643D3E1800,0xEEE8B4643C3C1600,0xEAEAB2633A361600,
        0xEAEAB2653A341E00,0xECE8B4663C381E00,0xEEE8B4663C3C1E00,0xF0E6B4663C3C1E00,
        0xF0E6B4663C3C1E00,0xEEE8B4663C3C1E00,0xECE8B4663C381E00,0xEAEAB2653A341E00,
        0xE6EAB06438321400,0xE8E8B2643A341400,0xEAE8B2643A361400,0xECE6B3643A361400,
        0xECE6B3643A361400,0xEAE8B2643A361400,0xE8E8B2643A341400,0xE6EAB06438321400,
    };

    public Move Think(Board board, Timer timer)
    {
        Move bestMove = default;
        int bestScore;
        int alpha = -CheckmateScore;
        int beta = CheckmateScore;
        int score;
        Move[] move = board.GetLegalMoves();
        for (Depth = 1; Depth < 9; Depth++)
        {
            bestScore = -CheckmateScore;
            bestMove = default;
            for (int i = 0; i < move.Length; i++)
            {
                board.MakeMove(move[i]);
                if (board.IsInCheckmate())
                {
                    board.UndoMove(move[i]);
                    return move[i];
                }
                if (board.IsDraw())
                {
                    score = 0;
                }
                else score = -NegaMax(-beta, -alpha, 1, board);
                board.UndoMove(move[i]);

                if (score > bestScore)
                {
                    bestMove = move[i];
                    move[i] = move[0];
                    move[0] = bestMove;
                    bestScore = score;
                }
            }
            if (timer.MillisecondsElapsedThisTurn > 400) break;
        }
        return bestMove;
    }

    private int NegaQ(int alpha, int beta, Board board)
    {
        int eval = Eval(board);
        if (eval >= beta) return beta;
        if (eval > alpha) alpha = eval;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            if (move[i].IsCapture || move[i].IsPromotion || move[i].IsEnPassant)
            {
                Move tempMove;
                Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
                int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
                for (int j = i + 1; j < move.Length; j++)
                {
                    Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                    int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                    if (capturedPieceValue2 > capturedPieceValue1)
                    {
                        tempMove = move[i];
                        move[i] = move[j];
                        move[j] = tempMove;
                    }
                }

                board.MakeMove(move[i]);
                if (board.IsInCheckmate())
                {
                    board.UndoMove(move[i]);
                    return CheckmateScore - board.PlyCount;
                }
                if (board.IsDraw())
                {
                    board.UndoMove(move[i]);
                    return 0;
                }
                int score = -NegaQ(-beta, -alpha, board);
                board.UndoMove(move[i]);

                if (score > alpha)
                {
                    alpha = score;
                    if (score >= beta)
                        return beta;
                }
            }
        }
        return alpha;
    }

    private int NegaMax(int alpha, int beta, int depth, Board board)
    {
        int score;
        if (depth >= Depth)
            return NegaQ(alpha, beta, board);

        int bestScore = -CheckmateScore;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            Move tempMove;
            Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
            int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
            for (int j = i + 1; j < move.Length; j++)
            {
                Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                if (capturedPieceValue2 > capturedPieceValue1)
                {
                    tempMove = move[i];
                    move[i] = move[j];
                    move[j] = tempMove;
                }
            }

            board.MakeMove(move[i]);
            if (board.IsInCheckmate())
            {
                board.UndoMove(move[i]);
                return CheckmateScore - board.PlyCount;
            }
            if (board.IsDraw()) {
                board.UndoMove(move[i]);
                return 0;
            }
           if (beta - alpha > 1)
            {
                score = -NegaMax(-alpha - 1, -alpha, depth + 2, board);
                if (score > alpha)
                    score = -NegaMax(-beta, -alpha, depth + 1, board);
            }
            else
            {
                score = -NegaMax(-beta, -alpha, depth + 1, board);
            }
            board.UndoMove(move[i]);

            if (score > alpha)
            {
                alpha = score;
                if (score >= beta)
                    return beta;
            }
        }
        return alpha;
    }

    int Eval(Board board)
    {
        int eval = 0, value;
        foreach (PieceList list in board.GetAllPieceLists())
            foreach (Piece piece in list)
            {
                value = (byte)(pst[piece.Square.Index ^ (piece.IsWhite ? 0 : 0x38)] >>
                    ((int)piece.PieceType << 3));
                eval -= (board.IsWhiteToMove ^ piece.IsWhite) ? value : -value;
            }
        return eval;
    }
}
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

Anti team needed to make a better Evil Bot to test against. Both will be submitted. 8-)

Someone start an Evil Bot thread! :idea:

I'll start the thread.
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

At least I can report that the Bot is free of any major bugs.

Game 1000 of 1000

MyBot:
Score: +1000 =0 -0
Num Timeouts: 0
Num Illegal Moves: 0

Evil Bot:
Score: +0 =0 -1000
Num Timeouts: 0
Num Illegal Moves: 0
User avatar
leanchess
Posts: 181
Joined: Sun Dec 08, 2019 8:16 pm
Full name: Dmitry Shechtman

Re: Tiny Chess Bot Coding Challenge

Post by leanchess »

Have you tried uploading it to the live tournament? It's getting quite competitive (and depth 8 won't get you very far ;))
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

Final source for tonight. Made time usage a little better.

Code: Select all

// TalkChess Bot

// contributors:
// Thomas Jahn - Leorik
// Mike Sherwin - RomiChess
// Dmitry Shechtman - LeanChess
// (Get your name added here)
// Deadline October 1st 2023

using ChessChallenge.API;

public class MyBot : IChessBot
{
    //Implements the https://en.wikipedia.org/wiki/Negamax algorithm

    // Piece values: null, pawn, knight, bishop, rook, queen
    int[] PieceValues = { 0, 100, 320, 330, 500, 900, 10000 };
    int CheckmateScore = 9999;
    int Depth;

    ulong[] pst = {
        0xE6F4B06438321400,0xEAF6B2643A341400,0xEAF2B2643A361400,0xEAF0B3653A361400,
        0xEAF0B3653A361400,0xEAF2B2643A361400,0xEAF6B2643A341400,0xE6F4B06438321400,
        0xEAF4B2633A341500,0xEAF4B4643D381600,0xF0F0B5643C3C1600,0xF0F0B4643C3D1000,
        0xF0F0B4643C3D1000,0xF0F0B4643C3C1600,0xEAF4B4643D381600,0xEAF4B2633A341500,
        0xEAEEB2633A361500,0xEEECB5643E3D1300,0xF4ECB5643E3E1200,0xF6ECB5643E3F1400,
        0xF6ECB5643E3F1400,0xF4ECB5643E3E1200,0xEEECB4643E3D1300,0xEAEEB2633A361500,
        0xEAECB4633A361400,0xEEEAB4643C3C1400,0xF6EAB5643E3F1400,0xF8E8B5643E401800,
        0xF8E8B5643E401800,0xF6EAB5643E3F1400,0xEEEAB4643C3C1400,0xEAECB3633A361400,
        0xEAEAB3633A361500,0xEEE8B4643D3D1500,0xF6E8B5643D3F1600,0xF8E6B5643E401900,
        0xF8E6B5643E401900,0xF6E8B5643D3F1600,0xEEE8B4643D3D1500,0xEAEAB3633A361500,
        0xEAEAB2633A361600,0xEEE8B4643C3C1600,0xF4E8B5643D3E1800,0xF6E6B5643E3F1A00,
        0xF6E6B5643E3F1A00,0xF4E8B5643D3E1800,0xEEE8B4643C3C1600,0xEAEAB2633A361600,
        0xEAEAB2653A341E00,0xECE8B4663C381E00,0xEEE8B4663C3C1E00,0xF0E6B4663C3C1E00,
        0xF0E6B4663C3C1E00,0xEEE8B4663C3C1E00,0xECE8B4663C381E00,0xEAEAB2653A341E00,
        0xE6EAB06438321400,0xE8E8B2643A341400,0xEAE8B2643A361400,0xECE6B3643A361400,
        0xECE6B3643A361400,0xEAE8B2643A361400,0xE8E8B2643A341400,0xE6EAB06438321400,
    };

    public Move Think(Board board, Timer timer)
    {
        Move bestMove = default;
        int bestScore;
        int alpha = -CheckmateScore;
        int beta = CheckmateScore;
        int score;
        Move[] move = board.GetLegalMoves();
        for (Depth = 1; Depth < 99; Depth++)
        {
            bestScore = -CheckmateScore;
            bestMove = default;
            for (int i = 0; i < move.Length; i++)
            {
                board.MakeMove(move[i]);
                if (board.IsInCheckmate())
                {
                    board.UndoMove(move[i]);
                    return move[i];
                }
                if (board.IsDraw())
                {
                    score = 0;
                }
                else score = -NegaMax(-beta, -alpha, 1, board);
                board.UndoMove(move[i]);

                if (score > bestScore)
                {
                    bestMove = move[i];
                    move[i] = move[0];
                    move[0] = bestMove;
                    bestScore = score;
                }
            }
            if (timer.MillisecondsElapsedThisTurn > timer.MillisecondsRemaining / 40) break;
        }
        return bestMove;
    }

    private int NegaQ(int alpha, int beta, Board board)
    {
        int eval = Eval(board);
        if (eval >= beta) return beta;
        if (eval > alpha) alpha = eval;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            if (move[i].IsCapture || move[i].IsPromotion || move[i].IsEnPassant)
            {
                Move tempMove;
                Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
                int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
                for (int j = i + 1; j < move.Length; j++)
                {
                    Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                    int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                    if (capturedPieceValue2 > capturedPieceValue1)
                    {
                        tempMove = move[i];
                        move[i] = move[j];
                        move[j] = tempMove;
                    }
                }

                board.MakeMove(move[i]);
                if (board.IsInCheckmate())
                {
                    board.UndoMove(move[i]);
                    return CheckmateScore - board.PlyCount;
                }
                if (board.IsDraw())
                {
                    board.UndoMove(move[i]);
                    return 0;
                }
                int score = -NegaQ(-beta, -alpha, board);
                board.UndoMove(move[i]);

                if (score > alpha)
                {
                    alpha = score;
                    if (score >= beta)
                        return beta;
                }
            }
        }
        return alpha;
    }

    private int NegaMax(int alpha, int beta, int depth, Board board)
    {
        int score;
        if (depth >= Depth)
            return NegaQ(alpha, beta, board);

        int bestScore = -CheckmateScore;
        Move[] move = board.GetLegalMoves();
        for (int i = 0; i < move.Length; i++)
        {
            Move tempMove;
            Piece capturedPiece1 = board.GetPiece(move[i].TargetSquare);
            int capturedPieceValue1 = PieceValues[(int)capturedPiece1.PieceType];
            for (int j = i + 1; j < move.Length; j++)
            {
                Piece capturedPiece2 = board.GetPiece(move[j].TargetSquare);
                int capturedPieceValue2 = PieceValues[(int)capturedPiece2.PieceType];
                if (capturedPieceValue2 > capturedPieceValue1)
                {
                    tempMove = move[i];
                    move[i] = move[j];
                    move[j] = tempMove;
                }
            }

            board.MakeMove(move[i]);
            if (board.IsInCheckmate())
            {
                board.UndoMove(move[i]);
                return CheckmateScore - board.PlyCount;
            }
            if (board.IsDraw()) {
                board.UndoMove(move[i]);
                return 0;
            }
           if (beta - alpha > 1)
            {
                score = -NegaMax(-alpha - 1, -alpha, depth + 2, board);
                if (score > alpha)
                    score = -NegaMax(-beta, -alpha, depth + 1, board);
            }
            else
            {
                score = -NegaMax(-beta, -alpha, depth + 1, board);
            }
            board.UndoMove(move[i]);

            if (score > alpha)
            {
                alpha = score;
                if (score >= beta)
                    return beta;
            }
        }
        return alpha;
    }

    int Eval(Board board)
    {
        int eval = 0, value;
        foreach (PieceList list in board.GetAllPieceLists())
            foreach (Piece piece in list)
            {
                value = (byte)(pst[piece.Square.Index ^ (piece.IsWhite ? 0 : 0x38)] >>
                    ((int)piece.PieceType << 3));
                eval -= (board.IsWhiteToMove ^ piece.IsWhite) ? value : -value;
            }
        return eval;
    }
}
Mike Sherwin
Posts: 965
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: Tiny Chess Bot Coding Challenge

Post by Mike Sherwin »

leanchess wrote: Fri Sep 01, 2023 10:04 am Have you tried uploading it to the live tournament? It's getting quite competitive (and depth 8 won't get you very far ;))
Didn't even know about it. :oops: Won't get me very far? What about the team? :wink:
IDK, maybe there is no team. :(
I barely can do anything in C#. I, all by myself is not going to be able to compete against C# experts. Besides, the requested depth is now 99! :D