con un clic
puzzle-solver-patterns
Constraint satisfaction patterns for the Pips puzzle solver
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Constraint satisfaction patterns for the Pips puzzle solver
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Complete Claude Code hooks reference - input/output schemas, registration, testing patterns
How to use AI APIs like OpenAI, ChatGPT, Elevenlabs, etc. When a user asks you to make an app that requires an AI API, use this skill to understand how to use the API or how to respond to the user.
Debug Gemini/GPT vision API issues in puzzle image extraction
Create git commits with user approval and no Claude attribution
Create or update continuity ledger for state preservation across clears
Create handoff document for transferring work to another session
Basado en la clasificación ocupacional SOC
| name | puzzle-solver-patterns |
| description | Constraint satisfaction patterns for the Pips puzzle solver |
| allowed-tools | ["Read","Edit","Write","Bash","Grep"] |
Patterns and best practices for working with the constraint satisfaction solver in the Pips puzzle app.
The solver uses backtracking with constraint propagation:
src/lib/services/solver.ts - Main solver implementation
src/lib/types/puzzle.ts - Type definitions
Constraint Types (line 13-19 in puzzle.ts):
sum: Pips sum to exact valueequal: All pips identicaldifferent: All pips uniquegreater: All pips > valueless: All pips < valueany: No constraintHeuristics:
Validation:
File: src/lib/types/puzzle.ts
export type ConstraintType =
| 'sum'
| 'equal'
| 'different'
| 'greater'
| 'less'
| 'any'
| 'product' // NEW: Product of pips equals value
| 'sequential'; // NEW: Pips form sequence (1,2,3,4)
File: src/lib/types/puzzle.ts (line 112-129)
export function constraintLabel(constraint: RegionConstraint): string {
switch (constraint.type) {
// ... existing cases
case 'product':
return `Π${constraint.value}`; // Product symbol
case 'sequential':
return '→'; // Arrow for sequence
default:
return '';
}
}
File: src/lib/services/solver.ts (checkConstraint function, line 60-82)
function checkConstraint(
constraint: { type: string; value?: number },
pips: number[]
): boolean {
if (pips.length === 0) return true;
switch (constraint.type) {
// ... existing cases
case 'product':
const product = pips.reduce((a, b) => a * b, 1);
return product === constraint.value;
case 'sequential':
const sorted = [...pips].sort((a, b) => a - b);
for (let i = 1; i < sorted.length; i++) {
if (sorted[i] !== sorted[i-1] + 1) return false;
}
return true;
default:
return true;
}
}
File: src/lib/services/solver.ts (checkPartialConstraint function, line 84-109)
This is critical for early pruning to avoid exploring invalid branches.
function checkPartialConstraint(
constraint: { type: string; value?: number },
pips: number[]
): boolean {
if (pips.length === 0) return true;
switch (constraint.type) {
// ... existing cases
case 'product':
// Product so far shouldn't exceed target
const product = pips.reduce((a, b) => a * b, 1);
return product <= (constraint.value ?? Infinity);
case 'sequential':
// Check if partial sequence is valid
const sorted = [...pips].sort((a, b) => a - b);
for (let i = 1; i < sorted.length; i++) {
if (sorted[i] !== sorted[i-1] + 1) return false;
}
return true;
default:
return true;
}
}
File: src/components/RegionEditor.tsx
Add the new constraint type to the picker/selector UI.
When the solver returns "No solution found", check these patterns:
The solver already logs extensively (line 242-335). Check console output:
console.log('[Solver] Starting solve...', {
cells: puzzle.validCells.length,
dominoes: puzzle.availableDominoes.length,
regions: puzzle.regions.length,
});
Cell/Domino Count (line 258-266):
// Must be exactly 2 cells per domino
cells === dominoes * 2
Sum Constraints (line 318-334):
// Total of all sum constraints must equal total of all pips
sumConstraintsTotal === pipSum
Check if available pips can satisfy constraints:
// Check unique values for "different" constraints
const uniquePips = new Set(allPips).size;
if (region.constraint.type === 'different' && region.cells.length > uniquePips) {
// IMPOSSIBLE: Need 6 unique values but only 4 unique pips available
}
Extract a failing region and test in isolation:
// Test if constraint is even theoretically possible
const testPips = [1, 2, 3, 4];
console.log(checkConstraint({ type: 'sum', value: 10 }, testPips));
Current: MRV (Minimum Remaining Values) - pick cell with fewest neighbors
Alternative heuristics to try:
Degree Heuristic: Pick cell in most constrained region first
function findBestUncoveredCell(
validCells: Cell[],
coveredCells: Set<string>,
validCellSet: Set<string>,
regions: Region[] // NEW parameter
): Cell | null {
// Score cells by region constraint complexity
for (const cell of validCells) {
if (coveredCells.has(cellKey(cell))) continue;
// Find which region this cell belongs to
const region = regions.find(r =>
r.cells.some(c => cellKey(c) === cellKey(cell))
);
// Prioritize cells in 'sum' or 'different' regions (harder constraints)
const score = region?.constraint.type === 'sum' ? 3 :
region?.constraint.type === 'different' ? 2 : 1;
}
}
Try dominoes in specific order (e.g., high values first):
// Before: try dominoes in array order
for (let i = 0; i < puzzle.availableDominoes.length; i++) {
// ...
}
// After: sort by pip sum (descending)
const sortedIndices = puzzle.availableDominoes
.map((d, i) => ({ index: i, sum: d.pips[0] + d.pips[1] }))
.sort((a, b) => b.sum - a.sum)
.map(x => x.index);
for (const i of sortedIndices) {
// ...
}
Check if remaining uncovered cells can still be paired:
// After each placement, verify remaining cells have valid pairing
const remainingCells = validCells.filter(c => !coveredCells.has(cellKey(c)));
// If any cell has ZERO uncovered neighbors, this branch is dead
for (const cell of remainingCells) {
const neighbors = getAdjacentCells(cell, validCellSet);
const uncoveredNeighbors = neighbors.filter(n => !coveredCells.has(cellKey(n)));
if (uncoveredNeighbors.length === 0) {
return null; // Prune this branch early
}
}
Cause: Cell count doesn't equal 2 × domino count
Solution: Check image extraction in src/lib/services/gemini.ts:
Cause: Constraints are unsatisfiable
Debug steps:
Cause: Combinatorial explosion on larger grids
Solutions:
Use the L-shaped test puzzle:
File: src/lib/services/gemini.ts - createLShapedPuzzle()
export function createLShapedPuzzle(): PuzzleData {
return {
width: 5,
height: 5,
validCells: [
{ row: 0, col: 0 }, { row: 0, col: 1 },
// ... add cells for your test case
],
regions: [
{
id: 'test-region',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }],
color: '#E53935',
constraint: { type: 'product', value: 12 } // TEST NEW CONSTRAINT
}
],
availableDominoes: [
{ id: '3-4', pips: [3, 4] }, // Product = 12 ✓
// ... other dominoes
],
blockedCells: []
};
}
Then test via: "Try L-shaped puzzle" button in the app.
src/lib/types/puzzle.ts - Type definitions, constraint types
src/lib/services/solver.ts - Main backtracking solver
src/lib/services/gemini.ts - Image extraction + test puzzles
src/components/RegionEditor.tsx - UI for editing constraints
src/lib/state/puzzle-store.ts - State management
// 1. Add type
type ConstraintType = ... | 'odd' | 'even';
// 2. Label
case 'odd': return 'O';
case 'even': return 'E';
// 3. Full check
case 'odd': return pips.every(p => p % 2 === 1);
case 'even': return pips.every(p => p % 2 === 0);
// 4. Partial check (same as full for this constraint)
case 'odd': return pips.every(p => p % 2 === 1);
case 'even': return pips.every(p => p % 2 === 0);
// Add this after line 335 in solver.ts
console.log('[DEBUG] Analyzing unsatisfiable constraints...');
for (const region of regionsToCheck) {
const availablePipsForRegion = puzzle.availableDominoes.flatMap(d => d.pips);
if (region.constraint.type === 'different') {
const uniqueAvailable = new Set(availablePipsForRegion).size;
if (region.cells.length > uniqueAvailable) {
console.error(`[DEBUG] Region ${region.id} needs ${region.cells.length} unique values but only ${uniqueAvailable} available!`);
}
}
}
Always implement both full AND partial constraint checks
Test with small puzzles first
createLShapedPuzzle() or createSamplePuzzle()Add console logging
Consider mathematical properties
Update UI components