/** * llmindex.io — reasoning item templates (generated deduction puzzles, unique solution) * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * License: Proprietary — © Simon-Pierre Boucher, all rights reserved */ import { ANSWER_FORMAT_INSTRUCTIONS } from '../answer'; import type { ItemTemplate } from '../types'; const NAMES = [ 'Alice', 'Bruno', 'Chen', 'Dara', 'Emil', 'Farah', 'Goran', 'Hana', 'Ines', 'Jonas', 'Kira', 'Liam', 'Mona', 'Nadir', 'Ola', 'Priya', 'Quinn', 'Rosa', 'Sami', 'Tessa', ]; const ATTRIBUTES = [ { adj: 'taller', sup: 'tallest', inv: 'shortest' }, { adj: 'older', sup: 'oldest', inv: 'youngest' }, { adj: 'faster', sup: 'fastest', inv: 'slowest' }, { adj: 'heavier', sup: 'heaviest', inv: 'lightest' }, ]; export const reasoningOrder: ItemTemplate = { id: 'reasoning.deduction.order-v2', domain: 'reasoning', description: 'Total-order deduction over 7 people with non-adjacent transitive clues, an inert distractor person, and a rank query (not just the extremes).', paramSpace: NAMES.length ** 8 * ATTRIBUTES.length * 5040 * 7, render(rng, perturbSeed) { const people = rng.shuffle(NAMES).slice(0, 8); const ordered = people.slice(0, 7); // hidden order; ordered[0] greatest const distractor = people[7]!; const attr = rng.pick(ATTRIBUTES); // Clue set: adjacent relations pin the order, but present them mixed with // redundant non-adjacent clues (harder to assemble) and one distractor // clue about an unrelated attribute for an unrelated person. const relations: string[] = []; for (let i = 0; i < ordered.length - 1; i++) { relations.push(`${ordered[i]} is ${attr.adj} than ${ordered[i + 1]}.`); } for (let k = 0; k < 3; k++) { const i = rng.int(0, ordered.length - 3); const j = rng.int(i + 2, ordered.length - 1); relations.push(`${ordered[i]} is ${attr.adj} than ${ordered[j]}.`); } const otherAttr = rng.pick(ATTRIBUTES.filter((a) => a !== attr)); relations.push(`${distractor} is ${otherAttr.adj} than everyone here, but ${distractor} is not being ranked.`); const clues = rng.shuffle(relations).join(' '); const rank = rng.int(2, 6); // interior ranks are harder than extremes const ordinal = ['', 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh'][rank]!; return { templateId: this.id, domain: this.domain, prompt: `Seven people are ranked by who is ${attr.adj} (rank 1 = ${attr.sup}). ${clues} ` + `Who is ${ordinal} (rank ${rank})?\n\nAnswer with the name only.\n\n${ANSWER_FORMAT_INSTRUCTIONS}`, answerKey: ordered[rank - 1]!, grading: 'exact', perturbSeed, }; }, }; export const reasoningSchedule: ItemTemplate = { id: 'reasoning.deduction.position-v1', domain: 'reasoning', description: 'Positional deduction: 4 people in a queue with relative-position clues; ask who occupies a given position.', paramSpace: NAMES.length ** 4 * 24 * 4, render(rng, perturbSeed) { const people = rng.shuffle(NAMES).slice(0, 4); const order = rng.shuffle(people); // order[0] = front of the queue const clues: string[] = []; // Clues that jointly force the permutation: position of one, plus relative constraints. const anchorIdx = rng.int(0, 3); clues.push(`${order[anchorIdx]} is number ${anchorIdx + 1} in the queue.`); for (let i = 0; i < 3; i++) { if (i !== anchorIdx) clues.push(`${order[i]} is directly ahead of ${order[i + 1]}.`); } const askPos = rng.int(1, 4); return { templateId: this.id, domain: this.domain, prompt: `Four people stand in a queue (number 1 is the front). ${rng.shuffle(clues).join(' ')} ` + `Who is number ${askPos}?\n\nAnswer with the name only.\n\n${ANSWER_FORMAT_INSTRUCTIONS}`, answerKey: order[askPos - 1]!, grading: 'exact', perturbSeed, }; }, };