diff --git a/src/graph.js b/src/graph.js index 6ee20ec..04b1e0e 100644 --- a/src/graph.js +++ b/src/graph.js @@ -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"; } diff --git a/src/main.js b/src/main.js index c77b615..1b6a695 100644 --- a/src/main.js +++ b/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); diff --git a/src/simulator/restricted_sliding_blocks/model.js b/src/simulator/restricted_sliding_blocks/model.js index 63a4e53..52828f2 100644 --- a/src/simulator/restricted_sliding_blocks/model.js +++ b/src/simulator/restricted_sliding_blocks/model.js @@ -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]; diff --git a/src/simulator/restricted_sliding_blocks/state.js b/src/simulator/restricted_sliding_blocks/state.js index 95191f8..04ddba4 100644 --- a/src/simulator/restricted_sliding_blocks/state.js +++ b/src/simulator/restricted_sliding_blocks/state.js @@ -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); diff --git a/src/simulator/sliding_blocks/model.js b/src/simulator/sliding_blocks/model.js index 069740c..3155898 100644 --- a/src/simulator/sliding_blocks/model.js +++ b/src/simulator/sliding_blocks/model.js @@ -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]; diff --git a/src/simulator/sliding_blocks/state.js b/src/simulator/sliding_blocks/state.js index e06518b..8f9828e 100644 --- a/src/simulator/sliding_blocks/state.js +++ b/src/simulator/sliding_blocks/state.js @@ -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);