diff --git a/src/modules/loldle/handlers.js b/src/modules/loldle/handlers.js
index 66ccd2f..32dc46a 100644
--- a/src/modules/loldle/handlers.js
+++ b/src/modules/loldle/handlers.js
@@ -55,6 +55,23 @@ function isFinished(game) {
return game.solved || game.giveup || game.guesses.length >= MAX_GUESSES;
}
+/**
+ * Recompute comparison rows for each stored guess against the current target.
+ * Guesses that no longer resolve (e.g. champion removed from loldle.net) are
+ * dropped silently — an edge case for stale rounds spanning a data refresh.
+ */
+function rehydrateGuesses(game) {
+ const target = champions.find((c) => c.championName === game.target);
+ if (!target) return [];
+ const rows = [];
+ for (const name of game.guesses) {
+ const guess = champions.find((c) => c.championName === name);
+ if (!guess) continue;
+ rows.push({ champion: name, results: compareChampions(guess, target) });
+ }
+ return rows;
+}
+
/**
* Load existing round, or create + persist a fresh random one.
* A previously-finished round is discarded and replaced with a fresh one so
@@ -112,13 +129,14 @@ export async function handleLoldle(ctx, db) {
if (!arg) {
const header = `Guess ${game.guesses.length}/${MAX_GUESSES}. Use /loldle <champion>.`;
- return ctx.reply(`${header}\n\n${renderBoard(game.guesses)}`, { parse_mode: "HTML" });
+ const board = renderBoard(rehydrateGuesses(game));
+ return ctx.reply(`${header}\n\n${board}`, { parse_mode: "HTML" });
}
const guess = findChampion(champions, arg);
if (!guess) return ctx.reply(`Champion not found: "${arg}".`);
- if (game.guesses.some((g) => g.champion === guess.championName)) {
+ if (game.guesses.includes(guess.championName)) {
return ctx.reply(
`🔁 ${escapeHtml(guess.championName)} was already guessed this round — try another champion.`,
{ parse_mode: "HTML" },
@@ -134,7 +152,7 @@ export async function handleLoldle(ctx, db) {
);
}
const results = compareChampions(guess, target);
- game.guesses.push({ champion: guess.championName, results });
+ game.guesses.push(guess.championName);
const won = guess.championName === target.championName;
if (won) game.solved = true;
await saveGame(db, subject, game);
diff --git a/src/modules/loldle/state.js b/src/modules/loldle/state.js
index be39e9d..d4b7a1f 100644
--- a/src/modules/loldle/state.js
+++ b/src/modules/loldle/state.js
@@ -5,8 +5,12 @@
* can /loldle_giveup to reveal (a fresh round auto-starts). Streak = consecutive wins.
*
* Key layout (inside module-prefixed store):
- * game: -> { target, guesses[], solved, giveup, startedAt }
+ * game: -> { target, guesses[championName...], solved, giveup, startedAt }
* stats: -> { played, wins, streak, bestStreak, lastResultAt }
+ *
+ * Only championName strings are stored in `guesses` — comparison rows are
+ * recomputed at render time from the live champions.json. This keeps payloads
+ * tiny and avoids stale results if loldle.net data shifts mid-round.
*/
const MAX_GUESSES = 8;
@@ -20,8 +24,8 @@ const statsKey = (subject) => `stats:${subject}`;
/**
* @typedef {object} GameState
- * @property {string} target — champion id
- * @property {Array<{champion:string, results:any[]}>} guesses
+ * @property {string} target — championName of the hidden champion
+ * @property {string[]} guesses — championNames already tried this round
* @property {boolean} solved
* @property {boolean} [giveup]
* @property {number} [startedAt] — epoch ms