mirror of
https://github.com/tiennm99/loldle.git
synced 2026-05-22 02:25:28 +00:00
daf4d60bb2
- Replace vanilla HTML/CSS/JS with Next.js App Router + React components - Game logic (champion-data, game-engine, classic-mode) moved to lib/ - UI split into modular components (game-board, champion-search, guess-grid, etc.) - Add Tailwind CSS v4 with CSS variables for theming - Immutable state updates, fetch deduplication, next/image for CDN images - champions.json moved from assets/ to public/
110 lines
3.0 KiB
JavaScript
110 lines
3.0 KiB
JavaScript
// Classic mode: compare two champions across 7 attributes
|
|
|
|
/** Attributes displayed in order */
|
|
export const CLASSIC_ATTRIBUTES = [
|
|
{ key: "gender", label: "Gender", type: "exact" },
|
|
{ key: "genre", label: "Genre", type: "multi" },
|
|
{ key: "attackType", label: "Range", type: "exact" },
|
|
{ key: "resource", label: "Resource", type: "exact" },
|
|
{ key: "region", label: "Region", type: "exact" },
|
|
{ key: "lane", label: "Lane", type: "multi" },
|
|
{ key: "releaseDate", label: "Year", type: "year" },
|
|
];
|
|
|
|
/** Compare guess champion against target champion */
|
|
export function compareChampions(guess, target) {
|
|
return CLASSIC_ATTRIBUTES.map((attr) => {
|
|
const guessVal = guess[attr.key] || "";
|
|
const targetVal = target[attr.key] || "";
|
|
|
|
switch (attr.type) {
|
|
case "exact":
|
|
return {
|
|
...attr,
|
|
guessValue: formatValue(attr.key, guessVal),
|
|
targetValue: formatValue(attr.key, targetVal),
|
|
result: guessVal.toLowerCase() === targetVal.toLowerCase() ? "correct" : "wrong",
|
|
};
|
|
|
|
case "multi":
|
|
return {
|
|
...attr,
|
|
guessValue: formatValue(attr.key, guessVal),
|
|
targetValue: formatValue(attr.key, targetVal),
|
|
result: compareMultiValue(guessVal, targetVal),
|
|
};
|
|
|
|
case "year":
|
|
return {
|
|
...attr,
|
|
guessValue: guessVal || "?",
|
|
targetValue: targetVal || "?",
|
|
...compareYear(guessVal, targetVal),
|
|
};
|
|
|
|
default:
|
|
return { ...attr, guessValue: guessVal, targetValue: targetVal, result: "wrong" };
|
|
}
|
|
});
|
|
}
|
|
|
|
function compareMultiValue(guessStr, targetStr) {
|
|
const guessSet = parseSet(guessStr);
|
|
const targetSet = parseSet(targetStr);
|
|
|
|
if (guessSet.size === 0 && targetSet.size === 0) return "correct";
|
|
if (guessSet.size === 0 || targetSet.size === 0) return "wrong";
|
|
if (setsEqual(guessSet, targetSet)) return "correct";
|
|
|
|
for (const val of guessSet) {
|
|
if (targetSet.has(val)) return "partial";
|
|
}
|
|
return "wrong";
|
|
}
|
|
|
|
function compareYear(guessYear, targetYear) {
|
|
const g = Number(guessYear);
|
|
const t = Number(targetYear);
|
|
|
|
if (!g || !t) return { result: "wrong" };
|
|
if (g === t) return { result: "correct" };
|
|
return { result: "wrong", direction: g < t ? "up" : "down" };
|
|
}
|
|
|
|
function parseSet(str) {
|
|
if (!str) return new Set();
|
|
return new Set(
|
|
str.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean),
|
|
);
|
|
}
|
|
|
|
function setsEqual(a, b) {
|
|
if (a.size !== b.size) return false;
|
|
for (const val of a) {
|
|
if (!b.has(val)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function formatValue(key, value) {
|
|
if (!value) return "—";
|
|
|
|
switch (key) {
|
|
case "gender":
|
|
return capitalize(value);
|
|
case "attackType":
|
|
return value === "close" ? "Melee" : "Ranged";
|
|
case "region":
|
|
return value.split("-").map(capitalize).join(" ");
|
|
case "genre":
|
|
case "lane":
|
|
return value.split(",").map((s) => capitalize(s.trim())).join(", ");
|
|
default:
|
|
return value;
|
|
}
|
|
}
|
|
|
|
function capitalize(str) {
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
}
|