Closed sroelants closed 4 weeks ago
I wonder if I should have Position
be in charge of these? Change the structure of Position
to be:
struct Position {
board: Board,
history: [ZHash; 100],
hash: ZHash,
pawn_hash: ZHash,
// Eval specific stuff
phase: u8,
pawn_eval: Option<Score>,
incremental_eval: Eval,
}
impl Position {
pub fn incremental_eval(self) -> Score {}
pub fn volatile_eval(self, tt: &mut TTable) -> Score {}
pub fn pawn_eval(self, pawn_cache: &mut PawnCache) -> Score {}
}
The idea would be: if Position#play_move
processes a pawn move, it clears the pawn structure eval stored on the position, so it needs to get recomputed. Then we can either compute from scratch, or re-use a cached result, and store the result back in the cache afterwards.
I think I may have been overthinking it.
Instead of splitting the eval up into incremental
and volatile
, we simply only do the incremental work in every Position#play_move
call, and do the "volatile" work when the search calls Eval::total
.
That means we're not doing double work (when adding/removing), and we're getting the same speedup from TT hits as we would've if we'd only stored the volatile part.
Pawn cache might still be relevant, though, since we can cache those much more effectively. Though the relevance of the pawn cache only gets bigger when we start adding extra pawn-terms that might be used in other eval terms as well (pawn tropism? pawn attacks for mobility calculations?)
The big chunks that we could pull out, as far as I can tell, are:
1. Incremental eval
These are the bits that should get updated on a per-move basis, because we might only have to do a fraction of the work if we know what move the player made. E.g., if it was a rook move, we don't have to recalculate the pawn structure, bishop pair, or king safety terms. Since we need to compute the running total on every move anyway, there is no point storing this in the TT! Examples: Pawn shield, Pawn storm, Rook on open file, Bishop pair, etc...
2. Volatile eval
This is the part of the evaluation that has to be recomputed on every single move, and doesn't really benefit from being included in the incremental part. Because it gets computed from scratch every single time, this would benefit from getting cached. Examples: Mobility, King zone attacks, King virtual mobility, etc...
3. Pawn structure eval
This is a bit of an in-between. It benefits from the incremental updates (No need to recompute the pawn structure eval when moving a bishop), but is also imminently cacheable, since the pawn structure tends to be far more static.