Merge pull request #63 from hppeng-wynn/smarter_compute_node

ComputeNode subclass that will defer updates if its value did not change
This commit is contained in:
hppeng-wynn 2022-06-30 23:07:23 -07:00 committed by GitHub
commit 91d295ef92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 16 deletions

View file

@ -124,16 +124,16 @@ const default_abils = {
/**
* Update ability tree internal representation. (topologically sorted node list)
*
* Signature: AbilityTreeUpdateNode(build: Build) => ATree (List of atree nodes in topological order)
* Signature: AbilityTreeUpdateNode(player-class: str) => ATree (List of atree nodes in topological order)
*/
const atree_node = new (class extends ComputeNode {
constructor() { super('builder-atree-update'); }
compute_func(input_map) {
if (input_map.size !== 1) { throw "AbilityTreeUpdateNode accepts exactly one input (build)"; }
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
if (input_map.size !== 1) { throw "AbilityTreeUpdateNode accepts exactly one input (player-class)"; }
const [player_class] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
const atree_raw = atrees[wep_to_class.get(build.weapon.statMap.get('type'))];
const atree_raw = atrees[player_class];
if (!atree_raw) return null;
let atree_map = new Map();

View file

@ -435,6 +435,17 @@ class BuildAssembleNode extends ComputeNode {
}
}
class PlayerClassNode extends ValueCheckComputeNode {
constructor(name) { super(name); }
compute_func(input_map) {
if (input_map.size !== 1) { throw "PlayerClassNode accepts exactly one input (build)"; }
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
return wep_to_class.get(build.weapon.statMap.get('type'));
}
}
/**
* Read an input field and parse into a list of powderings.
* Every two characters makes one powder. If parsing fails, NULL is returned.
@ -1085,8 +1096,9 @@ function builder_graph_init() {
// Phase 3/3: Set up atree stuff.
let class_node = new PlayerClassNode('builder-class').link_to(build_node);
// These two are defined in `atree.js`
atree_node.link_to(build_node, 'build');
atree_node.link_to(class_node, 'player-class');
atree_merge.link_to(build_node, 'build');
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
.link_to(atree_collect_spells, 'spells');

View file

@ -14,7 +14,10 @@ class ComputeNode {
this.name = name;
this.update_task = null;
this.fail_cb = false; // Set to true to force updates even if parent failed.
this.dirty = true;
this.dirty = 2; // 3 states:
// 2: dirty
// 1: possibly dirty
// 0: clean
this.inputs_dirty = new Map();
this.inputs_dirty_count = 0;
all_nodes.push(this);
@ -27,15 +30,17 @@ class ComputeNode {
if (this.inputs_dirty_count != 0) {
return;
}
if (!this.dirty) {
if (this.dirty === 0) {
return;
}
if (this.dirty == 2) {
let calc_inputs = new Map();
for (const input of this.inputs) {
calc_inputs.set(this.input_translation.get(input.name), input.value);
}
this.value = this.compute_func(calc_inputs);
this.dirty = false;
}
this.dirty = 0;
for (const child of this.children) {
child.mark_input_clean(this.name, this.value);
}
@ -64,12 +69,12 @@ class ComputeNode {
}
}
mark_dirty() {
if (!this.dirty) {
this.dirty = true;
mark_dirty(dirty_state=2) {
if (this.dirty < dirty_state) {
this.dirty = dirty_state;
for (const child of this.children) {
child.mark_input_dirty(this.name);
child.mark_dirty();
child.mark_dirty(dirty_state);
}
}
return this;
@ -125,6 +130,50 @@ class ComputeNode {
}
}
class ValueCheckComputeNode extends ComputeNode {
constructor(name) { super(name); }
/**
* Request update of this compute node. Pushes updates to children,
* but only if this node's value changed.
*/
update() {
if (this.inputs_dirty_count != 0) {
return;
}
if (this.dirty === 0) {
return;
}
let calc_inputs = new Map();
for (const input of this.inputs) {
calc_inputs.set(this.input_translation.get(input.name), input.value);
}
let val = this.compute_func(calc_inputs);
if (val !== this.value) {
this.mark_dirty(2);
}
else {
console.log("soft update");
}
this.value = val;
this.dirty = 0;
for (const child of this.children) {
child.mark_input_clean(this.name, this.value);
}
return this;
}
/**
* Defaulting to "dusty" state.
*/
mark_dirty(dirty_state="unused") {
return super.mark_dirty(1);
}
}
/**
* Schedule a ComputeNode to be updated.
*