Puzzle Page Mini Grid
A weekend newspaper runs a mini number puzzle: a 6 × 6 grid split into six boxes of 2 rows by 3 columns. Readers fill every empty square with a digit from 1 to 6 so that each row, each column and each box contains every digit exactly once.
The puzzle editor wants a program that fills in a grid before it goes to print. It must also catch broken puzzles: a typo in the clues can make a grid impossible, and those must not reach the readers.
Your task
Write solvePuzzle(grid). grid is a list of 6 strings of length 6. Each character is a digit '1' to '6' (a printed clue) or '.' (an empty square). The boxes are rows 0–1, 2–3 and 4–5, each split into columns 0–2 and 3–5. Square (r, c) is in box ⌊r/2⌋ × 2 + ⌊c/3⌋.
Return the completed grid as 6 strings of digits, keeping every clue where it is. If the puzzle has several solutions, return any one. If it has none, including when the clues themselves repeat a digit in a row, column or box, return an empty list.
solvePuzzle(grid: string[]) → string[] Several answers can be correct; any valid one is accepted.
Examples
grid = [".23...", "..136.", ".6..1.", "....2.", ".1.5..", ".3.2.."]
["623154", "451362", "362415", "145623", "214536", "536241"]
Row 0 needs a 6. The top-right box already has a 6 (row 1, column 4), so row 0's 6 must sit in the top-left box, and the only empty square there in row 0 is column 0. Reasoning like this, or plain trial and error, fills the rest. This puzzle has exactly one solution.
grid = ["...23.", ".61...", "4.....", "5.....", "......", "......"]
[]
Look at the top-left square. Its row already has 2 and 3, its column has 4 and 5, and its box has 6 and 1. No digit is left for it, so the puzzle cannot be completed.
Constraints
- grid has 6 rows, each a string of 6 characters from '1'–'6' and '.'.
- Any valid completed grid is accepted.
- Return [] when no completion exists.
Start with brute force
Try every combination of digits in the empty squares and check the full grid each time. With 24 empty squares that is 624, about 4.7 × 1018 grids. Even at a billion grids per second it would take well over a century.
The opposite shortcut, putting the first digit that fits in each square and never going back, is fast but often gets stuck: an early guess that looks fine can leave a later square with no digit at all.
Hints
0 of 4 shown- Hint 1
Pick any empty square and list the digits it could take. What should happen when that list is empty?
- Hint 2
Fill the empty squares one at a time, trying each digit that does not already appear in that square's row, column or box. If a later square has no digit left, an earlier choice was wrong: go back and try the next digit there.
- Hint 3
Keep three lists of sets (or boolean tables): digits used in each row, each column and each box. Then "can digit d go at (r, c)?" is three lookups. Fill them from the clues first, and if a clue repeats a digit, return
[]straight away. - Hint 4
Collect the empty squares in a list.
fill(i)returnstruewhen all squares from positionion are filled. For each allowed digit at squarei: place it and mark the three sets, and iffill(i + 1)succeeds returntrueright away (without undoing); otherwise clear the square and unmark the sets. Returnfalsewhen no digit works.
Walkthrough and solution
Unlocks when you pass every test. Stuck for a while? Looking is fine too, as long as you then close it and write the code yourself.
How to solve it
Fill squares in reading order. At each square, try only digits that do not clash with its row, column or box. If a square has no digit left, the mistake was earlier, so undo the most recent choice and try its next digit. That is backtracking.
Trace the first row of example 1, .23...:
- Square (0, 0): the box already has 1, 2 and 3, so try 4. Square (0, 3) can then only be 1, and (0, 4) only 5. Square (0, 5) needs the last digit, 6, but the top-right box already has a 6. Dead end: undo back to (0, 0).
- Square (0, 0) = 5. Now (0, 3) can be 1 or 4. With 1, square (0, 4) must be 4 and (0, 5) again needs 6: dead. With 4, square (0, 4) has no digit at all: dead. Undo back to (0, 0).
- Square (0, 0) = 6. This is the choice the example's reasoning predicted, and the search continues into the next rows from here.
Two details matter. When fill(i + 1) succeeds, return at once without undoing, or the solved grid gets erased on the way back up. And load the clues into the sets before searching, so a misprinted grid with a repeated clue is rejected instead of "solved".
function solvePuzzle(grid) {
const g = grid.map((row) => row.split(''));
const make = () => Array.from({ length: 6 }, () => new Set());
const rows = make(), cols = make(), boxes = make();
const boxOf = (r, c) => Math.floor(r / 2) * 2 + Math.floor(c / 3);
const empty = [];
for (let r = 0; r < 6; r++) {
for (let c = 0; c < 6; c++) {
const d = g[r][c], b = boxOf(r, c);
if (d === '.') { empty.push([r, c]); continue; }
if (rows[r].has(d) || cols[c].has(d) || boxes[b].has(d)) return []; // misprint
rows[r].add(d); cols[c].add(d); boxes[b].add(d);
}
}
function fill(i) {
if (i === empty.length) return true; // every square is filled
const [r, c] = empty[i], b = boxOf(r, c);
for (const d of '123456') {
if (rows[r].has(d) || cols[c].has(d) || boxes[b].has(d)) continue;
g[r][c] = d; rows[r].add(d); cols[c].add(d); boxes[b].add(d);
if (fill(i + 1)) return true; // keep the first full solution
g[r][c] = '.'; // dead end: undo, try the next digit
rows[r].delete(d); cols[c].delete(d); boxes[b].delete(d);
}
return false;
}
return fill(0) ? g.map((row) => row.join('')) : [];
}def solve_puzzle(grid):
g = [list(row) for row in grid]
rows = [set() for _ in range(6)]
cols = [set() for _ in range(6)]
boxes = [set() for _ in range(6)] # box of (r, c) is (r // 2) * 2 + c // 3
empty = []
for r in range(6):
for c in range(6):
d = g[r][c]
if d == '.':
empty.append((r, c))
continue
b = (r // 2) * 2 + c // 3
if d in rows[r] or d in cols[c] or d in boxes[b]:
return [] # misprint: a digit repeats
rows[r].add(d); cols[c].add(d); boxes[b].add(d)
def fill(i):
if i == len(empty):
return True # every square is filled
r, c = empty[i]
b = (r // 2) * 2 + c // 3
for d in '123456':
if d in rows[r] or d in cols[c] or d in boxes[b]:
continue
g[r][c] = d
rows[r].add(d); cols[c].add(d); boxes[b].add(d)
if fill(i + 1):
return True # keep the first full solution
g[r][c] = '.' # dead end: undo and try the next digit
rows[r].remove(d); cols[c].remove(d); boxes[b].remove(d)
return False
return [''.join(row) for row in g] if fill(0) else []Run your code to see results here. Run checks the examples. Submit checks every test, including hidden edge cases.