I was writing test cases for my quiescence search and happen to come across this position:
8 | |||||||||
7 | |||||||||
6 | |||||||||
5 | |||||||||
4 | |||||||||
3 | |||||||||
2 | |||||||||
1 | |||||||||
a | b | c | d | e | f | g | h |
8/8/6k1/5p1N/3p1Pp1/3K2P1/8/8 w - - 0 1
The quiescence routine is based on the pseudocode from chess programming wiki: https://www.chessprogramming.org/Quiescence_Search
At every depth of q-search I am generating all of the possible captures (not checks) and choose between the score of the best capture and the score of the current position. The evaluation function is based on piece-square tables.
In the position above, the q-search favors doing nothing over taking d4. Not seeing that the knight on h5 is lost.
So, a case of "if i don't give my opponent a turn, they cannot do bad things to me".
I can think of two possible ways to handle this better:
1. A more sophisticated evaluation function that is aware of hanging pieces. That would surely be way too expensive in terms of CPU time. Especially, compared to PSTs.
2. Allowing null moves in quiescence search. So, instead of evaluating the position as standing pat, we pass the move to the opponent and let them choose between doing nothing and taking a capture. That would increase the quiescence search tree somewhat. Obviously i'd need to limit the number of allowed null moves (is just one enough?) in order to avoid infinite recursive descent.
Which approach is better? Are there any other ways to handle it?
Alternatively, is this even a real problem to solve? I am assuming it must be - otherwise the engine would be susceptible to horizon-style effects.