Jump to first and last state

This commit is contained in:
2026-02-10 12:54:54 +01:00
parent ea23f0b73a
commit c4cbf42e73

View File

@@ -386,9 +386,9 @@
// --- STATE & HISTORY ---
let ws, myRole, selected = null;
let state = null; // Authoritative latest state
let history = []; // Array of state snapshots
let historyIndex = -1; // Current view index
let state = null;
let history = [];
let historyIndex = -1;
const pos = { 0: [80, 210], 1: [180, 110], 2: [180, 210], 3: [180, 310], 4: [280, 110], 5: [280, 210], 6: [280, 310], 7: [380, 110], 8: [380, 210], 9: [380, 310], 10: [480, 210] };
const edges = [[0, 1], [0, 2], [0, 3], [1, 2], [2, 3], [1, 4], [2, 5], [3, 6], [4, 5], [5, 6], [4, 7], [5, 8], [6, 9], [7, 8], [8, 9], [5, 1], [5, 3], [5, 7], [5, 9], [7, 10], [8, 10], [9, 10]];
@@ -398,7 +398,7 @@
function initLocalState() {
const startState = { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null, lastMove: null };
history = [];
updateHistory(startState);
updateHistory(startState, true);
document.getElementById("lobby").classList.add("hidden");
document.getElementById("game").classList.remove("hidden");
document.getElementById("roomInfo").style.visibility = "hidden";
@@ -406,15 +406,20 @@
document.getElementById("copyBtn").style.opacity = "0.5";
}
function updateHistory(newState) {
// If it's a reset (moveCount 0) or a new move, add to history
function updateHistory(newState, forceJump = false) {
const isAtEnd = historyIndex === history.length - 1;
if (newState.moveCount === 0) {
history = [JSON.parse(JSON.stringify(newState))];
} else if (!state || newState.moveCount > state.moveCount) {
history.push(JSON.parse(JSON.stringify(newState)));
}
state = newState;
historyIndex = history.length - 1; // Snap to newest
// Only jump the view to the latest state if the player was already at the end or if forced (like on restart)
if (isAtEnd || forceJump) {
historyIndex = history.length - 1;
}
render();
}
@@ -437,24 +442,36 @@
if (msg.type === "state") {
myRole = msg.yourRole;
document.getElementById("overlay").className = msg.onlineCount < 2 ? "" : "hidden";
updateHistory(msg.game);
updateHistory(msg.game, msg.game.moveCount === 0);
}
};
}
// Keyboard controls for back/forward
// Keyboard controls for Navigation
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowLeft" && historyIndex > 0) {
historyIndex--;
render();
} else if (e.key === "ArrowRight" && historyIndex < history.length - 1) {
historyIndex++;
render();
if (history.length === 0) return;
switch (e.key) {
case "ArrowLeft":
if (historyIndex > 0) historyIndex--;
break;
case "ArrowRight":
if (historyIndex < history.length - 1) historyIndex++;
break;
case "ArrowUp": // Jump to Present
historyIndex = history.length - 1;
break;
case "ArrowDown": // Jump to Start
historyIndex = 0;
break;
default:
return; // Exit if other key
}
selected = null; // Clear selection when moving through time
render();
});
function handleLocalMove(from, to) {
// If moving while viewing history, we branch off
const baseState = history[historyIndex];
const allowed = (baseState.turn === "wolf") ? bWolf[from] : bSheep[from];
if (!allowed?.includes(to) || baseState.sheep.includes(to) || baseState.wolf === to) return;
@@ -466,9 +483,9 @@
nextState.moveCount++;
nextState.winner = checkWinConditions(nextState);
// Truncate history if we were looking at the past
// Branch history
history = history.slice(0, historyIndex + 1);
updateHistory(nextState);
updateHistory(nextState, true);
}
function checkWinConditions(g) {
@@ -525,7 +542,6 @@
const type = isWolf ? "wolf" : (isSheep ? "sheep" : "empty");
const isWinnerPiece = (viewState.winner === "wolf" && isWolf) || (viewState.winner === "sheep" && isSheep);
// Logic for possible moves
const canInteract = isLocal || (!isViewingHistory && state.turn === myRole);
const isPossible = canInteract && selected !== null && (viewState.turn === "wolf" ? bWolf[selected] : bSheep[selected]).includes(i) && !isWolf && !isSheep;