Files
2026-06-12 16:06:03 -04:00

238 lines
6.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const STORAGE_KEY = "euchre-scoreboard";
const defaultState = () => ({
teamUsName: "Us",
teamThemName: "Them",
teamUsScore: 0,
teamThemScore: 0,
targetScore: 10,
loneEnabled: true,
history: [],
gameOver: false,
});
let state = loadState();
const els = {
teamUsName: document.getElementById("teamUsName"),
teamThemName: document.getElementById("teamThemName"),
teamUsScore: document.getElementById("teamUsScore"),
teamThemScore: document.getElementById("teamThemScore"),
targetScore: document.getElementById("targetScore"),
loneEnabled: document.getElementById("loneEnabled"),
historyList: document.getElementById("historyList"),
handCount: document.getElementById("handCount"),
undoBtn: document.getElementById("undoBtn"),
resetBtn: document.getElementById("resetBtn"),
settingsBtn: document.getElementById("settingsBtn"),
settingsPanel: document.getElementById("settingsPanel"),
closeSettings: document.getElementById("closeSettings"),
winnerOverlay: document.getElementById("winnerOverlay"),
winnerText: document.getElementById("winnerText"),
winnerScore: document.getElementById("winnerScore"),
newGameBtn: document.getElementById("newGameBtn"),
teamUsCard: document.getElementById("teamUsCard"),
teamThemCard: document.getElementById("teamThemCard"),
};
function loadState() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
return { ...defaultState(), ...JSON.parse(saved) };
}
} catch {
/* ignore corrupt storage */
}
return defaultState();
}
function saveState() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
function teamLabel(team) {
return team === "us" ? state.teamUsName : state.teamThemName;
}
function addScore(team, points, label) {
if (state.gameOver) return;
const entry = {
team,
points,
label,
teamUsScore: state.teamUsScore,
teamThemScore: state.teamThemScore,
timestamp: Date.now(),
};
if (team === "us") {
state.teamUsScore += points;
} else {
state.teamThemScore += points;
}
state.history.push(entry);
checkWinner();
saveState();
render();
}
function undoLast() {
if (!state.history.length) return;
const last = state.history.pop();
state.teamUsScore = last.teamUsScore;
state.teamThemScore = last.teamThemScore;
state.gameOver = false;
saveState();
render();
}
function resetGame(skipConfirm = false) {
if (
!skipConfirm &&
state.history.length &&
!confirm("Start a new game? Current scores will be lost.")
) {
return;
}
state = defaultState();
state.teamUsName = els.teamUsName.value.trim() || "Us";
state.teamThemName = els.teamThemName.value.trim() || "Them";
state.targetScore = Number(els.targetScore.value);
state.loneEnabled = els.loneEnabled.checked;
saveState();
render();
}
function checkWinner() {
const target = state.targetScore;
state.gameOver = state.teamUsScore >= target || state.teamThemScore >= target;
}
function renderHistory() {
const count = state.history.length;
els.handCount.textContent = `${count} hand${count === 1 ? "" : "s"}`;
if (!count) {
els.historyList.innerHTML = '<li class="history-empty">No hands played yet</li>';
return;
}
els.historyList.innerHTML = state.history
.slice()
.reverse()
.map((entry, i) => {
const handNum = count - i;
const name = teamLabel(entry.team);
return `<li>
<span>#${handNum}${name}: ${entry.label}</span>
<span class="history-points">+${entry.points}</span>
</li>`;
})
.join("");
}
function renderWinner() {
if (!state.gameOver) {
els.winnerOverlay.hidden = true;
els.teamUsCard.classList.remove("winner");
els.teamThemCard.classList.remove("winner");
return;
}
const usWins = state.teamUsScore >= state.targetScore;
const winner = usWins ? state.teamUsName : state.teamThemName;
const winnerScore = usWins ? state.teamUsScore : state.teamThemScore;
const loserScore = usWins ? state.teamThemScore : state.teamUsScore;
els.winnerText.textContent = `${winner} wins!`;
els.winnerScore.textContent = `${winnerScore} ${loserScore}`;
els.winnerOverlay.hidden = false;
if (usWins) {
els.teamUsCard.classList.add("winner");
els.teamThemCard.classList.remove("winner");
} else {
els.teamThemCard.classList.add("winner");
els.teamUsCard.classList.remove("winner");
}
}
function updateLoneButtons() {
document.querySelectorAll(".lone-btn").forEach((btn) => {
btn.classList.toggle("hidden", !state.loneEnabled);
});
}
function render() {
els.teamUsName.value = state.teamUsName;
els.teamThemName.value = state.teamThemName;
els.teamUsScore.textContent = state.teamUsScore;
els.teamThemScore.textContent = state.teamThemScore;
els.targetScore.value = String(state.targetScore);
els.loneEnabled.checked = state.loneEnabled;
els.undoBtn.disabled = !state.history.length;
updateLoneButtons();
renderHistory();
renderWinner();
document.querySelectorAll(".btn-score, .btn-euchre").forEach((btn) => {
btn.disabled = state.gameOver;
});
}
document.querySelectorAll(".btn-score, .btn-euchre").forEach((btn) => {
btn.addEventListener("click", () => {
addScore(btn.dataset.team, Number(btn.dataset.points), btn.dataset.label);
});
});
els.undoBtn.addEventListener("click", undoLast);
els.resetBtn.addEventListener("click", resetGame);
els.newGameBtn.addEventListener("click", () => resetGame(true));
els.settingsBtn.addEventListener("click", () => {
els.settingsPanel.hidden = !els.settingsPanel.hidden;
});
els.closeSettings.addEventListener("click", () => {
state.targetScore = Number(els.targetScore.value);
state.loneEnabled = els.loneEnabled.checked;
saveState();
render();
els.settingsPanel.hidden = true;
});
els.teamUsName.addEventListener("change", () => {
state.teamUsName = els.teamUsName.value.trim() || "Us";
saveState();
renderHistory();
});
els.teamThemName.addEventListener("change", () => {
state.teamThemName = els.teamThemName.value.trim() || "Them";
saveState();
renderHistory();
});
els.targetScore.addEventListener("change", () => {
state.targetScore = Number(els.targetScore.value);
checkWinner();
saveState();
render();
});
els.loneEnabled.addEventListener("change", () => {
state.loneEnabled = els.loneEnabled.checked;
saveState();
render();
});
checkWinner();
saveState();
render();