234 lines
6.0 KiB
JavaScript
234 lines
6.0 KiB
JavaScript
import { Vec2 } from './vec2.js';
|
|
import { N, SQR_TYPE, Maze, AnalyzeContext, State } from './pushbox.js';
|
|
|
|
class BoardSquare {
|
|
constructor(attach_element) {
|
|
this.e = attach_element ?? document.createElement('div');
|
|
this.e.classList.add('fixtl', 'maze-sqr');
|
|
this.e.style.top = '0';
|
|
this.e.style.left = '0';
|
|
this.type = null;
|
|
}
|
|
|
|
moveTo(x, y) {
|
|
this.e.style.top = `${4 * x}vh`;
|
|
this.e.style.left = `${4 * y}vh`;
|
|
return this;
|
|
}
|
|
|
|
moveToV(v) {
|
|
return this.moveTo(v.x, v.y);
|
|
}
|
|
|
|
setType(type) {
|
|
if (this.type) {
|
|
this.e.classList.remove(`sqr-${this.type}`);
|
|
}
|
|
this.type = type;
|
|
this.e.classList.add(`sqr-${this.type}`);
|
|
return this;
|
|
}
|
|
};
|
|
|
|
|
|
class BoardUI {
|
|
constructor() {
|
|
this.maze = document.getElementById('maze');
|
|
this.target = new BoardSquare(document.getElementById('target'));
|
|
this.box = new BoardSquare(document.getElementById('box'));
|
|
this.player = new BoardSquare(document.getElementById('player'));
|
|
this.squares = [];
|
|
}
|
|
|
|
drawMaze(maze) {
|
|
while (this.maze.hasChildNodes()) {
|
|
this.maze.removeChild(this.maze.lastChild);
|
|
}
|
|
|
|
this.squares = [];
|
|
for (let i = 0; i < N; ++i) {
|
|
for (let j = 0; j < N; ++j) {
|
|
let sqr = new BoardSquare();
|
|
sqr.moveTo(i, j);
|
|
if (maze.get(i, j) == SQR_TYPE.WALL) {
|
|
sqr.setType('wall');
|
|
} else {
|
|
sqr.setType('space');
|
|
}
|
|
this.maze.appendChild(sqr.e);
|
|
}
|
|
}
|
|
this.target.moveToV(maze.target);
|
|
}
|
|
|
|
updateState(state) {
|
|
this.player.moveToV(state.player);
|
|
this.box.moveToV(state.box);
|
|
}
|
|
};
|
|
|
|
function fillTemplate(id, val) {
|
|
document.getElementById(id).innerHTML = val.toString();
|
|
}
|
|
|
|
function updateProgressBar(p) {
|
|
document.getElementById('progress-bar-fg').style.width = `${p}%`;
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const charmap_input = document.getElementById('maze-charmap');
|
|
const analyze_button = document.getElementById('analyze');
|
|
const analyze_status_element = document.getElementById('analyze-status');
|
|
let board = new BoardUI();
|
|
let maze = null, analyze_res = null, current_progress = 0;
|
|
let autoplay_timer = null;
|
|
let current_worker = null;
|
|
|
|
function stopAutplay() {
|
|
if (autoplay_timer != null) {
|
|
clearInterval(autoplay_timer);
|
|
autoplay_timer = null;
|
|
fillTemplate('progressctl-auto', 'Play');
|
|
}
|
|
}
|
|
|
|
function resetAnalyze() {
|
|
stopAutplay();
|
|
analyze_status_element.innerHTML = 'Not Analyzed';
|
|
fillTemplate('analyze-steps', 'N/A');
|
|
fillTemplate('current-progress', '0');
|
|
fillTemplate('full-progress', '0');
|
|
fillTemplate('progress-remain', '-0');
|
|
updateProgressBar(0);
|
|
current_progress = 0;
|
|
analyze_res = null;
|
|
}
|
|
|
|
function moveProgress(d) {
|
|
if (analyze_res == null || analyze_res.step == -1) {
|
|
return;
|
|
}
|
|
|
|
current_progress += d;
|
|
if (current_progress < 0) {
|
|
current_progress = 0;
|
|
}
|
|
|
|
if (current_progress > analyze_res) {
|
|
current_progress = analyze_res.step;
|
|
}
|
|
|
|
let st = analyze_res.path[current_progress];
|
|
board.updateState(st);
|
|
fillTemplate('current-progress', current_progress.toString());
|
|
fillTemplate('progress-remain', `-${analyze_res.step - current_progress}`);
|
|
updateProgressBar(current_progress * 100 / analyze_res.step);
|
|
}
|
|
|
|
function importMaze(charmap_val) {
|
|
let charmp = charmap_val.split('\n').map(x => x.trimEnd());
|
|
maze = Maze.fromCharMap(charmp);
|
|
board.drawMaze(maze);
|
|
board.updateState(maze.init);
|
|
analyze_button.disabled = false;
|
|
resetAnalyze();
|
|
}
|
|
|
|
if (localStorage.getItem('charmap-cache') != null) {
|
|
charmap_input.value = localStorage.getItem('charmap-cache');
|
|
importMaze(charmap_input.value);
|
|
} else {
|
|
analyze_button.disabled = true;
|
|
}
|
|
|
|
document.getElementById('charmap_import').addEventListener('click', function() {
|
|
localStorage.setItem('charmap-cache', charmap_input.value);
|
|
importMaze(charmap_input.value);
|
|
});
|
|
|
|
document.getElementById('progressctl-next').addEventListener('click', function() {
|
|
moveProgress(1);
|
|
});
|
|
|
|
document.getElementById('progressctl-prev').addEventListener('click', function() {
|
|
moveProgress(-1);
|
|
});
|
|
|
|
document.addEventListener('keydown', function(event) {
|
|
if (event.isComposing || event.keyCode === 229) {
|
|
return;
|
|
}
|
|
|
|
if (event.key === 'ArrowLeft') {
|
|
moveProgress(-1);
|
|
} else if (event.key === 'ArrowRight') {
|
|
moveProgress(1);
|
|
}
|
|
});
|
|
|
|
document.getElementById('progressctl-auto').addEventListener('click', function() {
|
|
if (autoplay_timer == null) {
|
|
autoplay_timer = setInterval(() => {
|
|
if (current_progress == analyze_res.step) {
|
|
stopAutplay();
|
|
return;
|
|
}
|
|
moveProgress(1);
|
|
}, 500);
|
|
fillTemplate('progressctl-auto', 'Pause');
|
|
} else {
|
|
stopAutplay();
|
|
}
|
|
});
|
|
|
|
document.getElementById('progress-bar').addEventListener('click', function(e) {
|
|
stopAutplay();
|
|
const bar = document.getElementById('progress-bar');
|
|
let p = (e.clientX - bar.getBoundingClientRect().x) / bar.clientWidth;
|
|
if (analyze_res != null && analyze_res.step != -1) {
|
|
current_progress = Math.round(analyze_res.step * p);
|
|
moveProgress(0);
|
|
}
|
|
});
|
|
|
|
document.getElementById('progressctl-reset').addEventListener('click', function() {
|
|
stopAutplay();
|
|
moveProgress(-current_progress);
|
|
});
|
|
|
|
analyze_button.addEventListener('click', function() {
|
|
resetAnalyze();
|
|
if (current_worker != null) {
|
|
current_worker.terminate();
|
|
current_worker = null;
|
|
analyze_button.innerHTML = 'Analyze';
|
|
return;
|
|
}
|
|
|
|
if (maze == null) {
|
|
return;
|
|
}
|
|
|
|
current_worker = new Worker('solver.js?v=7', { type: 'module' });
|
|
analyze_status_element.innerHTML = 'Dispatching';
|
|
analyze_button.innerHTML = 'Stop';
|
|
current_worker.onmessage = (msg_r) => {
|
|
let msg = msg_r.data;
|
|
if (msg.what === 'started') {
|
|
analyze_status_element.innerHTML = 'Analyzing';
|
|
} else if (msg.what == 'done') {
|
|
analyze_res = msg.value;
|
|
current_worker.terminate();
|
|
current_worker = null;
|
|
analyze_button.innerHTML = 'Analyze';
|
|
analyze_status_element.innerHTML = 'Analyze Done';
|
|
fillTemplate('full-progress', analyze_res.step);
|
|
fillTemplate('analyze-steps', analyze_res.step);
|
|
fillTemplate('progress-remain', `-${analyze_res.step}`);
|
|
}
|
|
};
|
|
current_worker.postMessage(maze);
|
|
});
|
|
});
|
|
|