Skip to content

Commit 3fe0235

Browse files
committed
fix: correct Set A name-template answers to match Gensler
Proper names are singular terms in Gensler's syllogistic language and translate into small letters ("Sally is a humorous person in Paris" = "s is H", not "S is H"). Templates *0 and *1 had the case inverted, marking the original 2008 engine's distractors as correct. Verified against the 2008 DSL letter-binding block (dKJJ = lowercase name, dRJ = uppercase) and Introduction to Logic 3rd ed. §2.1. Also: - remove the "Use the first letters of ..." line from wrong-option hints across all 23 templates (in 2008 it is instruction text for type-the-answer mode, not feedback; meaningless in multiple choice) - attach the original per-mistake explanations to *0/*1 wrong options - delete the unrouted legacy static question bank (setA.ts), which carried its own miskeyed answers - add fidelity tests: name-template case conventions and a property test that every correct answer matches one of Gensler's eight wff forms with correct case
1 parent 2cae155 commit 3fe0235

8 files changed

Lines changed: 192 additions & 4693 deletions

File tree

content/sets/__snapshots__/setA.generator.test.ts.snap

Lines changed: 55 additions & 109 deletions
Large diffs are not rendered by default.

content/sets/__snapshots__/setA.test.ts.snap

Lines changed: 0 additions & 2778 deletions
This file was deleted.

content/sets/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export { setA } from './setA';
21
export { setC } from './setC';
32
export { setQ } from './setQ';
43
export { setJ } from './setJ';

content/sets/setA.generator.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,78 @@ describe('setA generator — property tests', () => {
120120
);
121121
});
122122

123+
describe('setA generator — Gensler fidelity', () => {
124+
const SWEEP_SEEDS = [1, 42, 99, 12345];
125+
126+
/** Gensler's eight wff forms (Introduction to Logic, 3rd ed., §2.1):
127+
* wffs beginning with a word use two capitals; wffs beginning with
128+
* a letter begin with a small letter. */
129+
const WFF_FORMS = [
130+
/^all [A-Z] is [A-Z]$/,
131+
/^no [A-Z] is [A-Z]$/,
132+
/^some [A-Z] is [A-Z]$/,
133+
/^some [A-Z] is not [A-Z]$/,
134+
/^[a-z] is [A-Za-z]$/,
135+
/^[a-z] is not [A-Za-z]$/,
136+
];
137+
138+
const allQuestions = (seed: number) =>
139+
generateSetA(seed, 50).subSets.flatMap((s) => s.questions);
140+
141+
it.each(SWEEP_SEEDS)(
142+
'seed %i: every correct answer is one of the eight wff forms with correct case',
143+
(seed) => {
144+
for (const q of allQuestions(seed)) {
145+
const correct = q.options.find((o) => q.correctId.includes(o.id))!;
146+
expect(
147+
WFF_FORMS.some((re) => re.test(correct.label)),
148+
`"${correct.label}" (for "${q.prompt}") is not a wff`
149+
).toBe(true);
150+
}
151+
}
152+
);
153+
154+
it.each(SWEEP_SEEDS)(
155+
'seed %i: *0 proper name is lowercase, class predicate capital (s is H)',
156+
(seed) => {
157+
const qs = allQuestions(seed).filter((q) => q.id.startsWith('gen.A.0.'));
158+
expect(qs.length).toBeGreaterThan(0);
159+
for (const q of qs) {
160+
const correct = q.options.find((o) => q.correctId.includes(o.id))!;
161+
expect(correct.label).toMatch(/^[a-z] is [A-Z]$/);
162+
// subject letter = lowercased first letter of the name in the prompt
163+
expect(correct.label[0]).toBe(q.prompt[0]!.toLowerCase());
164+
}
165+
}
166+
);
167+
168+
it.each(SWEEP_SEEDS)(
169+
'seed %i: *1 name and definite description are both lowercase (h is s)',
170+
(seed) => {
171+
const qs = allQuestions(seed).filter((q) => q.id.startsWith('gen.A.1.'));
172+
expect(qs.length).toBeGreaterThan(0);
173+
for (const q of qs) {
174+
const correct = q.options.find((o) => q.correctId.includes(o.id))!;
175+
expect(correct.label).toMatch(/^[a-z] is [a-z]$/);
176+
expect(correct.label[0]).toBe(q.prompt[0]!.toLowerCase());
177+
}
178+
}
179+
);
180+
181+
it.each(SWEEP_SEEDS)(
182+
'seed %i: no option hint carries the type-answer letter instruction',
183+
(seed) => {
184+
for (const q of allQuestions(seed)) {
185+
for (const o of q.options) {
186+
if (o.hint) {
187+
expect(o.hint).not.toMatch(/first letter/i);
188+
}
189+
}
190+
}
191+
}
192+
);
193+
});
194+
123195
describe('setA generator — streaming iterators', () => {
124196
it('easyQuestions yields indefinitely (sanity-check first 100)', () => {
125197
const it = easyQuestions(TEST_SEED);

0 commit comments

Comments
 (0)