Working on my chess engine, and studying Stockfish, I find some intriguing situations.
The chessboard contains a pointer to a StateInfo struct, which has a pointer prev pointing to the previous StateInfo.
When doing a move inside the search they just declare a local state and pass that to position.do_move().
I find that at least interesting. The “array” of StateInfo structs seems only to exist inside the (recursive) function callstack.
Would that same trick also work in ZIg?
const StateInfo = struct {
// fields...
prev: ?*StateInfo
}
Yes, not sure why you think it might not work?
It is possible that tail call optimisation could break it, but I doubt you’ll be doing such simple recursion, and I’m not sure if zig even enables that optimisation.
Yes, such a trick works. I used it for debug info in my code for parsing xkb keymaps:
pub const DebugInfo = struct {
parser: *Parser,
parent_info: ?*DebugInfo = null,
token_context: ?TokenIndex = null,
};
pub fn addDebugInfo(this: *@This(), parent_info: ?*DebugInfo, token: TokenIndex) DebugInfo {
return .{
.parser = this,
.parent_info = parent_info,
.token_context = token,
};
}
Then I create a new debug info variable whenever I want to add a level of context:
var debug_info = this.addDebugInfo(parent_debug_info, keyname_token_index);
I don’t think the variable needs to be mutable, it should be enough to use a const pointer; I don’t remember why I chose to use a mutable pointer in this case.
Ok clear.
I actually run into a different problem I think. That is why I thought it did not work.
When creating an Engine with init(), the addresses of pos.state and &engine.history[0] are not the same.
pub const Engine = struct
{
/// The one and only history for the position.
/// * Search threads use a position copy without history.
history: [1024]StateInfo,
/// The one and only root position.
pos: Position,
pub fn init() Engine
{
var engine: Engine = undefined;
engine.history = @splat(StateInfo.empty);
engine.pos = .new(&engine.history[0]); // put the state pointer in pos
return engine;
}
EDIT: when I do not use init() the addresses are the same.
pub fn uci_run() !void
{
var engine: search.Engine = undefined; // no init call
engine.history = @splat(.empty);
engine.pos = .new(&engine.history[0]);
defer engine.deinit();
while (true) { do our things... }
}
Because it was pointing to stack memory in init which is no longer valid when it finished and was overwritten
Yep I noticed it… Always ask “Where are the bytes?”