Improve Generation Performance: Store states in HashMap and don't update the GraphData incrementally
This commit is contained in:
@ -56,7 +56,7 @@ export const reset_graph_view = (graph) => {
|
||||
|
||||
export const highlight_node = (states, graph, current_state) => {
|
||||
graph.nodeColor((node) => {
|
||||
if (states_are_equal(states[node.id], current_state)) {
|
||||
if (states_are_equal(states.get(node.id), current_state)) {
|
||||
return "#FF0000";
|
||||
}
|
||||
|
||||
|
52
src/main.js
52
src/main.js
@ -26,14 +26,49 @@ let graph = null;
|
||||
// Helpers
|
||||
//
|
||||
|
||||
export const key_of = (arr) => JSON.stringify(arr.board);
|
||||
|
||||
const clear_states = () => {
|
||||
states = {
|
||||
_map: new Map(),
|
||||
|
||||
add(arr) {
|
||||
const k = key_of(arr);
|
||||
if (!this._map.has(k)) this._map.set(k, arr);
|
||||
return this;
|
||||
},
|
||||
|
||||
has(arr) {
|
||||
return this._map.has(key_of(arr));
|
||||
},
|
||||
|
||||
get(k) {
|
||||
return this._map.get(k);
|
||||
},
|
||||
|
||||
delete(arr) {
|
||||
return this._map.delete(key_of(arr));
|
||||
},
|
||||
|
||||
values() {
|
||||
return [...this._map.values()];
|
||||
},
|
||||
|
||||
get length() {
|
||||
return this._map.size;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const clear_graph = () => {
|
||||
document.getElementById("graph").innerHTML = "";
|
||||
clear_visualization();
|
||||
model.visualize(initial_state);
|
||||
states = [initial_state];
|
||||
clear_states();
|
||||
states.add(initial_state);
|
||||
current_state = initial_state;
|
||||
data = {
|
||||
nodes: [{ id: 0 }],
|
||||
nodes: [{ id: key_of(current_state) }],
|
||||
links: [],
|
||||
};
|
||||
graph = null;
|
||||
@ -61,9 +96,10 @@ window.onload = () => {
|
||||
document.getElementById("state_name").innerHTML = initial_state.name;
|
||||
clear_visualization();
|
||||
model.visualize(initial_state);
|
||||
states = [initial_state];
|
||||
clear_states();
|
||||
states.add(initial_state);
|
||||
data = {
|
||||
nodes: [{ id: 0 }],
|
||||
nodes: [{ id: key_of(initial_state) }],
|
||||
links: [],
|
||||
};
|
||||
graph = generate_graph(data, node_click_view_state);
|
||||
@ -76,7 +112,7 @@ window.onload = () => {
|
||||
|
||||
const node_click_view_state = (node, graph) => {
|
||||
clear_visualization();
|
||||
current_state = states[node.id];
|
||||
current_state = states.get(node.id);
|
||||
|
||||
model.visualize(current_state);
|
||||
highlight_node(states, graph, current_state);
|
||||
@ -162,7 +198,8 @@ document.getElementById("select_model_button").addEventListener("click", () => {
|
||||
initial_state = model.initial_states[current_initial_state];
|
||||
selected_element = null;
|
||||
current_state = initial_state;
|
||||
states = [initial_state];
|
||||
clear_states();
|
||||
states.add(initial_state);
|
||||
graph = null;
|
||||
graph = generate_graph(data, node_click_view_state);
|
||||
model.visualize(initial_state);
|
||||
@ -178,7 +215,8 @@ document.getElementById("select_state_button").addEventListener("click", () => {
|
||||
initial_state = model.initial_states[current_initial_state];
|
||||
current_state = initial_state;
|
||||
selected_element = null;
|
||||
states = [initial_state];
|
||||
clear_states();
|
||||
states.add(initial_state);
|
||||
graph = null;
|
||||
graph = generate_graph(data, node_click_view_state);
|
||||
model.visualize(initial_state);
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
} from "./state.js";
|
||||
import { DIRECTIONS, LEFT, RIGHT, UP, DOWN } from "../../constants.js";
|
||||
import { HORIZONTAL, VERTICAL } from "../../constants.js";
|
||||
import { key_of } from "../../main.js";
|
||||
|
||||
const initial_states = [
|
||||
single,
|
||||
@ -35,6 +36,8 @@ const generate = (states, initial_state, graph, previous_move) => {
|
||||
|
||||
let last_print = 0;
|
||||
|
||||
let data = graph.graphData();
|
||||
|
||||
while (stack.length > 0) {
|
||||
const [current_state, prev_move] = stack.pop();
|
||||
|
||||
@ -45,7 +48,7 @@ const generate = (states, initial_state, graph, previous_move) => {
|
||||
const states_before = states.length;
|
||||
const [new_state, new_block] = move(
|
||||
states,
|
||||
graph,
|
||||
data,
|
||||
current_state,
|
||||
block,
|
||||
direction,
|
||||
@ -62,6 +65,8 @@ const generate = (states, initial_state, graph, previous_move) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph.graphData(data);
|
||||
};
|
||||
|
||||
const select = (state, offsetX, offsetY) => {
|
||||
@ -84,7 +89,7 @@ const select = (state, offsetX, offsetY) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const move = (states, graph, state, block, direction) => {
|
||||
const move = (states, data, state, block, direction) => {
|
||||
if (
|
||||
block[1] === HORIZONTAL &&
|
||||
(arrays_are_equal(direction, DIRECTIONS[UP]) ||
|
||||
@ -112,33 +117,29 @@ const move = (states, graph, state, block, direction) => {
|
||||
// insert_block(new_state, block);
|
||||
|
||||
let new_state = structuredClone(state);
|
||||
delete new_state.name;
|
||||
let new_block = move_block(block, direction);
|
||||
move_state_block(new_state, block, direction);
|
||||
|
||||
// TODO: Make states into a hashmap?
|
||||
let index = index_of_state(states, new_state);
|
||||
|
||||
let new_link = null;
|
||||
let new_node = null;
|
||||
if (index !== null) {
|
||||
if (states.has(new_state)) {
|
||||
// We already had this state, just generate a link
|
||||
new_link = {
|
||||
source: index_of_state(states, state), // We're coming from this state...
|
||||
target: index, // ...and ended up here, at a previous state.
|
||||
source: key_of(state), // We're coming from this state...
|
||||
target: key_of(new_state), // ...and ended up here, at a previous state.
|
||||
};
|
||||
} else {
|
||||
states.push(new_state);
|
||||
states.add(new_state);
|
||||
new_node = {
|
||||
id: states.length - 1,
|
||||
id: key_of(new_state),
|
||||
};
|
||||
new_link = {
|
||||
source: index_of_state(states, state), // We're coming from this state...
|
||||
target: states.length - 1, // ...and ended up here, at a new state.
|
||||
source: key_of(state), // We're coming from this state...
|
||||
target: key_of(new_state), // ...and ended up here, at a new state.
|
||||
};
|
||||
}
|
||||
|
||||
const data = graph.graphData();
|
||||
|
||||
// TODO: Faster without this?
|
||||
const has_link = (data, link) => {
|
||||
// for (let l of data.links) {
|
||||
@ -154,15 +155,10 @@ const move = (states, graph, state, block, direction) => {
|
||||
};
|
||||
|
||||
if (new_node !== null) {
|
||||
graph.graphData({
|
||||
nodes: [...data.nodes, new_node],
|
||||
links: [...data.links, new_link],
|
||||
});
|
||||
data.nodes.push(new_node);
|
||||
data.links.push(new_link);
|
||||
} else if (!has_link(data, new_link)) {
|
||||
graph.graphData({
|
||||
nodes: data.nodes,
|
||||
links: [...data.links, new_link],
|
||||
});
|
||||
data.links.push(new_link);
|
||||
}
|
||||
|
||||
return [new_state, new_block];
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
HORIZONTAL,
|
||||
VERTICAL,
|
||||
} from "../../constants.js";
|
||||
import { key_of } from "../../main.js";
|
||||
|
||||
export const arrays_are_equal = (array, other_array) => {
|
||||
if (array.length != other_array.length) {
|
||||
@ -41,6 +42,8 @@ export const state_contains_block = (state, block) => {
|
||||
};
|
||||
|
||||
export const states_are_equal = (state, other_state) => {
|
||||
return key_of(state) === key_of(other_state);
|
||||
|
||||
if (state.board.length != other_state.board.length) {
|
||||
return false;
|
||||
}
|
||||
@ -76,6 +79,7 @@ export const index_of_state = (states, state) => {
|
||||
|
||||
export const remove_block = (state, block) => {
|
||||
let new_state = structuredClone(state);
|
||||
delete new_state.name;
|
||||
|
||||
new_state.board.splice(block[0], 1);
|
||||
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
insert_block,
|
||||
move_state_block,
|
||||
} from "./state.js";
|
||||
import { key_of } from "../../main.js";
|
||||
|
||||
const initial_states = [single, two, three, four, small_klotski_like, klotski];
|
||||
|
||||
@ -21,6 +22,8 @@ const generate = (states, initial_state, graph, previous_move) => {
|
||||
|
||||
let last_print = 0;
|
||||
|
||||
let data = graph.graphData();
|
||||
|
||||
while (stack.length > 0) {
|
||||
const [current_state, prev_move] = stack.pop();
|
||||
|
||||
@ -31,7 +34,7 @@ const generate = (states, initial_state, graph, previous_move) => {
|
||||
const states_before = states.length;
|
||||
const [new_state, new_block] = move(
|
||||
states,
|
||||
graph,
|
||||
data,
|
||||
current_state,
|
||||
block,
|
||||
direction,
|
||||
@ -48,6 +51,8 @@ const generate = (states, initial_state, graph, previous_move) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph.graphData(data);
|
||||
};
|
||||
|
||||
const select = (state, offsetX, offsetY) => {
|
||||
@ -70,7 +75,7 @@ const select = (state, offsetX, offsetY) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const move = (states, graph, state, block, direction) => {
|
||||
const move = (states, data, state, block, direction) => {
|
||||
if (!block_is_movable(state, block, direction)) {
|
||||
return [null, null];
|
||||
}
|
||||
@ -82,6 +87,7 @@ const move = (states, graph, state, block, direction) => {
|
||||
let new_state;
|
||||
try {
|
||||
new_state = structuredClone(state);
|
||||
delete new_state.name; // Only need this for the initial state
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [null, null];
|
||||
@ -89,30 +95,25 @@ const move = (states, graph, state, block, direction) => {
|
||||
let new_block = move_block(block, direction);
|
||||
move_state_block(new_state, block, direction);
|
||||
|
||||
// TODO: Make states into a hashmap?
|
||||
let index = index_of_state(states, new_state);
|
||||
|
||||
let new_link = null;
|
||||
let new_node = null;
|
||||
if (index !== null) {
|
||||
if (states.has(new_state)) {
|
||||
// We already had this state, just generate a link
|
||||
new_link = {
|
||||
source: index_of_state(states, state), // We're coming from this state...
|
||||
target: index, // ...and ended up here, at a previous state.
|
||||
source: key_of(state), // We're coming from this state...
|
||||
target: key_of(new_state), // ...and ended up here, at a previous state.
|
||||
};
|
||||
} else {
|
||||
states.push(new_state);
|
||||
states.add(new_state);
|
||||
new_node = {
|
||||
id: states.length - 1,
|
||||
id: key_of(new_state),
|
||||
};
|
||||
new_link = {
|
||||
source: index_of_state(states, state), // We're coming from this state...
|
||||
target: states.length - 1, // ...and ended up here, at a new state.
|
||||
source: key_of(state), // We're coming from this state...
|
||||
target: key_of(new_state), // ...and ended up here, at a new state.
|
||||
};
|
||||
}
|
||||
|
||||
const data = graph.graphData();
|
||||
|
||||
// TODO: Faster without this?
|
||||
const has_link = (data, link) => {
|
||||
// for (let l of data.links) {
|
||||
@ -128,15 +129,10 @@ const move = (states, graph, state, block, direction) => {
|
||||
};
|
||||
|
||||
if (new_node !== null) {
|
||||
graph.graphData({
|
||||
nodes: [...data.nodes, new_node],
|
||||
links: [...data.links, new_link],
|
||||
});
|
||||
data.nodes.push(new_node);
|
||||
data.links.push(new_link);
|
||||
} else if (!has_link(data, new_link)) {
|
||||
graph.graphData({
|
||||
nodes: data.nodes,
|
||||
links: [...data.links, new_link],
|
||||
});
|
||||
data.links.push(new_link);
|
||||
}
|
||||
|
||||
return [new_state, new_block];
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
DIRECTIONS,
|
||||
invert_direction,
|
||||
} from "../../constants.js";
|
||||
import { key_of } from "../../main.js";
|
||||
|
||||
export const arrays_are_equal = (array, other_array) => {
|
||||
if (array.length != other_array.length) {
|
||||
@ -39,6 +40,8 @@ export const state_contains_block = (state, block) => {
|
||||
};
|
||||
|
||||
export const states_are_equal = (state, other_state) => {
|
||||
return key_of(state) === key_of(other_state);
|
||||
|
||||
if (state.board.length != other_state.board.length) {
|
||||
return false;
|
||||
}
|
||||
@ -74,6 +77,7 @@ export const index_of_state = (states, state) => {
|
||||
|
||||
export const remove_block = (state, block) => {
|
||||
let new_state = structuredClone(state);
|
||||
delete new_state.name;
|
||||
|
||||
new_state.board.splice(block[0], 1);
|
||||
|
||||
|
Reference in New Issue
Block a user