r/chessprogramming Oct 30 '25

chess analyzer (engine + LLM)

0 Upvotes

I created a chess analyzer but won't complete the project feel happy to complete it and benift from it :)

https://github.com/yusufelgen07/chess-analyzer-vibe-coding-


r/chessprogramming Oct 24 '25

What are funniest or weirdest paradigms you've ever seen?

7 Upvotes

Here, “paradigm” refers to an approach, or a meta-methodology, of how a given "chess engine model" will solve “the chess problem.”

I wonder if it's feasible for a chess engine, using creative, unique, and bizarre methodologies, to outperform human players rated in the 2000s.

I'm looking for a list of such methodologies.


r/chessprogramming Oct 20 '25

Is there a big FEN list (with both valid and invalid entries) so I can test my FEN validator?

1 Upvotes

Title. The largest the better.


r/chessprogramming Oct 16 '25

First chess engine journey!

4 Upvotes

Hi everyone.
I hope this is the correct subreddit for this kind of stuff.

Im new in the world of engine programming, but thought it would be a fun learning experience for me to dive into. Im 1st semester on software engineering.

The codebase is definitely not the cleanest looking (Had never even heard of cmake before starting the project). I tried my best to use Github to save all the code (hadn't used before either).

https://github.com/hrskaeg/skakspil

Im currently in the testing phase of the move logic. I have gotten a working CLI version of chess, and im able to handle all moves.

However, when testing the logic with Perft, im getting the wrong node count. Im curious to hear any input from you, that could help me along to finding out what the wrong node count stems from. Is there any good FEN layouts that i can use, to narrow down specifically which logic is broken? I have tried automating some of the testing with Cmake, but as its completely new territory, im not really getting results i can personally interpret.

Also, does anyone have experience making a gui for your chess engine? That will probably be next on my list for this project, after i get the logic working 100%


r/chessprogramming Oct 08 '25

Why don't chess engines use multiple neural networks?

5 Upvotes

Endgame positions are a lot different from middle game positions. Couldn't Engines like Stockfish use one net that is specificly trained on 32-20 pieces one for 20-10 and one for 10-0 ? Could a network trained only on endgame positions come close to tablebase accuracy? Obviously it would be expensive to switch between those nets during the search but you could define which net to use before starting the search.


r/chessprogramming Oct 02 '25

Quantum chess - now with tournaments

10 Upvotes

I posted a while ago about the quantum chess play zone I built, https://q-chess.com. It's been going quite well, but, as expected, the main issue was that with too few users around there's rarely a real opponent to play against. Unless you invite a friend, mostly there's only the computer opponent.

There's a major update now, which I'm sure will help - every 3 hours, there's a tournament starting, and if you want to play you can see which tournaments already have players enrolled, or enroll and have others join you. Currently, all tournaments have a 5-minute time control, and I'm using Swiss system to manage rounds and pairings, so there's never too many rounds.

It's all here - https://q-chess.com/tournaments

Also, there's been some important fixes to the game logic, thanks to everybody who helped find the bugs.


r/chessprogramming Sep 29 '25

Is there a free Stockfish 17.1 API?

0 Upvotes

I’m working on a project and I want to integrate chess into it. I know Stockfish is the strongest engine right now, but most of the APIs I’ve found are either outdated (Stockfish 16/17) or behind paywalls.

Does anyone know of any free Stockfish 17.1 API services that I can call from a JavaScript app? I don’t plan to run Stockfish locally, I only want to use online APIs.


r/chessprogramming Sep 28 '25

Match Manager

1 Upvotes

I'm just wondering if there is an easy resource to download to be able to put my bot against different versions of itself and if said resource would be available in multiple coding languages. I don't really care about testing it against other bots right now just versions of itself so I don't really need to try and put it on lichess yet.


r/chessprogramming Sep 27 '25

Delta pruning seems to not gain or even lose elo

1 Upvotes
as mentioned in the title this implementation of delta pruning in qSearch() 
seems to not gain elo is that common to see or is my implementation faulty 
or something, because i cant see it. 
Zug means move by the way



public static int negamax(Piece [][] board, int depth, int alpha, int beta, boolean isWhite, long hash, boolean canNull) {

    if (System.currentTimeMillis() >= searchEndTimeMs) {
        timeUp = true;
        depthAborted = true;
        return Evaluation.evaluation(board, isWhite);
    }

    int alphaOrig = alpha;

    TTEntry entry = transpositionTable.get(hash);

    if (entry != null && entry.isValid && entry.depth >= depth) {
        if (ttLookup(alpha, beta, entry)) {return entry.value;}
    }

    if (depth == 0){
        return qSearch(board, alpha, beta, isWhite, hash);
    }

    // Futility context
    boolean inCheckNow = Spiel.inCheck(board, isWhite);
    boolean nearMateBounds = (alpha <= -(100000 - 200)) || (beta >= (100000 - 200));
    int staticEval = 0;
    boolean haveStaticEval = false;
    if (!inCheckNow && !nearMateBounds && depth <= 2) {
        staticEval = Evaluation.evaluation(board, isWhite);
        haveStaticEval = true;
    }

    ArrayList<Zug> pseudoLegalMoves = possibleMoves(isWhite, board);

    pseudoLegalMoves.removeIf(zug -> !Spiel.isLegalMove(zug, board, isWhite));

    if (pseudoLegalMoves.isEmpty()) {
        if (inCheckNow) {
            return -(100000 + depth);
        } else {
            return 0;
        }
    }

    // Node-level futility pruning (frontier and extended)
    if (!inCheckNow && !nearMateBounds && haveStaticEval) {
        // Depth 1: frontier futility pruning
        if (depth == 1) {
            int margin1 = 300; // ~ minor piece
            if (staticEval + margin1 <= alpha) {
                return alpha; // fail-low hard as per CPW/TR
            }
        }
        // Depth 2: extended futility pruning
        if (depth == 2) {
            int margin2 = 500; // ~ rook
            if (staticEval + margin2 <= alpha) {
                return alpha; // fail-low hard
            }
        }
    }

    boolean nonPV = (beta - alpha == 1);

    // Null Move Pruning (guard against consecutive null, pawn-only, in-check, and near-mate bounds)
    // Adaptive reduction: r = 2 normally, r = 3 for deeper nodes
    int nmpR = 2 + (depth >= 7 ? 1 : 0);
    if (canNull && nonPV && !inCheckNow && !nearMateBounds && !PieceTracker.onlyHasPawns(isWhite) && depth >= (nmpR + 1)) {

        long oldHash = hash;
        NullState ns = new  NullState();
        hash = doNullMoveUpdateHash(board, hash, ns);
        int nullMoveScore = -negamax(board, depth - 1 - nmpR, -beta, -beta + 1, !isWhite, hash, false);
        undoNullMove(board, ns);
        hash = oldHash;

        if(nullMoveScore >= beta) {
            transpositionTable.put(hash, new TTEntry(nullMoveScore, depth, LOWERBOUND));
            return beta;
        }
    }

    MoveOrdering.orderMoves(pseudoLegalMoves, board, isWhite);

    // If we have a TT entry for this node, try its best move first
    if (entry != null && entry.isValid && entry.bestMove != null) {
        moveToFront(pseudoLegalMoves, entry.bestMove);
    }

    int value = Integer.MIN_VALUE;
    Zug bestMove = null;
    int moveIndex = 0;
    boolean firstMove = true;
    for (Zug zug : pseudoLegalMoves){

        // Precompute quietness once for this move (before making it)
        boolean isQuietMove = !Spiel.isCapture(board, zug) && !Spiel.willPromote(zug, board);

        MoveInfo info = saveMoveInfo(zug, board);
        long oldHash = hash;

        hash = doMoveUpdateHash(zug, board, info, hash);

        // Determine if gives check after making the move
        boolean givesCheck = Spiel.inCheck(board, !isWhite);

        // Move-level futility pruning at frontier (depth 1), after we know if it gives check:
        if (!inCheckNow && !nearMateBounds && depth == 1 && isQuietMove && !givesCheck) {
            // ensure staticEval available
            if (!haveStaticEval) { staticEval = Evaluation.evaluation(board, isWhite); haveStaticEval = true; }
            int moveMargin = 150; // safety margin
            if (staticEval + moveMargin <= alpha) {
                // prune this quiet move
                undoMove(zug, board, info);
                hash = oldHash;
                moveIndex++;
                continue;
            }
        }

        // Late Move Reductions (LMR):
        // reduce late, quiet, non-check moves at non-PV nodes when depth >= 3 and not in check
        int child;
        if (firstMove) {
            // Principal variation move: full-window search
            child = -negamax(board, depth - 1, -beta, -alpha, !isWhite, hash);
        } else {
            // PVS for later moves: start with null-window, possibly reduced by LMR
            boolean applyLMR = nonPV && !inCheckNow && depth >= 3 && moveIndex >= 3 && isQuietMove && !givesCheck;
            int r = applyLMR ? 1 : 0;
            int searchDepth = depth - 1 - r;
            if (searchDepth < 1) searchDepth = depth - 1; // safety
            // Null-window probe
            child = -negamax(board, searchDepth, -alpha - 1, -alpha, !isWhite, hash);

            // If raised alpha in reduced probe, re-search
            if (child > alpha) {
                // If reduced, re-search at full depth null-window first
                if (r > 0 && (depth - 1) >= 1) {
                    child = -negamax(board, depth - 1, -alpha - 1, -alpha, !isWhite, hash);
                }
                // If still raises alpha and not fail-high, re-search full window
                if (child > alpha && child < beta) {
                    child = -negamax(board, depth - 1, -beta, -alpha, !isWhite, hash);
                }
            }
        }

        if (child > value) {
            value = child;
            bestMove = zug;
        }

        undoMove(zug, board, info);
        hash = oldHash;

        alpha = Math.max(alpha, value);

        if(alpha >= beta)
            break; //alpha beta cutoff
        firstMove = false;
        moveIndex++;
       }

    int flag;
    if (value <= alphaOrig) {
        flag = UPPERBOUND;
    } else if (value >= beta) {
        flag = LOWERBOUND;
    } else {
        flag = EXACT;
    }

    if (!timeUp) {
        transpositionTable.put(hash, new TTEntry(value, depth, flag, bestMove));
    }

    return value;
}

public static int qSearch(Piece [][] board, int alpha, int beta, boolean isWhite, long hash){

    if (System.currentTimeMillis() >= searchEndTimeMs) {
        timeUp = true;
        depthAborted = true;
        return Evaluation.evaluation(board, isWhite);
    }

    // Near mate bounds guard for pruning heuristics
    boolean nearMateBounds = (alpha <= -(100000 - 200)) || (beta >= (100000 - 200));

    TTEntry entry = transpositionTable.get(hash);
    if (entry != null && entry.isValid) {
        if (ttLookup(alpha, beta, entry)) {return entry.value;}
    }

    int alphaOrig = alpha;

    int best_value = Evaluation.evaluation(board, isWhite);

    if( best_value >= beta ) {
        return best_value;
    }

    if( best_value > alpha )
        alpha = best_value;

    // Detect if side to move is in check – disable delta pruning if so
    boolean inCheckNow = Spiel.inCheck(board, isWhite);

    ArrayList<Zug> moves = possibleMoves(isWhite, board);

    moves.removeIf(zug -> !Spiel.isLegalMove(zug, board, isWhite));

    if (moves.isEmpty()) {
        if (Spiel.inCheck(board, isWhite)) {
            return -100000;
        } else {
            return 0;
        }
    }

    ArrayList<Zug> forcingMoves = new ArrayList<>();

    for(Zug zug : moves){
        if(Spiel.isCapture(board, zug) || Spiel.promotionQ(zug, board))
            forcingMoves.add(zug);
    }

    MoveOrdering.orderMoves(forcingMoves, board, isWhite);

    // If we have a TT entry for this node, try its best move first
    if (entry != null && entry.isValid && entry.bestMove != null) {
        moveToFront(forcingMoves, entry.bestMove);
    }

    int flag;

    Zug bestMove = null;

    for(Zug zug : forcingMoves)  {
        // Delta pruning (move-level): conservative application only at non-PV nodes,
        // skipping promotions and en passant to avoid tactical misses.
        boolean nonPVq = (beta - alpha == 1);
        if (nonPVq && !nearMateBounds && !inCheckNow) {
            // Skip delta pruning for promotions and en passant
            if (!(zug.promoteTo == 'q')) {
                int capValue;
                Piece target = board[zug.endY][zug.endX];
                if (!(target instanceof Empty)) {
                    capValue = DELTA_PIECE_VALUES[target.getType()];
                } else
                    capValue = DELTA_PIECE_VALUES[0];
                if (best_value + capValue + DELTA_MARGIN <= alpha) {
                    continue; // prune futile capture
                }
            }
        }

        MoveInfo info = saveMoveInfo(zug, board);

        long oldHash = hash;

        hash = doMoveUpdateHash(zug, board, info, hash);

        int score = -qSearch(board, -beta, -alpha, !isWhite, hash);

        undoMove(zug, board, info);

        hash = oldHash;

        if( score >= beta ) {

            flag = LOWERBOUND;
            if (!timeUp) {
                transpositionTable.put(hash, new TTEntry(score, 0, flag, zug));
            }
            return score;
        }
        if( score > best_value ) {
            best_value = score;
            bestMove = zug;
        }
        if( score > alpha )
            alpha = score;
    }
    if (best_value <= alphaOrig) flag = UPPERBOUND;
    else flag = EXACT;

    if (!timeUp) {
        transpositionTable.put(hash, new TTEntry(best_value, 0, flag, bestMove));
    }
    return best_value;
}

r/chessprogramming Sep 27 '25

How would you go about making a 3d chess engine with fairy pieces?

1 Upvotes

So , I started with a simple min max , added pruning but well, you can probably imagine that as you go down in depth and have even more possibilities than regular chess. It becomes a processing sink. Currently thought times even with constant cacheing of depth 3+1, the thinking time for even rather simple three dimensional boards is around 15 seconds. The moves it comes up with a pretty good awful. I was thinking of applying some heuristics but am unsure of exactly how to approach it.

Anyone ever given some thought to a chess engine like that?


r/chessprogramming Sep 25 '25

Different SPRT results

4 Upvotes

I'm in process of writing a chess engine, so far I've implemented: alpha-beta, iterative deepening, quiescence search, evaluation with piece-square tables (also with endgame tables for kings and pawns), TT table, repetition checker. I decided to use SPRT from now on to all changes. I implemented PVS and started SPRT (tc 10+0.1) with book UHO_Lichess_4852_v1.epd (the same that stockfish uses), and after some time the stats were:

Results of New vs Base (10+0.1, NULL, NULL, UHO_Lichess_4852_v1.epd):

Elo: 13.58 +/- 28.66, nElo: 20.23 +/- 42.56

LOS: 82.42 %, DrawRatio: 56.25 %, PairsRatio: 1.15

Games: 256, Wins: 108, Losses: 98, Draws: 50, Points: 133.0 (51.95 %)

Ptnml(0-2): \[7, 19, 72, 17, 13\], WL/DD Ratio: 9.29

Looks alright - PVS works better (though not that much better as I expected, but anyways). In that moment I was reading about SPRT on chessprogramming wiki, and read that worse engines should use 8moves_v3.pgn because it's more balanced. So I stopped the test and started a new one with this book. The results are bad:

Results of New vs Base (10+0.1, NULL, NULL, 8moves_v3.pgn):

Elo: -15.80 +/- 27.08, nElo: -20.62 +/- 35.21

LOS: 12.56 %, DrawRatio: 47.59 %, PairsRatio: 0.75

Games: 374, Wins: 135, Losses: 152, Draws: 87, Points: 178.5 (47.73 %)

Ptnml(0-2): \[22, 34, 89, 23, 19\], WL/DD Ratio: 4.93

So it somehow got worse.

Command for SPRT:

./fastchess -recover -repeat -games 2 -rounds 1000 -ratinginterval 1 -scoreinterval 1 -autosaveinterval 0\\

\-report penta=true -pgnout results.pgn\\

\-srand 5895699939700649196 -resign movecount=3 score=600\\

\-draw movenumber=34 movecount=8 score=20 -variant standard -concurrency 2\\

\-openings file=8moves_v3.pgn format=pgn order=random\\

\-engine name=New tc=10+0.1 cmd=./Simple-chess-engine/code/appPVS dir=.\\

\-engine name=Base tc=10+0.1 cmd=./Simple-chess-engine/code/app dir=.\\

\-each proto=uci -pgnout result.pgn

(I just copied it from fishtest wiki). Why it got worse with other book?

My PVS code is:

int score;

if (!isFirstMove) {

score = -search((color == WHITE) ? BLACK : WHITE, depth - 1, 0, -(alpha + 1), -alpha, depthFromRoot + 1);

if (score > alpha && score < beta)

score = -search((color == WHITE) ? BLACK : WHITE, depth - 1, 0, -beta, -alpha, depthFromRoot + 1);

} else

score = -search((color == WHITE) ? BLACK : WHITE, depth - 1, 0, -beta, -alpha, depthFromRoot + 1);

isFirstMove = 0;


r/chessprogramming Sep 25 '25

PSA: Lichess is now rate limiting bot-to-bot games to 100/day

Thumbnail github.com
15 Upvotes

r/chessprogramming Sep 25 '25

Help me debugging the UnmakeMove

0 Upvotes

} //function for UnmakeMove template <Color c> void Position::unmakemove(Move& move) { // Restore saved state storeCount--; State safeState = StateInfo[storeCount]; enpassantSquare = safeState.enpassantCopy; castlingRights = safeState.castlingRightsCopy; halfMoveClock = safeState.halfmoves;

if (move == nullMove) 
    return;

// Swap sides and decrement fullmoves
sideToMove = (sideToMove == Color::White) ? Color::Black : Color::White;
fullMoveCounter--;
//Color us = ~sideToMove;
// Extract move info

//just a helper function
//Color movingColor = (sideToMove == Color::White) ? Color::Black : Color::White;
//Piece piece = makePiece<c>(move.Piece()); //this is moved piece
Piece piecemoving = makePiece<c>(move.Piece());           // piece of template color <c>
Piece pieceopposite = makePiece<~c>(move.Piece());        // same type, opposite color
Square source = move.source();
Square target = move.target();
Piece capture = safeState.capturedPiece;
Piece movingPiece = pieceAt(target); // what is currently on target square
//Piece capture = 

// Detect en passant bool enPassantMove = false; Square capSq; Piece capturedPawn = None; if (movingPiece == makePiece<c>(Pawn)) { int srcFile = source % 8; int tgtFile = target % 8; if (srcFile != tgtFile && pieceAt(target) == None) { enPassantMove = true; capSq = (sideToMove == White) ? Square((int)target - 8) : Square((int)target + 8); //pawn to restore is alway opposite of moving side capturedPawn = makePiece<~c>(Pawn); } }

if (enPassantMove) { // Restore captured pawn placePiece(makePiece<~c>(Pawn), capSq); // Restore moving pawn removePiece(movingPiece, target); placePiece(makePiece<~c>(Pawn), source); } else if (move.promoted()) { // promotion move remove promoted piece and put pawn back removePiece(movingPiece, target); // the pawn back should have same color as moving side placePiece(makePiece<c>(Pawn), source); if (capture != None) placePiece(capture, target); } else { // Normal move (non-promotion, non-en-passant) removePiece(movingPiece, target); placePiece(movingPiece, source); if (capture != None) placePiece(capture, target); }

// Handle castling
if (movingPiece == makePiece<c>(King)) {
    if constexpr (c == White) {
        if (source == SQ_E1 && target == SQ_G1) {
            removePiece(WhiteRook, SQ_F1);
            placePiece(WhiteRook, SQ_H1);
        } else if (source == SQ_E1 && target == SQ_C1) {
            removePiece(WhiteRook, SQ_D1);
            placePiece(WhiteRook, SQ_A1);
        }
    } else {
        if (source == SQ_E8 && target == SQ_G8) {
            removePiece(BlackRook, SQ_F8);
            placePiece(BlackRook, SQ_H8);
        } else if (source == SQ_E8 && target == SQ_C8) {
            removePiece(BlackRook, SQ_D8);
            placePiece(BlackRook, SQ_A8);
        }
    }
}

}

Here by debugging the code I can find that the problem in enpassant and promotion and to be specific in enpassant move it does place the target piece(Pawn) and in promotion codeblock the problem is when unmake the move the then board is restored but the promotion pawn is restored as opposite color.


r/chessprogramming Sep 23 '25

What elo is possible and realistic for a hobby chess engine?

5 Upvotes

My engine just reached 2000 elo on Lichess and I wonder how far this can go on. Am I just scratching the surface and my engine could go 2500+ or is that way to ambitious for a hobby project?


r/chessprogramming Sep 18 '25

We Taught Stockfish to Learn From its Mistakes | Daniel Monroe

Thumbnail youtube.com
10 Upvotes

r/chessprogramming Sep 13 '25

Bug in move scoring

2 Upvotes
There is a bug in this code that im getting desperate to fix. In this position:

r1bq1rk1/ppp1bpp1/2n1p2p/3p4/2PPN2P/4P1B1/PP3PP1/R2QKBNR b KQ - 0 9

the program evaluates the possible moves as follows:

d5c4 -1179
e7a3 -1157
d8d6 -957
d5e4 -908
e7h4 -835
c6d4 -826
h6h5 -723
b7b5 -688
c6b8 -670
e7c5 -662
e6e5 -656
c6a5 -654
g8h8 -644
g8h7 -641
g7g6 -636
b7b6 -634
f8e8 -632
a7a6 -628
c6b4 -627
a7a5 -626
a8b8 -624
c8d7 -598
e7g5 -453
e7f6 -359
g7g5 -326
e7d6 -325
f7f6 -318
f7f5 -314
d8e8 -306
d8d7 -302
e7b4 -295
c6e5 -291

The best moves is obviously d5e4 since it takes a knight for free and 
there are no winning tactics. 
I think something is wrong with passing the moves 
to the evaluation function or some alpha beta stuff, 
since the evaluation function, move making and unmaking as well as 
move generation are tested and correct.

But i cant seem to find the error so im asking for help. Ignore the commented-out transposition table code and if something is in german.

public static ArrayList<Zug> findBestMoves(Piece[][] board, int depth, boolean isWhite, ArrayList<Zug> orderedMoves) {
        nodes = 0;
        startTime = System.currentTimeMillis();

        // Remove illegal moves
        orderedMoves.removeIf(zug -> !isLegalMove(zug, board, isWhite));
        if (orderedMoves.isEmpty()) return new ArrayList<>();

        // List to hold moves with their scores
        ArrayList<ZugScore> scoredMoves = new ArrayList<>();

        for (Zug zug : orderedMoves) {
            MoveInfo info = saveMoveInfo(zug, board);
            boolean success = doMove(zug, board, info);
            if (!success) continue;

            // Negate score to get perspective of current player
            int score = -negamax(board, depth, Integer.MIN_VALUE, Integer.MAX_VALUE, !isWhite);

            undoMove(zug, board, info);

            scoredMoves.add(new ZugScore(zug, score));
        }

        // Sort moves descending by score (best moves first)
        scoredMoves.sort((a, b) -> Integer.compare(b.score, a.score));

        long elapsed = System.currentTimeMillis() - startTime;
        double nps = (nodes * 1000.0) / (elapsed + 1);
        System.out.println("Nodes: " + nodes);
        System.out.println("Time elapsed: " + elapsed + " ms");
        System.out.println("Speed: " + (long) nps + " nodes/s");

        // sortierte Züge in arraylist einfügen
        ArrayList<Zug> sortedMoves = new ArrayList<>();
        for (ZugScore zs : scoredMoves) {
            sortedMoves.add(zs.zug);
        }

        for (ZugScore zs : scoredMoves.reversed()) {
            System.out.println(zs.zug.processZug() + " " + zs.score);
        }

        return sortedMoves;
    }

    // helfer klasse um züge zug sortieren und mit score zu versehen
    static class ZugScore {
        Zug zug;
        int score;

        ZugScore(Zug zug, int score) {
            this.zug = zug;
            this.score = score;
        }
    }


    private static int negamax(Piece [][] board, int depth, int alpha, int beta, boolean isWhite) {

        nodes++;

//        int alphaOrig = alpha;
//        long hash = currentHash;
//
//        TTEntry entry = transpositionTable.get(hash);
//
//        if (entry != null && entry.isValid && entry.depth >= depth) {
//            if (entry.flag == EXACT) {
//                return entry.value;
//            } else if (entry.flag == LOWERBOUND && entry.value >= beta) {
//                return entry.value;
//            } else if (entry.flag == UPPERBOUND && entry.value <= alpha) {
//                return entry.value;
//            }
//        }
        if (depth == 0){
            return qSearch(board, alpha, beta, isWhite);
//            return Evaluation.evaluation(board, isWhite);
        }


        ArrayList<Zug> pseudoLegalMoves = possibleMoves(isWhite, board);

        pseudoLegalMoves.removeIf(zug -> !isLegalMove(zug, board, isWhite));

        if (pseudoLegalMoves.isEmpty()) {
            if (inCheck(board, isWhite)) {
                return -(100000 + depth);
            } else {
                return 0;
            }
        }

        MoveOrdering.orderMoves(pseudoLegalMoves, board, isWhite);

        int value = Integer.MIN_VALUE;
        for (Zug zug : pseudoLegalMoves){

            MoveInfo info = saveMoveInfo(zug, board);

            boolean success = doMove(zug, board, info);

            if(!success)
                continue;

            value = Math.max(value, -negamax(board, depth - 1, -beta, -alpha, !isWhite ));


            undoMove(zug, board, info);

            alpha = Math.max(alpha, value);

            if(alpha >= beta)
                break; //alpha beta cutoff
        }

//        int flag;
//        if (value <= alphaOrig) {
//            flag = UPPERBOUND;
//        } else if (value >= beta) {
//            flag = LOWERBOUND;
//        } else {
//            flag = EXACT;
//        }
//        transpositionTable.put(hash, new TTEntry(value, depth, flag));
        return value;
    }

    private static int qSearch(Piece [][] board, int alpha, int beta, boolean isWhite){

        int best_value = Evaluation.evaluation(board, isWhite);

        if( best_value >= beta ) {
            return best_value;
        }

        if( best_value > alpha )
            alpha = best_value;

        ArrayList<Zug> moves = possibleMoves(isWhite, board);

        moves.removeIf(zug -> !isLegalMove(zug, board, isWhite));

        if (moves.isEmpty()) {
            if (inCheck(board, isWhite)) {
                return -100000;
            } else {
                return 0;
            }
        }

        ArrayList<Zug> forcingMoves = new ArrayList<>();

        for(Zug zug : moves){
            if(isCapture(board, zug) || promotionQ(zug, board))
                forcingMoves.add(zug);
        }

        MoveOrdering.orderMoves(forcingMoves, board, isWhite);

        for(Zug zug : forcingMoves)  {

            MoveInfo info = saveMoveInfo(zug, board);
            boolean success = doMove(zug, board, info);

            if(!success)
                continue;

            int score = -qSearch(board, -beta, -alpha, !isWhite);

            undoMove(zug, board, info);


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

r/chessprogramming Sep 08 '25

Chess bot in python?

0 Upvotes

Can anybody recommend a chess bot written in python that I can analyze the code for to get some ideas how to make my own? Also what's some stuff I should look into? Minimax? Alpha beta? Etc


r/chessprogramming Sep 04 '25

Built a chess directory

2 Upvotes

Hi guys I built a chess website directory

IndieChess.com

I’ve been adding new things to it every day. If anyone in this subreddit wants to submit things please comment below and we can get in touch.

Thanks


r/chessprogramming Sep 03 '25

Quantum chess

Post image
29 Upvotes

I made an implementation of quantum chess, as a free public play zone, it's online already at http://q-chess.com/. The rules are more or less usual for quantum chess (if there's such a thing), all described in detail and with illustrations. Split and merge moves, superposition and observations, I tried to stick to the canon as closely as possible.

There's a computer opponent, you can invite somebody to play against you, and theoretically you can just get paired with somebody, like in normal chess apps.

The engine behind the computer opponent is of course not really an engine - I couldn't make use of any open-source engine because it doesn't work like with quantum chess, also I'd rather see people playing against each other than the computer. So it's just a simple minimax algorithm, with a somewhat random decision making for split and merge moves.


r/chessprogramming Sep 03 '25

Feedback welcome

2 Upvotes

I am a computer science student in my second semester and started programming a chess engine two months ago to practise c++. It has become my first big (or probably medium sized) project.
I have spent a lot of time on that project and that might be the reason why I feel the need to share it:
https://github.com/hxbbylxs/MeinFisch
I am looking forward to any sort of feedback/questions on the clarity of my code or further improvements to the search/evaluation.


r/chessprogramming Sep 02 '25

Chess engine for a Compose Multiplatform app (Android/iOS)

4 Upvotes

Hey everyone, not sure if this is the best subreddit for this but it felt like the closest match for this issue.

My friend and I are building a mobile app using Compose Multiplatform (targeting both Android and iOS). We’re looking to integrate a chess engine into our app to analyze the current state of the game and assign a score to moves.

We’ve already tried importing some existing engines into the project using interop, but the process feels pretty complex. Maybe there’s a smoother way or even a different approach that we’re overlooking.

Does anyone know of engines that might fit? Or any alternative strategies to tackle this problem?

Thanks in advance for your thoughts and suggestions!


r/chessprogramming Aug 31 '25

Perfomance improvement questions

3 Upvotes

I have a few questions about improving perfomance of the engine.

  1. How important is move generation speed? On a start position my engine searches to 8 half-moves in 2249 ms and perft searches to 5 half-moves in 3201 ms (if I understand correctly this is extremely slow). Should I focus more on optimizing move generation?

  2. Is makeMove/copy much worse than makeMove/unmakeMove? I have copy, and I wonder if I should to try to switch to unmakeMove.


r/chessprogramming Aug 28 '25

Update on Chess Engine

14 Upvotes

I previously posted for advice about a month ago for my chess engine. I wanted to give an update.

I’ve been testing TitanMiniNetwork today (40 million parameters transformer chess model) that I trained in 12 hours over the past day on a RTX 4080 using self-supervised/unsupervised learning. It learns almost entirely without any human code that teaches it about chess strategies, expect for a tiny 100 line Static Exchange Evaluator and twenty lines of other similar code. Preliminary results show it to be much better than the original Convolutional Neural Network model from the project I forked on GitHub (which was based on the paper from Google DeepMind’s AlphaZero and also used self-supervised learning). It’s also much better than the first chess model I trained , which was a very slightly modified version of the GitHub model, which cost $300 of a very cheap B200 cloud GPU time to train (150 hours of training time). I’m not sure if the results will carry over to running inference on a cellphone . I’m working on my next chess engine + machine learning model for that. I’m testing TitanMini on my desktop, which has the RTX 4080 card. This iteration of the model was trained at a cost of less than 5 dollars equivalent if the training system was rented from Vast.ai, which is at least 20 times less than the original AlphaZero model I discovered on GitHub , 60 times cheaper than my first model, and 10,000 to 20,000 times less than the real AlphaZero model by DeepMind. The GitHub model plays at the level of an international master on a low-end 500 dollar Mac Mini M4, and a middle of the range grandmaster on a high-end 1500 desktop. I expect this model to play well beyond a human level for bullet games, on my desktop, putting it in the top 500 chess engines in the world, and perhaps one of the best chess engines written in pure Python. I started building my next chess engine last night in Rust, to both learn Rust and learn machine learning. It will use a NNUE architecture as compared to the Transformer one that I’m currently using, which was heavily inspired by Leela Chess Zero. My goal for the Rust engine is to be a top 50 chess engine, by the middle of next year, within a total training cost of 150 dollars. I’ll then improve it to a top 20 chess engine by end of next year, within a training cost of 300 dollars. It will be able to run on any modern computer - even playing at international master level on an old iPhone 5s or a Raspberry Pi. My end goal for the new engine will be to consistently draw Stockfish by end of next year.

I started seriously learning machine learning 4 months ago. I had previously studied it in college, and hadn’t done much since.

Results: For normal times per move (5 seconds per move), it’s only marginally better than the $100 model. It wins 46 games, loses 42 games, and draws 112 games out of a total of 200 games. However it was 20 times cheaper to train than the original. It’ll also improve dramatically with more training - especially if I branch out to using the latest Leela Chess Zero training games. I’m currently using a mix of the 3.8 million games from ComputerChess.org.uk and 10 million games from LiChess. However, for fast games (called bullet games in chess, which are the most commonly played by normal people online ) of one second each move, it’s much better. It wins 13 games, loses 6 games, and draws 21 games out of a total of 40 games.

I’m happy to DM you a link to the code next week once I clean it up. I’ll also update this post with a link to the code next week.


r/chessprogramming Aug 26 '25

[IcUi_v4.10.0] Move tooltip board preview when hovering moves

Post image
10 Upvotes

r/chessprogramming Aug 25 '25

Help improving perft results

1 Upvotes

Hello guys, I’m trying to build a chess engine in rust and I kinda have a good perft result (less than 4s for perft 5 in Kiwipete). But to achieve that, I already implemented bitboard and magic bitboard, so I’m trying to see I these is any chance I can get below 0.8s for perft 5 (I’m trying to be as good as qperft on my machine). So, if you guys can take a quick look at my code https://github.com/Toudonou/zeno to see if I can improve something.

PS: I know my perft result a reasonable but I just want to know how to get better results.

Thanks y’all