Refactor interactive elements

Change "Statements" to "Potential Truths". Add a new zone below the arena called "Incompatible with current worldview". When a statement conflicts with the current setup, it will be moved to this zone, and it will not be possible to drag statements from this zone.
This commit is contained in:
gpt-engineer-app[bot] 2025-08-08 07:29:51 +00:00
parent 4b1940859a
commit 81f6873c4d

View file

@ -78,39 +78,55 @@ const KnightsAndKnavesI = () => {
setInArena([]);
}
// Consistency checker via brute force over assignments
const isConsistent = useMemo(() => {
// Try all assignments for A, B
const ids = new Set(inArena);
// Consistency checker via brute force over assignments (with A's utterance semantics)
const consistentWith = (idsArr: StatementId[]) => {
const ids = new Set(idsArr);
for (const aKnight of [true, false]) {
for (const bKnight of [true, false]) {
// role constraints from dragged tokens
// role constraints
if (ids.has("AK") && !aKnight) continue;
if (ids.has("AN") && aKnight) continue;
if (ids.has("BK") && !bKnight) continue;
if (ids.has("BN") && bKnight) continue;
if (ids.has("SNEG") && !(aKnight && bKnight)) continue;
// A's utterance if included: S = "At least one is a knave"
// Content of A's statement: S = "At least one of us is a knave."
const Scontent = (!aKnight) || (!bKnight);
// Implicit linkage: if A is declared knight/knave, his statement's content must match
if (ids.has("AK") && !Scontent) continue;
if (ids.has("AN") && Scontent) continue;
// If token S is explicitly included, also enforce truthfulness parity
if (ids.has("S")) {
const S = (!aKnight) || (!bKnight);
// Knights tell truth, knaves lie
if (aKnight !== S) continue;
if (aKnight !== Scontent) continue;
}
// If we got here, this assignment fits all dragged statements
return true;
}
}
return inArena.length <= 1; // single or empty always treated as consistent for UX
}, [inArena]);
return idsArr.length <= 1;
};
const isConsistent = useMemo(() => consistentWith(inArena), [inArena]);
const arenaClasses = cn(
"relative rounded-lg border transition-colors min-h-[320px] grid place-items-center p-6",
isConsistent ? "bg-surface border-outline" : "bg-destructive/10 border-destructive"
);
// Partition remaining statements into compatible vs incompatible under current worldview
const partitions = useMemo(() => {
const remaining = STATEMENTS.filter(s => !inArena.includes(s.id));
const incompatible = remaining
.filter(s => !consistentWith([...inArena, s.id]))
.map(s => s.id);
const compatible = remaining
.filter(s => !incompatible.includes(s.id))
.map(s => s.id);
return { compatible, incompatible };
}, [inArena]);
function generateExplanation(idsArr: StatementId[]): string[] {
const ids = new Set(idsArr);
const lines: string[] = [];
@ -173,18 +189,19 @@ const KnightsAndKnavesI = () => {
{/* Available statements */}
<Card className="lg:col-span-1">
<CardHeader>
<CardTitle className="text-xl">Statements</CardTitle>
<CardTitle className="text-xl">Potential Truths</CardTitle>
</CardHeader>
<CardContent className="flex flex-wrap gap-3">
{available.map(s => {
{partitions.compatible.map(id => {
const s = STATEMENTS.find(x => x.id === id)!;
const meta = META[s.id];
const groupClass = meta.group === "A" ? "logic-a" : meta.group === "B" ? "logic-b" : "logic-s";
const styleClass = meta.neg ? "logic-dotted" : "logic-solid";
const classes = cn(
"cursor-move select-none rounded-md bg-surface-variant px-3 py-2 text-sm text-foreground shadow-card border-2",
"logic-border",
"cursor-move select-none rounded-md bg-surface-variant px-3 py-2 text-sm text-foreground shadow-card border-2 logic-border",
groupClass,
styleClass
styleClass,
"ring-1 ring-success/30"
);
return (
<div
@ -194,6 +211,7 @@ const KnightsAndKnavesI = () => {
className={classes}
aria-label={`Drag: ${s.label}`}
role="button"
title="Compatible with current worldview"
>
{s.label}
</div>
@ -267,6 +285,37 @@ const KnightsAndKnavesI = () => {
</ul>
</div>
)}
{partitions.incompatible.length > 0 && (
<Card className="mt-4">
<CardHeader>
<CardTitle className="text-xl">Incompatible with current worldview</CardTitle>
</CardHeader>
<CardContent className="flex flex-wrap gap-3">
{partitions.incompatible.map(id => {
const s = STATEMENTS.find(x => x.id === id)!;
const meta = META[id];
const groupClass = meta.group === "A" ? "logic-a" : meta.group === "B" ? "logic-b" : "logic-s";
const styleClass = meta.neg ? "logic-dotted" : "logic-solid";
const classes = cn(
"select-none rounded-md bg-surface-variant px-3 py-2 text-sm text-foreground shadow-card border-2 logic-border",
groupClass,
styleClass,
"opacity-60 cursor-not-allowed bg-destructive/10 ring-1 ring-destructive/40"
);
return (
<div
key={id}
className={classes}
aria-disabled="true"
title="Conflicts with current worldview"
>
{s.label}
</div>
);
})}
</CardContent>
</Card>
)}
</div>
</div>
</section>