diff --git a/README.md b/README.md index 8a5dff8..3d99238 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Note that you may also want to use Bun which is a much faster JS runtime env than Node.js. + ## Setup 1. Clone the repository to your machine. @@ -74,6 +75,7 @@ Note that the config file is compiled with the engine itself, so if you are usin * Countermove heuristic. * Transposition table. * Null-move pruning. +* Futility pruning. * Late move reductions. * Search extensions: * Check extensions. diff --git a/catto.config.js b/catto.config.js index ff5803e..35e0818 100644 --- a/catto.config.js +++ b/catto.config.js @@ -1,6 +1,6 @@ module.exports = { // Current version to show in UCI - version: "v0.7.0", + version: "v0.8.0", // Late move reduction config lmrFullDepth: 4, // Number of moves to be searched in full depth lmrMaxReduction: 3, // Only apply LMR above this depth diff --git a/package.json b/package.json index c5b4aed..26fb321 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "catto", - "version": "0.7.0", + "version": "0.8.0", "description": "The Catto chess engine", "main": "index.js", "scripts": { diff --git a/src/core.ts b/src/core.ts index 2c8e1c3..f950e0f 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,7 +1,8 @@ -import { Chess, Color, Move } from "chess.js"; +import { Chess, Move } from "chess.js"; import { evaluateBoard } from "./evaluate"; import { mvv_lva, PIECE_NUM } from "./evaluations"; import { genZobristKey } from "./zobrist"; +import { mgMaterial } from "./evaluations"; export enum HashFlag { exact, @@ -309,6 +310,15 @@ export class Engine { possibleMoves = this.sortMoves(possibleMoves); let searchedMoves = 0, bestMoveSoFar: Move; + // Futility pruning + let fpEnabled = false; + if (depth < 4 && Math.abs(alpha) < 48000) { + const currentEval = evaluateBoard(this.chess); + // Margin for each depth, the shallower the depth the more we reduce the margin + const futilityMargin = [ 0, mgMaterial[0], mgMaterial[1], mgMaterial[3] ]; + fpEnabled = currentEval + futilityMargin[depth] <= alpha; + } + // Find the best move for (const move of possibleMoves) { // const tempMove = move; @@ -319,6 +329,19 @@ export class Engine { this.chess.move(move); let score = 0; + // Apply futility pruning + if ( + fpEnabled && + searchedMoves > 0 && + !move.captured && + !move.promotion && + !this.chess.inCheck() + ) { + this.chess.undo(); + this.ply--; + continue; + } + // Do normal, alpha-beta full search on first (PV) move if (searchedMoves === 0) { score = -this.negamax(depth - 1 + extensions, -beta, -alpha, extended + extensions);