diff --git a/js/atree.js b/js/atree.js index 0a6151c..3e2f925 100644 --- a/js/atree.js +++ b/js/atree.js @@ -170,40 +170,23 @@ const atree_node = new (class extends ComputeNode { } node.parents = parents; } - console.log(atree_map); + let sccs = make_SCC_graph(atree_head, atree_map.values()); let atree_topo_sort = []; - topological_sort_tree(atree_head, atree_topo_sort, new Map()); - atree_topo_sort.reverse(); + for (const scc of sccs) { + for (const node of scc.nodes) { + delete node.visited; + delete node.assigned; + delete node.scc; + atree_topo_sort.push(node); + } + } + console.log("Approximate topological order ability tree:"); + console.log(atree_topo_sort); return atree_topo_sort; } })(); -/** - * Create a reverse topological sort of the tree in the result list. - * NOTE: our structure isn't a tree... it isn't even acyclic... but do it anyway i guess... - * - * https://en.wikipedia.org/wiki/Topological_sorting - * @param tree: Root of tree to sort - * @param res: Result list (reverse topological order) - * @param mark_state: Bookkeeping. Call with empty Map() - */ -function topological_sort_tree(tree, res, mark_state) { - const state = mark_state.get(tree); - if (state === undefined) { - // unmarked. - mark_state.set(tree, false); // temporary mark - for (const child of tree.children) { - topological_sort_tree(child, res, mark_state); - } - mark_state.set(tree, true); // permanent mark - res.push(tree); - } - // these cases are not needed. Case 1 does nothing, case 2 should never happen. - // else if (state === true) { return; } // permanent mark. - // else if (state === false) { throw "not a DAG"; } // temporary mark. -} - /** * Display ability tree from topologically sorted list. * diff --git a/js/skillpoints.js b/js/skillpoints.js index f83a47e..f57384d 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -223,21 +223,15 @@ function construct_scc_graph(items_to_consider) { let terminal_node = { item: null, children: [], - parents: nodes, - visited: false, - assigned: false, - scc: null + parents: nodes }; let root_node = { item: null, children: nodes, parents: [], - visited: false, - assigned: false, - scc: null }; for (const item of items_to_consider) { - nodes.push({item: item, children: [terminal_node], parents: [root_node], visited: false, assigned: false, scc: null}); + nodes.push({item: item, children: [terminal_node], parents: [root_node]}); } // Dependency graph construction. for (const node_a of nodes) { @@ -253,50 +247,6 @@ function construct_scc_graph(items_to_consider) { } } } - const res = [] - /* - * SCC graph construction. - * https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm - */ - function visit(u, res) { - if (u.visited) { return; } - u.visited = true; - for (const child of u.children) { - if (!child.visited) { visit(child, res); } - } - res.push(u); - } - visit(root_node, res); - res.reverse(); - const sccs = []; - function assign(node, cur_scc) { - if (node.assigned) { return; } - cur_scc.nodes.push(node); - node.scc = cur_scc; - node.assigned = true; - for (const parent of node.parents) { - assign(parent, cur_scc); - } - } - for (const node of res) { - if (node.assigned) { continue; } - const cur_scc = { - nodes: [], - children: new Set(), - parents: new Set() - }; - assign(node, cur_scc); - sccs.push(cur_scc); - } - for (const scc of sccs) { - for (const node of scc.nodes) { - for (const child of node.children) { - scc.children.add(child.scc); - } - for (const parent of node.parents) { - scc.parents.add(parent.scc); - } - } - } + const sccs = make_SCC_graph(root_node, nodes); return [root_node, terminal_node, sccs]; } diff --git a/js/utils.js b/js/utils.js index 2fa1c21..30cc395 100644 --- a/js/utils.js +++ b/js/utils.js @@ -889,3 +889,67 @@ function make_elem(type, classlist = [], args = {}) { } return ret_elem; } + +/** + * Nodes must have: + * node: { + * parents: List[node] + * children: List[node] + * } + * + * This function will define: "visited, assigned, scc" properties + * Assuming a connected graph. (only one root) + */ +function make_SCC_graph(root_node, nodes) { + for (const node of nodes) { + node.visited = false; + node.assigned = false; + node.scc = null; + } + const res = [] + /* + * SCC graph construction. + * https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm + */ + function visit(u, res) { + if (u.visited) { return; } + u.visited = true; + for (const child of u.children) { + if (!child.visited) { visit(child, res); } + } + res.push(u); + } + visit(root_node, res); + res.reverse(); + const sccs = []; + function assign(node, cur_scc) { + if (node.assigned) { return; } + cur_scc.nodes.push(node); + node.scc = cur_scc; + node.assigned = true; + for (const parent of node.parents) { + assign(parent, cur_scc); + } + } + for (const node of res) { + if (node.assigned) { continue; } + const cur_scc = { + nodes: [], + children: new Set(), + parents: new Set() + }; + assign(node, cur_scc); + sccs.push(cur_scc); + } + for (const scc of sccs) { + for (const node of scc.nodes) { + for (const child of node.children) { + scc.children.add(child.scc); + } + for (const parent of node.parents) { + scc.parents.add(parent.scc); + } + } + } + return sccs; +}