inital commit
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user