How I Built a 24 Game Solver: Brute-Force Meets Elegance in TypeScript
Source: Dev.to
The 24 Game is deceptively simple: given four numbers, combine them with +, -, ×, ÷ to make exactly 24. Sounds easy, right? Try solving 1, 5, 5, 5 — it stumps most people.
I built a complete solver for my math puzzle platform, and the algorithm turned out to be a great exercise in combinatorics, expression trees, and floating-point traps. Let me walk you through it.
The Problem Space
Four numbers, four operators, and parentheses. How many possibilities?
Permutations of 4 numbers: up to 24 (4!)
Operator combinations: 4³ = 64 (three slots, four choices each)
Parenthesization patterns: 5 (the Catalan number C₃)
Total: 24 × 64 × 5 = 7,680 evaluations. Trivial for modern hardware.
The key insight is that we need to enumerate all five binary tree structures for combining four operands — these correspond to the five ways to fully parenthesize a sequence of four items.
The Five Expression Patterns
For numbers a, b, c, d and operators op1, op2, op3:
Pattern 1: ((a op1 b) op2 c) op3 d
Pattern 2: (a op1 (b op2 c)) op3 d
Pattern 3: (a op1 b) op2 (c op3 d)
Pattern 4: a op1 ((b op2 c) op3 d)
Pattern 5: a op1 (b op2 (c op3 d))
Enter fullscreen mode
Exit fullscreen mode
These five patterns cover every possible way to combine four numbers with binary operators. No expression is left unexplored.
The Algorithm in TypeScript
Step 1: Generate Permutations (with deduplication)
When input contains duplicates (like 5, 5, 5, 1), many permutations are identical. We use a Set to filter:
function permutations(arr: number[]): number[][] {
if (arr.length ();
for (let i = 0; i ();
const perms = permutations(numbers);
for (const [a, b, c, d] of perms) {
for (const op1 of operators) {
for (const op2 of operators) {
for (const op3 of operators) {
// Evaluate all 5 patterns...
// (full implementation above)
}
}
}
}
return solutions.slice(0, 10); // Return top 10 solutions
}
Enter fullscreen mode
Exit fullscreen mode
The full solver runs in under 1ms for any input — brute force is perfectly fine here.
Tricky Examples
Input Solution
1, 5, 5, 5 5 × (5 - 1/5) = 24
3, 3, 8, 8 8 / (3 - 8/3) = 24
1, 3, 4, 6 6 / (1 - 3/4) = 24
These are the puzzles that make people give up — and exactly why having a solver is satisfying.
Try It Live
I integrated this solver into my math puzzle platform. You can play the 24 Game with a built-in hint system that uses this exact algorithm:
👉 Play 24 Game on MathPuzzleHub
The solver powers the “Show Solution” button — it finds all valid expressions and displays them step by step.
Performance Notes
Worst case (4 distinct numbers like 1, 2, 3, 4): ~7,680 evaluations, still under 1ms
Best case (all same like 6, 6, 6, 6): only 1 unique permutation, 320 evaluations
Memory: minimal — just storing up to 10 solution strings
If you wanted to scale this to 5 or 6 numbers, you’d want to switch from brute force to a recursive reduction approach (pick any two numbers, combine them, recurse on the smaller set). But for the classic 4-number game, brute force is elegant and fast.
Key Takeaways
Brute force is underrated. 7,680 evaluations is nothing. Don’t over-engineer.
Five parenthesization patterns cover all binary expression trees for 4 operands — this comes from Catalan numbers.
Floating-point epsilon comparison is essential. === 24 will miss valid solutions involving division.
Deduplication matters both for performance (fewer permutations) and UX (no duplicate solutions shown).
Happy solving! 🧮
Built with TypeScript and Next.js. Check out more math games at MathPuzzleHub.
