-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
366 lines (319 loc) · 13.6 KB
/
script.js
File metadata and controls
366 lines (319 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
const STORAGE_KEY = "license-plate-game-state";
const HISTORY_KEY = "license-plate-game-history";
function archiveExpiredGame(state) {
if (!state || !state.date) return;
try {
let history = JSON.parse(localStorage.getItem(HISTORY_KEY) || "{}");
history[state.date] = state;
localStorage.setItem(HISTORY_KEY, JSON.stringify(history));
console.log("Archived game for " + state.date);
} catch (e) {
console.error("Failed to archive game", e);
}
}
function getGameState() {
const stateStr = localStorage.getItem(STORAGE_KEY);
if (!stateStr) return null;
try {
const state = JSON.parse(stateStr);
// Check if state belongs to today's puzzle
// We use today_solution.date which is injected in index.html
if (typeof today_solution !== 'undefined' && state.date !== today_solution.date) {
console.log("Found expired game state from " + state.date);
archiveExpiredGame(state);
localStorage.removeItem(STORAGE_KEY);
return null;
}
return state;
} catch (e) {
return null;
}
}
function saveGameState(tries, won, guesses, score) {
if (typeof today_solution === 'undefined') return;
const state = {
date: today_solution.date,
tries: tries,
won: won,
guesses: guesses || [],
score: score || 0
};
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
function calculateScore(guesses, won, wonByLongest) {
let score = 0;
// Process guesses sequentially
guesses.forEach(g => {
if (g.correct) {
if (g.special) {
// Winning guess
if (g.special.includes("Longest")) {
score += 100;
} else if (g.special.includes("Shortest")) {
score += 85;
}
} else {
// Standard correct guess
score += 5;
}
} else {
// Wrong guess - penalty, but floor at 0
score = Math.max(0, score - 5);
}
});
// Cap at 100
return Math.min(100, score);
}
document.addEventListener('DOMContentLoaded', function () {
// Check if today_solution is defined (it should be in index.html)
if (typeof today_solution === 'undefined' || typeof clue_and_solution === 'undefined') {
console.error("Game data not found!");
return;
}
const MAX_TRIES = 3;
// Initialize state
let state = getGameState();
let triesLeft = state ? state.tries : MAX_TRIES;
let hasWon = state ? state.won : false;
let guesses = state ? (state.guesses || []) : [];
let currentScore = state ? (state.score || 0) : 0;
var clue = clue_and_solution["key"];
var clue_span = document.getElementById("clue");
clue_span.innerText = clue;
var guessInput = document.getElementById("guess");
var submitButton = document.getElementById("submitButton");
var resultSpan = document.getElementById("result");
var solutionForm = document.getElementById("solution");
var triesSpan = document.getElementById("tries");
var reviewContainer = document.getElementById("game-review");
var scoreSpan = document.getElementById("score");
var historyButton = document.getElementById("historyButton");
var historyModalBody = document.getElementById("historyModalBody");
// Share functionality helpers
function generateShareText(state) {
const plate = state.clue || "";
const correct = state.guesses.filter(g => g.correct).length;
const incorrect = state.guesses.filter(g => !g.correct).length;
const winStatus = state.won ? "WINNER" : `Score: ${state.score}`;
const url = window.location.href;
return `Plate: ${plate}\nCorrect guesses: ${correct}\nInccorect guesses: ${incorrect}\nResult: ${winStatus}\nPlay here: ${url}`;
}
function copyShareText(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => {
alert("Share text copied to clipboard!");
}).catch(err => {
console.error("Failed to copy: ", err);
});
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
alert('Share text copied to clipboard!');
} catch (err) {
console.error('Fallback copy failed', err);
}
document.body.removeChild(textarea);
}
}
function createShareButton() {
const container = document.getElementById('shareContainer');
if (!container) return;
// Avoid creating multiple buttons
if (document.getElementById('shareButton')) return;
const btn = document.createElement('button');
btn.id = 'shareButton';
btn.className = 'btn btn-primary btn-sm';
btn.innerText = 'Share Results';
btn.addEventListener('click', () => {
const state = {
clue: clue_span ? clue_span.innerText : "",
guesses: guesses,
score: currentScore,
won: hasWon
};
const text = generateShareText(state);
// Use Web Share API if available (better for mobile)
if (navigator.share) {
navigator.share({
title: 'License Plate Game Results',
text: text
}).catch(err => {
console.error('Share failed:', err);
// Fallback to clipboard if share fails (e.g. user cancelled)
copyShareText(text);
});
} else {
copyShareText(text);
}
});
container.appendChild(btn);
}
function updateUI() {
if (triesSpan) {
triesSpan.innerText = "Tries remaining: " + triesLeft;
}
if (scoreSpan) {
scoreSpan.innerText = currentScore;
}
showGameReview();
}
function showGameReview() {
if (!reviewContainer) return;
// Always show review if there are guesses
if (guesses.length === 0) {
reviewContainer.innerHTML = '';
return;
}
let html = '<div class="mt-4 card"><div class="card-body">';
html += '<h5 class="card-title">Guesses</h5>';
html += '<ul class="list-group list-group-flush text-start">';
guesses.forEach(function (g) {
let badgeClass = g.correct ? "bg-success" : "bg-danger";
let badgeText = g.correct ? "Match" : "No Match";
if (g.special) {
badgeClass = "bg-warning text-dark";
badgeText = g.special;
}
html += `<li class="list-group-item d-flex justify-content-between align-items-center">
${g.word}
<span class="badge ${badgeClass} rounded-pill">${badgeText}</span>
</li>`;
});
html += '</ul></div></div>';
reviewContainer.innerHTML = html;
}
function endGame(won, message, messageClass) {
submitButton.disabled = true;
guessInput.disabled = true;
resultSpan.innerHTML = message;
resultSpan.className = messageClass;
updateUI();
// After UI update, create share button
createShareButton();
}
// Check if already won or lost
if (hasWon) {
endGame(true, "You already won today! Come back tomorrow.", "text-success");
} else if (triesLeft <= 0) {
endGame(false, "No more tries today! Come back tomorrow.", "text-danger");
}
updateUI();
solutionForm.addEventListener('submit', function (e) {
e.preventDefault();
if (triesLeft <= 0 || hasWon) return;
var guess = guessInput.value.toLowerCase().trim();
if (!guess) return;
var hashed_guess = md5(guess);
var isCorrect = today_solution['solutions'].indexOf(hashed_guess) >= 0;
var special = null;
var wonByLongest = false;
if (isCorrect) {
// Check for special achievements
var is_longest = (hashed_guess === today_solution['longest_word_hash']);
var is_shortest = (hashed_guess === today_solution['shortest_word_hash']);
if (is_longest) {
special = "Longest!";
wonByLongest = true;
}
if (is_shortest) special = "Shortest!";
guesses.push({ word: guess, correct: true, special: special });
if (is_longest || is_shortest) {
hasWon = true;
currentScore = calculateScore(guesses, true, wonByLongest);
saveGameState(triesLeft, true, guesses, currentScore);
var message = "WINNER!";
if (is_longest) {
message = "🏆 WINNER! You found the LONGEST word!";
} else {
message = "✨ WINNER! You found the SHORTEST word!";
}
endGame(true, message + " Come back tomorrow to see the correct solutions and play a new plate.", "text-success");
} else {
// Correct word, but not the special one. Keep playing!
currentScore = calculateScore(guesses, false, false);
saveGameState(triesLeft, false, guesses, currentScore);
resultSpan.innerHTML = "Good job! Found a match, but not the longest or shortest.";
resultSpan.className = "text-info";
guessInput.value = "";
updateUI();
}
} else {
triesLeft--;
guesses.push({ word: guess, correct: false, special: null });
currentScore = calculateScore(guesses, false, false);
saveGameState(triesLeft, false, guesses, currentScore);
if (triesLeft <= 0) {
endGame(false, "Game Over! No more tries. Come back tomorrow to see the correct solutions and play a new plate.", "text-danger");
} else {
resultSpan.innerHTML = "Incorrect. Try again!";
resultSpan.className = "text-warning";
guessInput.value = "";
updateUI();
}
}
});
var giveUpButton = document.getElementById("giveUpButton");
if (giveUpButton) {
giveUpButton.addEventListener('click', function () {
if (hasWon || triesLeft <= 0) return;
if (confirm("Are you sure you want to give up?")) {
triesLeft = 0;
// Calculate score based on current state (likely low/negative)
currentScore = calculateScore(guesses, false, false);
saveGameState(triesLeft, false, guesses, currentScore);
endGame(false, "Game Over. You gave up. Come back tomorrow to see the correct solutions and play a new plate.", "text-danger");
}
});
}
// History Logic
fetch('history.json?t=' + new Date().getTime())
.then(response => {
if (response.ok) return response.json();
throw new Error('No history');
})
.then(historyData => {
// Check if we have yesterday's data
// We can just show the most recent entry in historyData
const dates = Object.keys(historyData).sort().reverse();
if (dates.length > 0) {
historyButton.style.display = 'block';
historyButton.addEventListener('click', function () {
// Show modal with yesterday's data
const date = dates[0]; // Most recent
const puzzleData = historyData[date];
// Get user's guesses for that date
const userHistory = JSON.parse(localStorage.getItem(HISTORY_KEY) || "{}");
const userGame = userHistory[date];
let html = `<p><strong>Date:</strong> ${date}</p>`;
html += `<p><strong>Plate:</strong> ${puzzleData.plate}</p>`;
if (userGame) {
html += `<p><strong>Your Score:</strong> ${userGame.score}</p>`;
html += `<p><strong>Result:</strong> ${userGame.won ? "WON" : "LOST"}</p>`;
html += `<h6>Your Guesses:</h6><ul>`;
userGame.guesses.forEach(g => {
html += `<li>${g.word} ${g.correct ? "✅" : "❌"}</li>`;
});
html += `</ul>`;
} else {
html += `<p class="text-muted">You didn't play on this day.</p>`;
}
html += `<hr><h6>Solution:</h6>`;
html += `<p><strong>Longest:</strong> ${puzzleData.longest_word}</p>`;
html += `<p><strong>Shortest:</strong> ${puzzleData.shortest_word}</p>`;
html += `<p><strong>All Solutions:</strong> ${puzzleData.solutions.join(", ")}</p>`;
// Show history modal using Bootstrap API
const historyModal = new bootstrap.Modal(document.getElementById('historyModal'));
document.getElementById('historyModalBody').innerHTML = html;
historyModal.show();
});
}
})
.catch(e => {
console.log("History not available yet.");
});
});