From 44dd1497f642359db23e595dca3b280769046a46 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 06:55:24 +0000 Subject: [PATCH] Add explanation for inconsistency Add an "Explain" button next to "Reset" in the Knights and Knaves puzzle. When clicked, this button displays an explanation for any inconsistencies found in the arena below it. --- src/components/KnightsAndKnavesI.tsx | 76 ++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/src/components/KnightsAndKnavesI.tsx b/src/components/KnightsAndKnavesI.tsx index 41882cd..e7e5153 100644 --- a/src/components/KnightsAndKnavesI.tsx +++ b/src/components/KnightsAndKnavesI.tsx @@ -15,7 +15,7 @@ type StatementId = typeof STATEMENTS[number]["id"]; const KnightsAndKnavesI = () => { const [inArena, setInArena] = useState([]); - + const [showExplain, setShowExplain] = useState(false); // SEO basics useEffect(() => { const title = "Knights and Knaves – I (Logic Puzzle)"; @@ -100,6 +100,54 @@ const KnightsAndKnavesI = () => { isConsistent ? "bg-surface border-outline" : "bg-destructive/10 border-destructive" ); + function generateExplanation(idsArr: StatementId[]): string[] { + const ids = new Set(idsArr); + const lines: string[] = []; + + // Direct role contradictions + if (ids.has("AK") && ids.has("AN")) lines.push("A cannot be both a knight and a knave."); + if (ids.has("BK") && ids.has("BN")) lines.push("B cannot be both a knight and a knave."); + + // A's utterance linkage + if (ids.has("S")) { + const forcedA = ids.has("AK") ? true : ids.has("AN") ? false : null; + const forcedB = ids.has("BK") ? true : ids.has("BN") ? false : null; + + if (forcedA === true && forcedB === true) { + lines.push("If A and B are both knights, then A’s statement “at least one of us is a knave” is false; but a knight cannot assert a falsehood."); + } + if (forcedA === false) { + lines.push("If A is a knave, his statement must be false. “At least one of us is a knave” being false means neither is a knave (both are knights), contradicting “A is a knave” and any claim that B is a knave."); + } + if (forcedB === true && forcedA === null) { + lines.push("B is a knight. If A were a knight, his statement would be false; if A were a knave, his statement would be true. Either way, A’s truthfulness would not match his statement, so no assignment works."); + } + + // Enumerate remaining candidate assignments for clarity + if (lines.length === 0) { + const aVals = forcedA === null ? [true, false] : [forcedA]; + const bVals = forcedB === null ? [true, false] : [forcedB]; + for (const aKnight of aVals) { + for (const bKnight of bVals) { + const content = (!aKnight) || (!bKnight); + const ok = (aKnight === content); + if (!ok) { + const contentTruth = content ? "true" : "false"; + lines.push(`If A is ${aKnight ? "a knight" : "a knave"} and B is ${bKnight ? "a knight" : "a knave"}, then the content “at least one of us is a knave” is ${contentTruth}, which a ${aKnight ? "knight must tell" : "knave must not tell"}.`); + } + } + } + } + } + + if (lines.length === 0) lines.push("The selected set cannot be satisfied by any assignment of A and B."); + return lines; + } + + const explanation = useMemo(() => (isConsistent ? [] : generateExplanation(inArena)), [inArena, isConsistent]); + + useEffect(() => { if (isConsistent) setShowExplain(false); }, [isConsistent]); + return (
@@ -145,10 +193,18 @@ const KnightsAndKnavesI = () => {

{isConsistent ? "Consistent" : "Incompatible set"}

- +
+ {!isConsistent && ( + + )} + +
{inArena.length === 0 ? ( @@ -179,6 +235,16 @@ const KnightsAndKnavesI = () => { )} + {showExplain && !isConsistent && ( +
+

Why inconsistent?

+
    + {explanation.map((line, i) => ( +
  • {line}
  • + ))} +
+
+ )}