I4AKZYZM5LOD7SNZRNWY34KWOHE34QPEWO6TIGHYMJACXOJKBMPQC
#### 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.
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)
These fancy names may seem intimidating, but implementing them is not that hard.