Crate timecat

source ·
Expand description

§Timecat Chess Engine

Timecat is a UCI-compatible chess engine designed in Rust that combines powerful algorithms and advanced evaluation techniques to deliver top-notch chess analysis and gameplay. Using alpha-beta pruning with the negamax algorithm and the NNUE evaluation method, Timecat achieves enhanced depth and accuracy in game analysis.

§Timecat as a Library

Timecat was originally conceived as a personal project. However, with the onset of a new chess-related project, I realized the benefits of publishing Timecat as a library to avoid excessive code duplication. Initially designed for personal use, Timecat will now be refined and updated to enhance its functionality and usability as a library, making it more accessible and beneficial for other users. Also the documentation will be further improved.

§Key Features

  • UCI Compatibility: Fully compatible with the Universal Chess Interface (UCI) standard.
  • Advanced Algorithms: Utilizes alpha-beta pruning and the negamax algorithm for efficient move searching.
  • NNUE Evaluation: Incorporates NNUE (efficiently updatable neural network) for state-of-the-art position evaluation.
  • Customizable Builds: Supports tailored builds through configurable cargo features.

§Integration of the Chess Library

Initially, Timecat was dependent on the external chess library, which is available at https://github.com/jordanbray/chess. To align more closely with specific requirements, the library was integrated directly into Timecat. This integration permitted significant modifications and extensions to its functionalities, thereby enhancing the engine’s overall capabilities. Such integration demonstrates a commitment to adapting and evolving the tools to secure the best possible performance and accuracy in chess analytics.

§User Controls

In the library, only pub or non-pub visibility modifiers are used (unless extremely necessary to prevent users from making catastrophic errors). This approach ensures that all potentially useful functions and structures are accessible to the user, avoiding the situation where a pub(crate) might restrict access to valuable components—a problem I’ve encountered while using the chess library. Therefore, only the features that is considered essential are included in timecat::prelude; all other functionalities are available for direct import from the timecat library.

Also several cargo features have been introduced to provide users with complete control over the code’s behavior.

§NNUE Support

Timecat currently utilizes the Stockfish NNUE for evaluation (only HalfKP supported). Plans are in place to transition to a custom-trained NNUE in the future.

§Engine Strength

Although it hasn’t been thoroughly tested yet, but my chess engine is capable of defeating chess.com’s max bot, which has an Elo rating of 3200.

§Installation

§Installing as a Binary

Optimize your setup for the best performance:

RUSTFLAGS="-C target-cpu=native" cargo install timecat

§Compilation from Source

Clone the repository and compile with native optimizations:

git clone https://github.com/Gourab-Ghosh/timecat-rs.git
cd timecat-rs
RUSTFLAGS="-C target-cpu=native" cargo run --release

§Usage as a Library

§Minimal Dependency Integration

Integrate Timecat into your Rust projects with minimal dependencies:

cargo add timecat --no-default-features

§Examples

This example demonstrates how to set up a chess board, make moves, evaluate board positions, and utilize the inbuilt engine to find optimal moves using the timecat library. Some features such as position evaluation (nnue) and engine computation (engine) are optional and can be enabled via cargo features.

First, add the timecat crate to your project with the necessary features enabled (nnue feature is already enabled within the engine feature):

cargo add timecat --no-default-features --features engine

Then, you can proceed with the following Rust code:

use timecat::prelude::*;

fn main() {
    // Initialize a chess board with the default starting position.
    let mut board = Board::default();

    // Apply moves in standard algebraic notation.
    board.push_san("e4").expect("Failed to make move: e4");
    board.push_san("e5").expect("Failed to make move: e5");

    // Evaluate the current board position using the nnue feature.
    let evaluation = board.evaluate();
    println!("Current Evaluation: {}\n", evaluation);

    // Initialize the engine with the current board state.
    let engine = Engine::new(
        board,
        TranspositionTable::default(),
    );

    // Configure the engine to search for the best move up to a depth of 10 plies.
    let response = engine.go_verbose(GoCommand::Depth(10));
    let best_move = response.get_best_move()
                            .expect("No best move found");

    // Output the best move found by the engine.
    println!("\nBest Move: {}", best_move);
}

You can use UCI commands, although it’s not recommended in production environments due to potential parsing delays and unpredictable outputs. The nnue and engine features are also required in this context.

As previous, add the timecat crate to your project:

cargo add timecat --no-default-features --features engine

Then, you can proceed with the following Rust code:

use timecat::prelude::*;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Create the default engine initialized with the standard starting position.
    let mut engine = Engine::default();

    // List of UCI commands to be executed on the chess engine.
    let uci_commands = [
        // Checks if the engine is ready to receive commands.
        "isready",
        // Sets the move overhead option.
        "setoption name move overhead value 200",
        // Display the current state of the chess board.
        "d",
        // Sets a new game position by applying the moves.
        "position startpos moves e2e4 e7e5",
        // Instructs the engine to calculate the best move within 3000 milliseconds.
        "go movetime 3000",
    ];

    // Process each UCI command and handle potential errors.
    for command in uci_commands {
        timecat::UCIParser::parse_command(&mut engine, command)?;
    }

    Ok(())
}

Or just enjoy the engine play against itself:

use timecat::prelude::*;
use std::error::Error;

fn main() {
    timecat::Parser::parse_command(
        &mut Engine::default(),
        // selfplay command has same format as go command
        "selfplay movetime 10", // Adjust time according to your wish
    ).unwrap();
}

The selfplay command works on the binary as well.

§Cargo Features

  • binary: Enables binary builds, including NNUE and engine functionalities.
  • nnue_reader: Adds support for NNUE evaluation by reading nnue files.
  • inbuilt_nnue: Integrate built-in NNUE evaluation support by including the nnue file directly into the binary, fetched using the reqwest library.
  • engine: Provides the Engine struct for in-depth position analysis and move searching.
  • colored_output: Displays all information in a visually appealing colored format for enhanced readability.
  • speed: Optimize the code to improve speed at the cost of increased memory usage and in extremely rare cases cause unexpected behavior. Note that the gain in speed might be minimal compared to the additional memory required.
  • serde: Enables serialization and deserialization support via serde.
  • debug: Intended solely for development use.
  • experimental: Codes under development for upcoming features.

Default features include binary, colored_output and speed.

§TODO

  • Implement other variants of chess.
  • Implement Syzygy Tablebase.
  • Organize the Polyglot Table codes to make it usable.
  • Organize the pgn related codes to make it usable.
  • Implement xboard feature.
  • Add svg feature like the python package chess for better visualization.

§License

Timecat is open-sourced under the GNU GENERAL PUBLIC LICENSE. You are free to use, modify, and distribute it under the same license.

§Contributing

Feel free to fork the repository, make improvements, and submit pull requests. You can also report issues or suggest features through the GitHub issue tracker.

Re-exports§

Modules§

  • Composable external iteration.
  • Inspection and manipulation of the process’s environment.
  • Utilities for formatting and printing Strings.
  • Filesystem manipulation operations.
  • The concrete iterator types.
  • Native threads.
  • Traits helpful for using certain Itertools methods in generic contexts.

Macros§

Structs§

  • A thread-safe reference-counting pointer. ‘Arc’ stands for ‘Atomically Reference Counted’.
  • A vector with a fixed capacity.
  • A boolean type which can be safely shared between threads.
  • An integer type which can be safely shared between threads.
  • A “meta iterator adaptor”. Its closure receives a reference to the iterator and may pick off as many elements as it likes, to produce the next iterator element.
  • An iterator for the elements in a single chunk.
  • ChunkBy is the storage for the lazy grouping operation.
  • An iterator that yields the Chunk iterators.
  • An iterator over all windows, wrapping back to the first elements when the window would otherwise exceed the length of the iterator, producing tuples of a specific size.
  • An iterator to iterate through all the k-length combinations in an iterator.
  • An iterator to iterate through all the n-length combinations in an iterator, with replacement.
  • An iterator that maps an iterator of tuples like ((A, B), C) to an iterator of (A, B, C).
  • A Duration type to represent a span of time, typically used for system timeouts.
  • Iterator returned for the error case of Itertools::exactly_one() This iterator yields exactly the same elements as the input iterator.
  • An iterator adapter to filter and apply a transformation on values within a nested Result::Ok.
  • An iterator adapter to filter values within a nested Result::Ok.
  • An iterator adaptor that flattens Result::Ok values and allows Result::Err values through unchanged.
  • Format all iterator elements lazily, separated by sep.
  • Format all iterator elements lazily, separated by sep.
  • An iterator for the elements in a single group.
  • GroupingMap is an intermediate struct for efficient group-and-fold operations. It groups elements by their key and at the same time fold each group using some aggregating operation.
  • An iterator that yields the Group iterators.
  • A measurement of a monotonically nondecreasing clock. Opaque and useful only with Duration.
  • An iterator adaptor that alternates elements from two iterators until both run out.
  • An iterator adaptor that alternates elements from the two iterators until one of them runs out.
  • An iterator adaptor to insert a particular value created by a function between each element of the adapted iterator.
  • ChunkLazy is the storage for a lazy chunking operation.
  • An iterator that infinitely applies function to value and yields results.
  • An iterator adaptor that merges an abitrary number of base iterators according to an ordering function.
  • An iterator adaptor that merges the two base iterators in ascending order. If both base iterators are sorted (ascending), the result is sorted.
  • See multipeek() for more information.
  • An iterator adaptor that iterates over the cartesian product of multiple iterators of type I.
  • An iterator adaptor that pads a sequence to a minimum length by filling missing elements using a closure.
  • An error returned when parsing a bool using from_str fails
  • An error which can be returned when parsing an integer.
  • See peek_nth() for more information.
  • An iterator adaptor that takes items while a closure returns true.
  • An iterator adaptor that iterates through all the k-permutations of the elements from an iterator.
  • An iterator adapter to get the positions of each element that matches a predicate.
  • An iterator to iterate through the powerset of the elements from an iterator.
  • An iterator that produces only the T values as long as the inner iterator produces Ok(T).
  • An iterator adaptor that iterates over the cartesian product of the element sets of two iterators I and J.
  • An iterator adaptor that allows putting back a single item to the front of the iterator.
  • An iterator adaptor that allows putting multiple items in front of the iterator.
  • A (half-open) range bounded inclusively below and exclusively above (start..end).
  • A wrapper for Rc<RefCell<I>>, that implements the Iterator trait.
  • An iterator that produces n repetitions of an element.
  • A reader-writer lock
  • An iterator adaptor that consumes elements while the given predicate is true, including the element for which the predicate first returned false.
  • An iterator adaptor that borrows from a Clone-able iterator to only pick off elements while the predicate returns true.
  • One half of an iterator pair where both return the same elements.
  • An iterator over a incomplete tuple.
  • An iterator to iterate through all combinations in a Clone-able iterator that produces tuples of a specific size.
  • An iterator over all contiguous windows that produces tuples of a specific size.
  • An iterator that groups the items in tuples of a specific size.
  • UnfoldDeprecated
    See unfold for more information.
  • An iterator adapter to filter out duplicate elements.
  • An iterator adapter to filter out duplicate elements.
  • An iterator adapter to apply a mutating function to each element before yielding it.
  • An iterator adaptor that filters Option<A> iterator elements and produces A. Stops on the first None encountered.
  • An iterator adaptor that wraps each element in an Position.
  • See multizip for more information.
  • An iterator which iterates two other iterators simultaneously and panic if they have different lengths.
  • An iterator which iterates two other iterators simultaneously and wraps the elements in EitherOrBoth.

Enums§

Constants§

Statics§

Traits§

  • The addition operator +.
  • The addition assignment operator +=.
  • A BinRead trait allows reading a structure from anything that implements io::Read and io::Seek BinRead is implemented on the type to be read out of the given reader
  • The bitwise AND operator &.
  • The bitwise AND assignment operator &=.
  • The bitwise OR operator |.
  • The bitwise OR assignment operator |=.
  • The bitwise XOR operator ^.
  • The bitwise XOR assignment operator ^=.
  • ? formatting.
  • Used for immutable dereferencing operations, like *v.
  • Used for mutable dereferencing operations, like in *v = 1;.
  • A data structure that can be deserialized from any data format supported by Serde.
  • The division operator /.
  • The division assignment operator /=.
  • Error is a trait representing the basic expectations for error values, i.e., values of type E in Result<T, E>.
  • Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
  • Parse a value from a string
  • A hashable type.
  • A trait for hashing an arbitrary stream of bytes.
  • Used for indexing operations (container[index]) in immutable contexts.
  • Used for indexing operations (container[index]) in mutable contexts.
  • An Iterator blanket implementation that provides extra adaptors and methods.
  • The multiplication operator *.
  • The multiplication assignment operator *=.
  • An iterator that can be unzipped into multiple collections.
  • The unary negation operator -.
  • The unary logical negation operator !.
  • An iterator that allows peeking at an element before deciding to accept it.
  • The remainder operator %.
  • The remainder assignment operator %=.
  • A data structure that can be serialized into any data format supported by Serde.
  • The left shift operator <<. Note that because this trait is implemented for all integer types with multiple right-hand-side types, Rust’s type checker has special handling for _ << _, setting the result type for integer operations to the type of the left-hand-side operand. This means that though a << b and a.shl(b) are one and the same from an evaluation standpoint, they are different when it comes to type inference.
  • The left shift assignment operator <<=.
  • The right shift operator >>. Note that because this trait is implemented for all integer types with multiple right-hand-side types, Rust’s type checker has special handling for _ >> _, setting the result type for integer operations to the type of the left-hand-side operand. This means that though a >> b and a.shr(b) are one and the same from an evaluation standpoint, they are different when it comes to type inference.
  • The right shift assignment operator >>=.
  • The subtraction operator -.
  • The subtraction assignment operator -=.
  • Trait to represent types that can be created by summing up an iterator.

Functions§

  • Test whether the predicate holds for all elements in the iterable.
  • Test whether the predicate holds for any elements in the iterable.
  • Assert that two iterables produce equal sequences, with the same semantics as equal(a, b).
  • Get a line between these two squares, not including the squares themselves.
  • Takes two iterables and creates a new iterator over both in sequence.
  • Create an iterator that clones each element from &T to T.
  • Combine all an iterator’s elements into one element by using Extend.
  • Create an iterator that maps for example iterators of ((A, B), C) to (A, B, C).
  • Compares every element yielded by both i and j with the given function in lock-step and returns a Diff which describes how j differs from i.
  • Iterate iterable with a running index.
  • Return true if both iterables produce equal sequences (elements pairwise equal and sequences of the same length), false otherwise.
  • Perform a fold operation over the iterable.
  • Get a BitBoard that represents the squares on the 1 or 2 files next to this file.
  • Get the moves for a bishop on a particular square, given blockers blocking my movement.
  • Get the rays for a bishop on a particular square.
  • Get the legal destination castle squares for both players
  • Get a BitBoard that represents all the squares on a particular file.
  • Get the king moves for a particular square.
  • Get the knight moves for a particular square.
  • Get the pawn capture move for a particular square, given the pawn’s color and the potential victims
  • Get all the pawn moves for a particular square, given the pawn’s color and the potential blocking pieces and victims.
  • Get the quiet pawn moves (non-captures) for a particular square, given the pawn’s color and the potential blocking pieces.
  • Get a BitBoard that represents all the squares on a particular rank.
  • Get the moves for a rook on a particular square, given blockers blocking my movement.
  • Get the rays for a rook on a particular square.
  • Create an iterator that interleaves elements in i and j.
  • Iterate iterable with a particular value inserted between each element.
  • Iterate iterable with a particular value created by a function inserted between each element.
  • Creates a new iterator that infinitely applies function to value and yields results.
  • Combine all iterator elements into one String, separated by sep.
  • Create an iterator that merges elements of the contained iterators using the ordering function.
  • Create an iterator that merges elements of the contained iterators.
  • Get a line (extending to infinity, which in chess is 8 squares), given two squares. This line does extend past the squares.
  • Return the maximum value of the iterable.
  • Create an iterator that merges elements in i and j.
  • Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
  • Return the minimum value of the iterable.
  • An iterator adaptor that allows the user to peek at multiple .next() values without advancing the base iterator.
  • Converts an iterator of tuples into a tuple of containers.
  • An iterator that generalizes .zip() and allows running multiple iterators in lockstep.
  • Partition a sequence using predicate pred so that elements that map to true are placed before elements which map to false.
  • A drop-in replacement for std::iter::Peekable which adds a peek_nth method allowing the user to peek at a value several iterations forward without advancing the base iterator.
  • “Lift” a function of the values of an iterator so that it can process an iterator of Result values instead.
  • Create an iterator where you can put back a single item
  • Create an iterator where you can put back multiple values to the front of the iteration.
  • Return an iterator inside a Rc<RefCell<_>> wrapper.
  • Create an iterator that produces n repetitions of element.
  • Iterate iterable in reverse.
  • Sort all iterator elements into a new iterator in ascending order.
  • Sort all iterator elements into a new iterator in ascending order. This sort is unstable (i.e., may reorder equal elements).
  • unfoldDeprecated
    Creates a new unfold source with the specified closure as the “iterator function” and an initial state to eventually pass to the closure
  • zipDeprecated
    Converts the arguments to iterators and zips them.
  • Zips two iterators but panics if they are not of the same length.

Type Aliases§

  • A Result for any binread function that can return an error
  • An iterator adaptor that may join together adjacent elements.
  • An iterator adaptor that removes repeated duplicates.
  • An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
  • An iterator adaptor that removes repeated duplicates, while keeping a count of how many repeated elements were present. This will determine equality using a comparison function.
  • An iterator adaptor that removes repeated duplicates, while keeping a count of how many repeated elements were present.
  • An iterator adapter to filter out duplicate elements.
  • An iterator adapter to filter for duplicate elements.
  • GroupByDeprecated
    See ChunkBy.
  • GroupingMapBy is an intermediate struct for efficient group-and-fold operations.
  • An iterator adaptor to insert a particular value between each element of the adapted iterator.
  • An iterator adaptor that merges an abitrary number of base iterators in ascending order. If all base iterators are sorted (ascending), the result is sorted.
  • An iterator adapter to apply Into conversion to each element.
  • An iterator adapter to apply a transformation within a nested Result::Ok.
  • An iterator adaptor that merges the two base iterators in ascending order. If both base iterators are sorted (ascending), the result is sorted.
  • An iterator adaptor that merge-joins items from the two base iterators in ascending order.

Derive Macros§