diff --git a/src/graph.js b/src/graph.js index 04b1e0e..88088d0 100644 --- a/src/graph.js +++ b/src/graph.js @@ -23,8 +23,10 @@ export const generate_graph = (data, node_click_handler) => { node_click_handler(node, graph); }); + // graph.forceEngine("ngraph"); + // graph.numDimensions([3]); graph.d3Force("link").distance(35); - // graph.warmupTicks([100]); + graph.warmupTicks([10]); // graph.cooldownTicks([0]); reset_graph_view(graph); diff --git a/src/main.js b/src/main.js index 1b6a695..e772d40 100644 --- a/src/main.js +++ b/src/main.js @@ -26,28 +26,44 @@ let graph = null; // Helpers // -export const key_of = (arr) => JSON.stringify(arr.board); +export const prepare = (state) => { + let _state = structuredClone(state); + + for (let i = 0; i < state.board.length; i++) { + // Remove IDs + _state.board[i].splice(0, 1); + } + // Sort, so we can compare keys + _state.board.sort(); + + return _state; +}; + +// Disable "prepare" to compare IDs aswell (explodes the state space though) +export const key_of = (state) => JSON.stringify(prepare(state).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); + add(state) { + const key = key_of(state); + if (!this._map.has(key)) { + this._map.set(key, state); + } return this; }, - has(arr) { - return this._map.has(key_of(arr)); + has(state) { + return this._map.has(key_of(state)); }, - get(k) { - return this._map.get(k); + get(key) { + return this._map.get(key); }, - delete(arr) { - return this._map.delete(key_of(arr)); + delete(state) { + return this._map.delete(key_of(state)); }, values() { @@ -135,19 +151,20 @@ document.getElementById("model_canvas").addEventListener("click", (event) => { }); const move = (direction) => { - if (selected_element === null) { + if (selected_element === null || graph === null) { return; } const [state, element] = model.move( states, - graph, + data, current_state, selected_element, DIRECTIONS[direction], ); if (state !== null) { + graph.graphData(data); current_state = state; selected_element = element; clear_visualization(); @@ -190,26 +207,22 @@ document.addEventListener("keyup", (event) => { // document.getElementById("select_model_button").addEventListener("click", () => { - clear_graph(); - clear_visualization(); current_model = (current_model + 1) % models.length; model = models[current_model]; current_initial_state = 0; initial_state = model.initial_states[current_initial_state]; - selected_element = null; current_state = initial_state; + selected_element = null; clear_states(); states.add(initial_state); - graph = null; - graph = generate_graph(data, node_click_view_state); + clear_graph(); + clear_visualization(); model.visualize(initial_state); document.getElementById("model_name").innerHTML = model.name; document.getElementById("state_name").innerHTML = initial_state.name; }); document.getElementById("select_state_button").addEventListener("click", () => { - clear_graph(); - clear_visualization(); current_initial_state = (current_initial_state + 1) % model.initial_states.length; initial_state = model.initial_states[current_initial_state]; @@ -217,8 +230,10 @@ document.getElementById("select_state_button").addEventListener("click", () => { selected_element = null; clear_states(); states.add(initial_state); - graph = null; - graph = generate_graph(data, node_click_view_state); + clear_graph(); + clear_visualization(); + // graph = null; + // graph = generate_graph(data, node_click_view_state); model.visualize(initial_state); document.getElementById("state_name").innerHTML = initial_state.name; }); diff --git a/src/simulator/restricted_sliding_blocks/model.js b/src/simulator/restricted_sliding_blocks/model.js index 52828f2..104ba6b 100644 --- a/src/simulator/restricted_sliding_blocks/model.js +++ b/src/simulator/restricted_sliding_blocks/model.js @@ -9,10 +9,7 @@ import { cross_three } from "./initial_states/cross_three.js"; import { get_moves, block_is_movable, - index_of_state, - remove_block, move_block, - insert_block, move_state_block, arrays_are_equal, } from "./state.js"; @@ -112,53 +109,27 @@ const move = (states, data, state, block, direction) => { return [null, null]; } - // let new_state = remove_block(state, block); - // let new_block = move_block(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); - let new_link = null; + const new_link = { + source: key_of(state), // We're coming from this state... + target: key_of(new_state), // ...and ended up here, at a previous state. + }; + let new_node = null; - if (states.has(new_state)) { - // We already had this state, just generate a link - new_link = { - source: key_of(state), // We're coming from this state... - target: key_of(new_state), // ...and ended up here, at a previous state. - }; - } else { + if (!states.has(new_state)) { states.add(new_state); new_node = { id: key_of(new_state), }; - new_link = { - source: key_of(state), // We're coming from this state... - target: key_of(new_state), // ...and ended up here, at a new state. - }; } - // TODO: Faster without this? - const has_link = (data, link) => { - // for (let l of data.links) { - // if (l.source.id === link.source && l.target.id === link.target) { - // return true; - // } - // if (l.source.id === link.target && l.target.id === link.source) { - // return true; - // } - // } - - return false; - }; - + data.links.push(new_link); if (new_node !== null) { data.nodes.push(new_node); - data.links.push(new_link); - } else if (!has_link(data, 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 04ddba4..a8e7327 100644 --- a/src/simulator/restricted_sliding_blocks/state.js +++ b/src/simulator/restricted_sliding_blocks/state.js @@ -24,80 +24,20 @@ export const arrays_are_equal = (array, other_array) => { return true; }; -export const state_contains_block = (state, block) => { - return ( - state.board.length >= block[0] && - arrays_are_equal(state.board[block[0]], block) - ); - - // for (let i = 0; i < state.board.length / 4; i++) { - // const other_block = state.board.slice(i * 4, (i + 1) * 4); - // - // if (arrays_are_equal(block, other_block)) { - // return true; - // } - // } - // - // return false; -}; - 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; - } + let _state = structuredClone(state); + let _other_state = structuredClone(other_state); + // Disable to compare IDs aswell (explodes the state space though) for (let i = 0; i < state.board.length; i++) { - if (!arrays_are_equal(state.board[i], other_state.board[i])) { - return false; - } + // Remove IDs + _state.board[i].splice(0, 1); + _other_state.board[i].splice(0, 1); } + _state.board.sort(); + _other_state.board.sort(); - return true; - - // for (let i = 0; i < state.board.length / 4; i++) { - // const block = state.board.slice(i * 4, (i + 1) * 4); - // - // if (!state_contains_block(other_state, block)) { - // return false; - // } - // } - // - // return true; -}; - -export const index_of_state = (states, state) => { - for (let i = 0; i < states.length; i++) { - if (states_are_equal(states[i], state)) { - return i; - } - } - - return null; -}; - -export const remove_block = (state, block) => { - let new_state = structuredClone(state); - delete new_state.name; - - new_state.board.splice(block[0], 1); - - return new_state; - - // for (let i = 0; i < state.board.length / 4; i++) { - // const other_block = state.board.slice(i * 4, (i + 1) * 4); - // - // if (arrays_are_equal(block, other_block)) { - // new_state.board.splice(i * 4, 4); - // } - // } - // - // return new_state; -}; - -export const insert_block = (state, block) => { - state.board.splice(block[0], 0, block); + return key_of(_state) === key_of(_other_state); }; export const move_block = (block, direction) => { diff --git a/src/simulator/sliding_blocks/model.js b/src/simulator/sliding_blocks/model.js index 3155898..f018d5f 100644 --- a/src/simulator/sliding_blocks/model.js +++ b/src/simulator/sliding_blocks/model.js @@ -7,10 +7,7 @@ import { klotski } from "./initial_states/klotski.js"; import { get_moves, block_is_movable, - index_of_state, - remove_block, move_block, - insert_block, move_state_block, } from "./state.js"; import { key_of } from "../../main.js"; @@ -46,6 +43,11 @@ const generate = (states, initial_state, graph, previous_move) => { last_print = states_after; } + // Arbitrary upper limit in case of errors + if (states_after > 100000) { + return; + } + if (states_after > states_before) { stack.push([new_state, m]); } @@ -80,10 +82,6 @@ const move = (states, data, state, block, direction) => { return [null, null]; } - // let new_state = remove_block(state, block); - // let new_block = move_block(block, direction); - // insert_block(new_state, block); - let new_state; try { new_state = structuredClone(state); @@ -95,44 +93,22 @@ const move = (states, data, state, block, direction) => { let new_block = move_block(block, direction); move_state_block(new_state, block, direction); - let new_link = null; + const new_link = { + source: key_of(state), // We're coming from this state... + target: key_of(new_state), // ...and ended up here, at a previous state. + }; + let new_node = null; - if (states.has(new_state)) { - // We already had this state, just generate a link - new_link = { - source: key_of(state), // We're coming from this state... - target: key_of(new_state), // ...and ended up here, at a previous state. - }; - } else { + if (!states.has(new_state)) { states.add(new_state); new_node = { id: key_of(new_state), }; - new_link = { - source: key_of(state), // We're coming from this state... - target: key_of(new_state), // ...and ended up here, at a new state. - }; } - // TODO: Faster without this? - const has_link = (data, link) => { - // for (let l of data.links) { - // if (l.source.id === link.source && l.target.id === link.target) { - // return true; - // } - // if (l.source.id === link.target && l.target.id === link.source) { - // return true; - // } - // } - - return false; - }; - + data.links.push(new_link); if (new_node !== null) { data.nodes.push(new_node); - data.links.push(new_link); - } else if (!has_link(data, 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 8f9828e..89e96a6 100644 --- a/src/simulator/sliding_blocks/state.js +++ b/src/simulator/sliding_blocks/state.js @@ -22,80 +22,23 @@ export const arrays_are_equal = (array, other_array) => { return true; }; -export const state_contains_block = (state, block) => { - return ( - state.board.length >= block[0] && - arrays_are_equal(state.board[block[0]], block) - ); - - // for (let i = 0; i < state.board.length / 4; i++) { - // const other_block = state.board.slice(i * 4, (i + 1) * 4); - // - // if (arrays_are_equal(block, other_block)) { - // return true; - // } - // } - // - // return false; -}; - 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; - } + let _state = structuredClone(state); + let _other_state = structuredClone(other_state); + // Disable to compare IDs aswell (explodes the state space though) for (let i = 0; i < state.board.length; i++) { - if (!arrays_are_equal(state.board[i], other_state.board[i])) { - return false; - } + // Remove IDs + _state.board[i].splice(0, 1); + _other_state.board[i].splice(0, 1); } - return true; + // TODO: Doesn't work + // Sort, so we can compare keys + _state.board.sort(); + _other_state.board.sort(); - // for (let i = 0; i < state.board.length / 4; i++) { - // const block = state.board.slice(i * 4, (i + 1) * 4); - // - // if (!state_contains_block(other_state, block)) { - // return false; - // } - // } - // - // return true; -}; - -export const index_of_state = (states, state) => { - for (let i = 0; i < states.length; i++) { - if (states_are_equal(states[i], state)) { - return i; - } - } - - return null; -}; - -export const remove_block = (state, block) => { - let new_state = structuredClone(state); - delete new_state.name; - - new_state.board.splice(block[0], 1); - - return new_state; - - // for (let i = 0; i < state.board.length / 4; i++) { - // const other_block = state.board.slice(i * 4, (i + 1) * 4); - // - // if (arrays_are_equal(block, other_block)) { - // new_state.board.splice(i * 4, 4); - // } - // } - // - // return new_state; -}; - -export const insert_block = (state, block) => { - state.board.splice(block[0], 0, block); + return key_of(_state) === key_of(_other_state); }; export const move_block = (block, direction) => {