Chess engine in zig
# Pistike Chess Engine

This engine is the zig implementation of
[Bitboard chess engine in C]https://www.youtube.com/playlist?list=PLmN0neTso3Jxh8ZIylk74JpwfiWNI76Cs.

## Architecture

### Basics
Every chess engine consists of 3 major parts:
- Move generator (which is tightly coupled with Board representation): Move generators
just generate all the possible moves for a given state.
- Evaluation: Evaluation tells our engine if a given state is good or bad for us. Is it worth
taking that piece? Should I move my pawn instead? etc.
- Search: A chess engine just dumbly creates every move and evaluates the position after
every move. Searching is about ruling out moves and ordering them to find the _best move_.

### Move generator

#### BitBoard
This engine uses a bitboard representation. A chess board got `8x8` squares, so a `u6`
number can represent the whole board.
You have to separate the pieces (pawn, knight, bishop, rook, queen, king) and colors, which
gives means 12 `u6` numbers.
It is also convenient to store the occupied squares for each side, that takes 2x`u6`.

see BitBoard and GameState in Board.zig

#### Attacks

Based on the rules for each piece You just generate all possibilities for a given square.
For leaper pieces (pawn, knight and king) it is not that hard, but the sliding pieces
(bishop, rook, and queen) have a lot of possibilities.
To calculate bishop and rook attacks the state of the art method is using
[Magic Bitboards]https://www.chessprogramming.org/Magic_Bitboards.

This engine uses the [Plain]https://www.chessprogramming.org/Magic_Bitboards#Plain method,
which is the simplest one.

The Attacks.zig file contains everything for attacks. Note, that we have to initialize
bishop and rook attacks with `initSliderAttacks()` before using them with `getBishopAttacks()`,
`getRookAttacks()` and `genQueenAttacks()`.

The `findMagicNumber()` function finds magic numbers. You should only run that once and
just paste it in Your code, it makes no sense to regenerate the same numbers every time.

#### Generating moves

We created the attacks, now it is time to apply them. The `generateMoves()` function in
`Board.zig` does exactly that. For every piece generates all the possible moves given
the current Board state.
Unlike the previous step this is for a given GameState, not some general rule.

The `isSquareAttacked()` helps to determine if a castling move is possible or not. We
will also use this function to discard invalid moves in next step.

#### Making moves

After generating all possible moves it is time to make those moves. We are still in
`Board.zig`, but we use the `makeMove()` function for that.

This function takes a move, updates `GameState`'s `bitboards` and `occupancies`,
sets the new side etc. The final result is a new `GameState`.

This function finally checks if that is a valid move (eg. the king is not in check)
with `isSquareAttacked()` and returns `true` or `false` accordingly.

##### Preserving and restoring  game states

When the chess engine makes a move the board state changes. We have to use `backup()` and
`restore()` functions to reset the states after a given move.
This is called the [Copy-Make]https://www.chessprogramming.org/Copy-Make method.

### Evaluation

In chess for years the only option for evaluation was a bunch of handcrafted rules. This
engine can use a _very_ simple table-based evaluation in `eval.zig`. That is the default
for development as its simplicity makes it predictable which makes debugging easier.

However You can replace `eval.evaluate()` in `search.zig` with `nnue.evaluate()`. Download
the nnue file from [here]https://tests.stockfishchess.org/api/nn/nn-62ef826d1a6d.nnue put
it next to the engine binary and it will use Stockfish's NNUE from that point.

NOTE: It can only read SFNNv1 files, so do not try to use later networks, they will not work.

### Search

Searching is the method of collecting all the possible moves and picking the best move.

#### Negamax

The base searching algorithm used here is [Negamax]https://www.chessprogramming.org/Negamax.

The basic search is enhanced with the following techniques:
 - [Alpha-beta pruning]https://www.chessprogramming.org/Alpha-Beta
 - [Checkmate and Stalemate detection]https://youtu.be/lAAdjCkWd9s
 - [Quiescence Search]https://www.chessprogramming.org/Quiescence_Search
 - [Move Scoring]https://www.chessprogramming.org/Move_Ordering
    - [MVV-MLA]https://www.chessprogramming.org/MVV-LVA
    - [Killer Move]https://www.chessprogramming.org/Killer_Move
    - [History Heuristics]https://www.chessprogramming.org/History_Heuristic
    - [Iterative Deepening]https://www.chessprogramming.org/Iterative_Deepening
    - [PV-Move]https://www.chessprogramming.org/PV-Move
    - [PVS - Principle Variation Search]https://web.archive.org/web/20071030220825/http://www.brucemo.com/compchess/programming/pvs.htm
    - [Late Move Reduction - LMR]https://www.chessprogramming.org/Late_Move_Reductions
    - [NULL move pruning]https://web.archive.org/web/20071031095933/http://www.brucemo.com/compchess/programming/nullmove.htm
    - Aspiration window

These fancy names may seem intimidating, but implementing them is not that hard.

## Interesting links

[Bruce Moreland]https://web.archive.org/web/20070922181832/http://www.brucemo.com/compchess/programming/index.htm
[Transposition Table Scoring]https://raw.githubusercontent.com/maksimKorzh/chess_programming/master/src/bbc/tt_search_mating_scores/TT_mate_scoring.txt

### NNUE
- [Using NNUE for Shogi translations]https://github.com/asdfjkl/nnue/
- [Stockfish NNUE layout]https://github.com/glinscott/nnue-pytorch/blob/master/docs/nnue.md
- [Neural Networks for Chess]https://github.com/asdfjkl/neural_network_chess

### NNUE readers
- [https://github.com/dshawul/nnue-probe]https://github.com/dshawul/nnue-probe
- [https://github.com/jdart1/nnue/]https://github.com/jdart1/nnue/

## TODO

### Before first release
- finnish BBC series
- use incremental nnue evaluation
- NULL move pruning: see comment below the [video]https://www.youtube.com/watch?v=n6xAzopULxU&list=PLmN0neTso3Jxh8ZIylk74JpwfiWNI76Cs&index=63, this is not a perfect solution

### Soon
- Check [SEE - Static Exchange Evaluation]https://web.archive.org/web/20070606043300/http://www.brucemo.com/compchess/programming/quiescent.htm

### Later
- train our own network and use that
- [Gigantua - fast movegen]https://www.codeproject.com/Articles/5313417/Worlds-fastest-Bitboard-Chess-Movegenerator