diff --git a/index.html b/index.html
index 3e75d09..704b860 100644
--- a/index.html
+++ b/index.html
@@ -102,6 +102,11 @@
Invalid Guess ⚠️
diff --git a/scripts/main.js b/scripts/main.js
index 2e76d56..a57c4b3 100644
--- a/scripts/main.js
+++ b/scripts/main.js
@@ -18,7 +18,29 @@ let warningTimeout;
//The day Costcodle was launched. Used to find game number each day
const costcodleStartDate = new Date("09/21/2023");
-const gameNumber = getGameNumber();
+
+// Placeholder value until actual game count is loaded from games.json (10000 is chosen as it's
+// safely beyond any realistic game count, allowing initial URL validation to pass through)
+let MAX_GAME_NUMBER = 10000;
+
+// Get game number from URL parameter or use current date
+function getGameNumberFromURL() {
+ const urlParams = new URLSearchParams(window.location.search);
+ const dayParam = urlParams.get('day');
+
+ if (dayParam) {
+ const requestedDay = parseInt(dayParam, 10);
+ // Validate the day is within available range
+ if (!isNaN(requestedDay) && requestedDay >= 1 && requestedDay <= MAX_GAME_NUMBER) {
+ return requestedDay;
+ }
+ }
+
+ // Default to current day
+ return getGameNumber();
+}
+
+const gameNumber = getGameNumberFromURL();
//Elements with event listeners to play the game
const input = document.getElementById("guess-input");
@@ -53,7 +75,7 @@ const gameState = JSON.parse(localStorage.getItem("state")) || {
playGame();
function playGame() {
- fetchGameData(getGameNumber());
+ fetchGameData(gameNumber);
}
/*
@@ -65,6 +87,19 @@ function fetchGameData(gameNumber) {
fetch("./games.json")
.then((response) => response.json())
.then((json) => {
+ // Dynamically determine max game number from JSON keys
+ if (MAX_GAME_NUMBER === 10000) {
+ const gameKeys = Object.keys(json).filter(key => key.startsWith('game-'));
+ MAX_GAME_NUMBER = gameKeys.length;
+ }
+
+ // Validate that the requested game actually exists
+ if (!json[`game-${gameNumber}`]) {
+ console.warn(`Game #${gameNumber} not found, redirecting to current day`);
+ window.location.href = window.location.pathname; // Redirect to current day
+ return;
+ }
+
productName = json[`game-${gameNumber}`].name;
productPrice = json[`game-${gameNumber}`].price;
productPrice = Number(productPrice.slice(1, productPrice.length));
@@ -102,6 +137,9 @@ function initializeGame() {
} else {
convertToShareButton();
}
+
+ // Initialize navigation buttons
+ initializeNavigation();
}
function convertToShareButton() {
@@ -244,18 +282,24 @@ function copyStats() {
navigator.userAgent.match(/IEMobile/i) ||
navigator.userAgent.match(/Opera Mini/i);
+ // Calculate current day once for share URL logic
+ const currentDay = getGameNumber();
+ const shareUrl = gameNumber !== currentDay
+ ? `https://costcodle.com/?day=${gameNumber}`
+ : "https://costcodle.com";
+
if (isMobile) {
if (navigator.canShare) {
navigator
.share({
title: "COSTCODLE",
text: output,
- url: "https://costcodle.com",
+ url: shareUrl,
})
.catch((error) => console.error("Share failed:", error));
}
} else {
- output += `https://costcodle.com`;
+ output += shareUrl;
navigator.clipboard.writeText(output);
displayToast();
}
@@ -513,3 +557,57 @@ function getGameNumber() {
return Math.ceil(dayDifference) + 1;
}
+/*
+ Navigation functions for previous/next game
+*/
+
+function initializeNavigation() {
+ const prevButton = document.getElementById("prev-button");
+ const nextButton = document.getElementById("next-button");
+ const dayIndicator = document.getElementById("day-indicator");
+
+ // Verify elements exist before proceeding
+ if (!prevButton || !nextButton || !dayIndicator) {
+ console.error('Navigation elements not found');
+ return;
+ }
+
+ // Display current game number and date
+ const gameDate = new Date(costcodleStartDate.getTime() + (gameNumber - 1) * (1000 * 3600 * 24));
+ dayIndicator.textContent = `Game #${gameNumber}`;
+ dayIndicator.title = gameDate.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric'});
+
+ // Disable buttons at boundaries
+ prevButton.disabled = gameNumber <= 1;
+ nextButton.disabled = gameNumber >= MAX_GAME_NUMBER;
+
+ // Add event listeners
+ prevButton.addEventListener("click", navigateToPrevious);
+ nextButton.addEventListener("click", navigateToNext);
+ document.addEventListener("keydown", handleKeyboardNavigation);
+}
+
+function navigateToPrevious() {
+ if (gameNumber > 1) {
+ window.location.href = `?day=${gameNumber - 1}`;
+ }
+}
+
+function navigateToNext() {
+ if (gameNumber < MAX_GAME_NUMBER) {
+ window.location.href = `?day=${gameNumber + 1}`;
+ }
+}
+
+function handleKeyboardNavigation(event) {
+ // Only handle arrow keys when not typing in input fields
+ if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
+ return;
+ }
+
+ if (event.key === 'ArrowLeft') {
+ navigateToPrevious();
+ } else if (event.key === 'ArrowRight') {
+ navigateToNext();
+ }
+}
diff --git a/styles/game.css b/styles/game.css
index c40f71c..60b3d0b 100644
--- a/styles/game.css
+++ b/styles/game.css
@@ -246,3 +246,43 @@
border-radius: 8px;
background-color: rgb(0, 0, 0);
}
+
+/* Navigation container */
+#navigation-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 16px;
+ margin-bottom: 16px;
+ margin-top: 8px;
+}
+
+.nav-button {
+ font-family: "VT323", monospace;
+ font-size: 20px;
+ padding: 8px 16px;
+ color: white;
+ background-color: rgb(0, 96, 169);
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.nav-button:hover:not(:disabled) {
+ background-color: rgba(0, 96, 169, 0.8);
+}
+
+.nav-button:disabled {
+ background-color: rgb(173, 173, 173);
+ color: rgb(100, 100, 100);
+ cursor: not-allowed;
+}
+
+#day-indicator {
+ font-family: "VT323", monospace;
+ font-size: 18px;
+ color: rgb(50, 50, 50);
+ min-width: 120px;
+ text-align: center;
+}