diff --git a/public/index.html b/public/index.html
index ef147fb..81b2cdc 100644
--- a/public/index.html
+++ b/public/index.html
@@ -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;