diff --git a/src/50_table.js b/src/50_table.js index e79f4131..42f2adcc 100644 --- a/src/50_table.js +++ b/src/50_table.js @@ -17,23 +17,35 @@ const table_prototype = { this.nps = 0; // Stat sent by engine this.tbhits = 0; // Stat sent by engine this.time = 0; // Stat sent by engine + this.terminal = null; // null = unknown, "" = not terminal, "Non-empty string" = terminal reason this.eval = null; // Used by grapher only, value from White's POV - this.eval_nodes = 0; // Number of search nodes used to generate the eval + this.eval_version = 0; // Which version (above) was used to generate the eval this.already_autopopulated = false; }, - update_eval_from_move: function(move) { - - // move should be the best move - - let info = this.moveinfo[move]; - - if (!info || info.__ghost) return; - - // if (info.uci_nodes < this.eval_nodes) return; // This can feel unintuitive. + get_eval: function() { + if (this.eval_version === this.version) { + return this.eval; + } else { + let info = SortedMoveInfoFromTable(this)[0]; + if (info && !info.__ghost) { + this.eval = info.board.active === "w" ? info.value() : 1 - info.value(); + } else { + this.eval = null; + } + this.eval_version = this.version; + return this.eval; + } + }, - this.eval = info.board.active === "w" ? info.value() : 1 - info.value(); - this.eval_nodes = info.uci_nodes; + set_terminal_info: function(reason, ev) { // ev is ignored if reason is "" (i.e. not a terminal position) + if (reason) { + this.terminal = reason; + this.eval = ev; + this.eval_version = this.version; + } else { + this.terminal = ""; + } }, autopopulate: function(node) { diff --git a/src/51_node.js b/src/51_node.js index 741188b2..b6e4099c 100644 --- a/src/51_node.js +++ b/src/51_node.js @@ -28,7 +28,6 @@ function NewNode(parent, move, board_for_root) { // move must be legal; board i node.table = NewTable(); node.searchmoves = []; node.__nice_move = null; - node.__terminal = null; node.destroyed = false; node.children = []; @@ -132,7 +131,7 @@ const node_prototype = { let node = this; while (node) { - ret.push(node.table.eval); + ret.push(node.table.get_eval()); node = node.parent; } @@ -353,34 +352,29 @@ const node_prototype = { // Returns "" if not a terminal position, otherwise returns the reason. // Also updates table.eval (for the graph) if needed. - if (typeof this.__terminal === "string") { - return this.__terminal; + if (typeof this.table.terminal === "string") { + return this.table.terminal; } let board = this.board; if (board.no_moves()) { if (board.king_in_check()) { - this.__terminal = "Checkmate"; // The PGN writer checks for this exact string! (Lame...) - this.table.eval = board.active === "w" ? 0 : 1; + this.table.set_terminal_info("Checkmate", board.active === "w" ? 0 : 1); // The PGN writer checks for this exact string! (Lame...) } else { - this.__terminal = "Stalemate"; - this.table.eval = 0.5; + this.table.set_terminal_info("Stalemate", 0.5); } } else if (board.insufficient_material()) { - this.__terminal = "Insufficient Material"; - this.table.eval = 0.5; + this.table.set_terminal_info("Insufficient Material", 0.5); } else if (board.halfmove >= 100) { - this.__terminal = "50 Move Rule"; - this.table.eval = 0.5; + this.table.set_terminal_info("50 Move Rule", 0.5); } else if (this.is_triple_rep()) { - this.__terminal = "Triple Repetition"; - this.table.eval = 0.5; + this.table.set_terminal_info("Triple Repetition", 0.5); } else { - this.__terminal = ""; + this.table.set_terminal_info("", null); } - return this.__terminal; + return this.table.terminal; }, validate_searchmoves: function(arr) { @@ -488,7 +482,6 @@ function __clean_tree(node) { while (node.children.length === 1) { node.table.clear(); - node.__terminal = null; node.searchmoves = []; node = node.children[0]; } @@ -496,7 +489,6 @@ function __clean_tree(node) { // Recursive when necessary... node.table.clear(); - node.__terminal = null; node.searchmoves = []; for (let child of node.children) { diff --git a/src/52_sorted_moves.js b/src/52_sorted_moves.js index c32af379..3d259439 100644 --- a/src/52_sorted_moves.js +++ b/src/52_sorted_moves.js @@ -2,6 +2,15 @@ function SortedMoveInfo(node) { + if (!node || node.destroyed) { + return []; + } + + return SortedMoveInfoFromTable(node.table); +} + +function SortedMoveInfoFromTable(table) { + // There are a lot of subtleties around sorting the moves... // // - We want to allow other engines than Lc0. @@ -10,15 +19,11 @@ function SortedMoveInfo(node) { // - We want to work with searchmoves, which is bound to leave stale info in the table. // - We can try and track the age of the data by various means, but these are fallible. - if (!node || node.destroyed) { - return []; - } - let info_list = []; let latest_cycle = 0; let latest_subcycle = 0; - for (let o of Object.values(node.table.moveinfo)) { + for (let o of Object.values(table.moveinfo)) { info_list.push(o); if (o.cycle > latest_cycle) latest_cycle = o.cycle; if (o.subcycle > latest_subcycle) latest_subcycle = o.subcycle; diff --git a/src/95_hub.js b/src/95_hub.js index dfa47cc7..8ed49aaa 100644 --- a/src/95_hub.js +++ b/src/95_hub.js @@ -440,7 +440,6 @@ let hub_props = { this.tick++; this.draw(); this.purge_finished_loaders(); - this.update_graph_eval(this.engine.search_running.node); // Possibly null. this.maybe_save_window_size(); setTimeout(this.spin.bind(this), config.update_delay); }, @@ -449,16 +448,6 @@ let hub_props = { this.loaders = this.loaders.filter(o => o.callback); }, - update_graph_eval: function(node) { - if (!node || node.destroyed) { - return; - } - let info = SortedMoveInfo(node)[0]; // Possibly undefined. - if (info) { - node.table.update_eval_from_move(info.move); - } - }, - maybe_save_window_size: function() { if (this.window_resize_time && performance.now() - this.window_resize_time > 1000) { this.window_resize_time = null; @@ -883,8 +872,6 @@ let hub_props = { receive_bestmove: function(s, relevant_node) { - this.update_graph_eval(relevant_node); // Now's the last chance to update our graph eval for this node. - let ok; // Could be used by 2 different parts of the switch (but not at time of writing...) switch (config.behaviour) {