Threat Map version 0!
An idea I've had is a threat map. Ideally we could have a map that looks like a weather pattern map like above. Using a variety of factors such as:
- Pieces attacking the square.
- Pieces defending the square.
- If the piece is pinned or scewered.
The simplest logic is basically a Static Exchange Evaluation for each square on the board. As part of an explatory project with the Stockfish engine I got an initial version of that logic below.
As this is an ongoing project, it is possible there have been modifications to the github repository. Any references in this walkthrough refer to the following version: bdac8a95b1c2059683a078a59a66b1c471f230a9
In terms of interacting with the Stockfish engine, here is an overview of how Threatmap is implemented with, hopefully, some useful information on interacting with the C++ version of Stockfish.
Stockfish uses the Universal Chess Interface, which is implemented in the uci.h and uci.cpp files.
void UCI::loop(int argc, char *argv) runs
a loop that listens for various commands such as setting positions and evaluating moves etc.
else if (token == "go") go(pos, is, states); else if (token == "explain") explain(pos, is, states); else if (token == "position") position(pos, is, states);
In that method I added listening for an "explain" command with a basic function to then call logic in explainer.h and explainer.cpp.
To start I've made one class Explainer, which is loaded with a given position and ideally will support queries about said position, ideally with a visualiable output. So far, only a basic Threat Map is implemented.
The current Threat Map version exists in the
void Explainer::display_threatmap() which works based on
a given position. The Position class in the position.cpp file has a lot of exisiting logic that supports the initial
Threat Map. Below is a brief run down of the current methods. As the code/logic gets more solidified, obviously
these will be the basis for the code documentation for it.
int Explainer::piece_value(Stockfish::Piece piece)
Definitely a temporary method, but quickly gives the value of a piece. Used the standard 1 for pawn, 3 for knight and bishop, 5 for a rooke, 9 for a queen, and 99 for a king. This was used to sort attackers and defenders on a given square.
vector<Stockfish::Square> Explainer::squares_from_bb(Stockfish::Bitboard bb)
Another possible temporary method. This method convert a Bitboard into the corresponding Stockfish Square objects. Bitboards support fast boolean logic to determine board configurations, this method gives access to the squares to be able to inquire about pieces/etc.
Returns vector of piece values for a given selection of squares.
int Explainer::square_threat_level(Stockfish::Square square)
For a given square determines if it is occupied and if it is the same color as the current turn or not. Finds a Bitboard of all pieces attacking the square and all pieces defending the square. Then converts these into vectors of int values using the methods above. Then does a quick iteration until either the defending square runs out of defenders or until the attacking piece is "better" than the next defending piece (e.g. a rookie attacking a square defended by a bishop.) The total Threat Value to the square is the number of pieces that will lose an exchange on the square. This will be positive for squares good for the current player's turn (e.g. a hanging piece will have a positive value.) whereas this will be negative for squares where the current player's pieces are endangered.
The next step for the Threat Level on the square definitely needs to include pins/skewers and check threats.
From the existing codebase, here is an example of how to compile and generate a Threat Map.
make build ./stockfish position fen r1bqk2r/pp2bppp/2pp1n2/3B2B1/3QP3/2N5/PPP2PPP/R3K2R b KQkq - 1 9 explain position threatmap