inital commit

This commit is contained in:
2026-06-12 16:06:03 -04:00
commit bdb2f88b90
10 changed files with 1052 additions and 0 deletions
+237
View File
@@ -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();