April 4, 2026 Claude upgrade to Solve Griddler 260236 Illustrated Book Code
Here’s the actual code changes Claude made. I’m still impressed that it changes the comments too!
The fix: replace the naive "first unknown cell" with a minimum-remaining-values (MRV) heuristic — pick the row or column with
the fewest unknown cells remaining. This ensures backtracking happens in the most constrained areas first, where wrong guesses
contradict quickly.
⏺ Update(BoardSolverCmdLine.cpp)
⎿ Added 52 lines, removed 19 lines
203 if (seen.insert(clue.color).second)
204 candidates.push_back(clue.color);
205
206 - // Find the first unknown cell.
207 - for (short r = 0; r < (short)theBoard.rowCount; r++){
206 + // Find the most constrained unknown cell (MRV heuristic).
207 + // Pick the row or column with the fewest unknown cells remaining;
208 + // guessing there triggers contradictions earliest in the search tree.
209 + short bestR = -1, bestC = -1;
210 + {
211 + int minUnknowns = INT_MAX;
212 +
213 + for (short r = 0; r < (short)theBoard.rowCount; r++){
214 + int cnt = 0;
215 + short firstC = -1;
216 + for (short c = 0; c < (short)theBoard.columnCount; c++){
217 + if (theBoard.contents[r][c] == UNKNOWN_COLOR){
218 + cnt++;
219 + if (firstC < 0) firstC = c;
220 + }
221 + }
222 + if (cnt > 0 && cnt < minUnknowns){
223 + minUnknowns = cnt;
224 + bestR = r;
225 + bestC = firstC;
226 + }
227 + }
228 +
229 for (short c = 0; c < (short)theBoard.columnCount; c++){
209 - if (theBoard.contents[r][c] != UNKNOWN_COLOR)
210 - continue;
230 + int cnt = 0;
231 + short firstR = -1;
232 + for (short r = 0; r < (short)theBoard.rowCount; r++){
233 + if (theBoard.contents[r][c] == UNKNOWN_COLOR){
234 + cnt++;
235 + if (firstR < 0) firstR = r;
236 + }
237 + }
238 + if (cnt > 0 && cnt < minUnknowns){
239 + minUnknowns = cnt;
240 + bestR = firstR;
241 + bestC = c;
242 + }
243 + }
244 + }
245
212 - // Save board state, then try each candidate color.
213 - Array2D_uchar savedBoard = theBoard;
246 + if (bestR < 0)
247 + return false; // No unknown cells; IsSolved() should have caught this.
248
215 - for (unsigned char color : candidates){
216 - guessCount++;
217 -
218 - theBoard = savedBoard;
219 - theBoard.contents[r][c] = color;
249 + // Save board state, then try each candidate color.
250 + Array2D_uchar savedBoard = theBoard;
251
221 - if (SolveWithGuessing())
222 - return true;
223 - }
252 + for (unsigned char color : candidates){
253 + guessCount++;
254
225 - // All candidates failed → contradiction at this branch.
226 - theBoard = savedBoard;
227 - return false;
228 - }
255 + theBoard = savedBoard;
256 + theBoard.contents[bestR][bestC] = color;
257 +
258 + if (SolveWithGuessing())
259 + return true;
260 }
261
231 - return false; // Unreachable if IsSolved() is accurate.
262 + // All candidates failed → contradiction at this branch.
263 + theBoard = savedBoard;
264 + return false;
265 }
266
267 /* ------------------------------------------------------------------------ */
⏺ Now I'll rewrite BoardSolverCmdLine.cpp to add probing:
Read 1 file (ctrl+o to expand)
⏺ Update(BoardSolverCmdLine.cpp)
⎿ Added 80 lines, removed 16 lines
177 }
178
179 /* ------------------------------------------------------------------------ */
180 -// Solve With Guessing (backtracking search)
180 +// Build Candidates
181 /* ------------------------------------------------------------------------ */
182 -// 1. Run pure constraint propagation to fixed point.
183 -// 2. If solved, return true.
184 -// 3. If contradicted, return false.
185 -// 4. Otherwise pick the first unknown cell and try each plausible color:
186 -// • EMPTY_COLOR first (often triggers early contradictions)
187 -// • Then each color that actually appears in the row or column clues.
188 -// 5. For each candidate, save board state, apply guess, recurse.
189 -// Restore on failure.
182 +// Returns the ordered list of colors to try when guessing a cell:
183 +// EMPTY first (often triggers early contradictions), then every color that
184 +// appears in any row clue.
185 /* ------------------------------------------------------------------------ */
191 -bool BoardSolverCmdLine::SolveWithGuessing(){
192 -
193 - if (!SolveLogic()) return false;
194 - if (IsSolved()) return true;
195 -
196 - // Collect candidate colors to try for the guessed cell:
197 - // EMPTY first, then every color that appears in any clue.
186 +vector<unsigned char> BoardSolverCmdLine::BuildCandidates() const {
187 vector<unsigned char> candidates;
188 candidates.push_back(EMPTY_COLOR);
189 set<unsigned char> seen;
...
191 for (auto &clue : line)
192 if (seen.insert(clue.color).second)
193 candidates.push_back(clue.color);
194 + return candidates;
195 +}
196
197 +/* ------------------------------------------------------------------------ */
198 +// Solve With Probing
199 +/* ------------------------------------------------------------------------ */
200 +// Alternates between constraint propagation (SolveLogic) and probing.
201 +// Probing: for each unknown cell, try each candidate color with full
202 +// constraint propagation. If all colors except one lead to contradictions,
203 +// the cell is forced to the non-contradicting color. Repeats until stable.
204 +// This converts hidden forced moves into explicit ones, slashing the number
205 +// of actual backtracking guesses needed.
206 +// Returns false if a contradiction is detected at any point.
207 +/* ------------------------------------------------------------------------ */
208 +bool BoardSolverCmdLine::SolveWithProbing(const vector<unsigned char> &candidates){
209 +
210 + while (true){
211 + if (!SolveLogic()) return false;
212 + if (IsSolved()) return true;
213 +
214 + bool anyForced = false;
215 +
216 + short numRows = (short)theBoard.rowCount;
217 + short numCols = (short)theBoard.columnCount;
218 +
219 + for (short r = 0; r < numRows; r++){
220 + for (short c = 0; c < numCols; c++){
221 + if (theBoard.contents[r][c] != UNKNOWN_COLOR) continue;
222 +
223 + unsigned char forcedColor = UNKNOWN_COLOR;
224 + int validCount = 0;
225 +
226 + for (unsigned char color : candidates){
227 + Array2D_uchar savedBoard = theBoard;
228 + theBoard.contents[r][c] = color;
229 + bool ok = SolveLogic();
230 + theBoard = savedBoard;
231 +
232 + if (ok){
233 + forcedColor = color;
234 + if (++validCount > 1) break; // Multiple valid colors — not forced.
235 + }
236 + }
237 +
238 + if (validCount == 0) return false; // All colors contradict → dead end.
239 + if (validCount == 1){
240 + // Cell is forced to the one valid color.
241 + theBoard.contents[r][c] = forcedColor;
242 + anyForced = true;
243 + }
244 + }
245 + }
246 +
247 + if (!anyForced) break; // Probing found nothing new; hand off to backtracking.
248 + }
249 +
250 + return true;
251 +}
252 +
253 +/* ------------------------------------------------------------------------ */
254 +// Solve With Guessing (backtracking search)
255 +/* ------------------------------------------------------------------------ */
256 +// 1. Run probing (constraint propagation + look-ahead) to fixed point.
257 +// 2. If solved, return true.
258 +// 3. If contradicted, return false.
259 +// 4. Otherwise pick the most constrained unknown cell (MRV heuristic):
260 +// the row or column with the fewest remaining unknowns.
261 +// 5. Try each candidate color; recurse. Restore on failure.
262 +/* ------------------------------------------------------------------------ */
263 +bool BoardSolverCmdLine::SolveWithGuessing(){
264 +
265 + vector<unsigned char> candidates = BuildCandidates();
266 +
267 + if (!SolveWithProbing(candidates)) return false;
268 + if (IsSolved()) return true;
269 +
270 // Find the most constrained unknown cell (MRV heuristic).
271 // Pick the row or column with the fewest unknown cells remaining;
272 // guessing there triggers contradictions earliest in the search tree.
⏺ Now add the declarations to the header:
⏺ Update(BoardSolverCmdLine.hpp)
⎿ Added 12 lines, removed 2 lines
61 // Returns false immediately if a contradiction is detected in any line.
62 bool SolveLogic();
63
64 - // Recursive backtracking search. Calls SolveLogic(), then if stuck picks an
65 - // unknown cell and tries each plausible color. Returns false on contradiction.
64 + // Returns the ordered list of candidate colors to try when guessing: EMPTY
65 + // first, then every color that appears in any row clue.
66 + vector<unsigned char> BuildCandidates() const;
67 +
68 + // Alternates SolveLogic with probing: forces cells whose only non-contradicting
69 + // color is determined by look-ahead. Reduces guesses needed. Returns false on
70 + // contradiction.
71 + bool SolveWithProbing(const vector<unsigned char> &candidates);
72 +
73 + // Recursive backtracking search. Calls SolveWithProbing(), then if stuck picks
74 + // the most constrained unknown cell (MRV) and tries each plausible color.
75 + // Returns false on contradiction.
76 bool SolveWithGuessing();
77
78 void extractRow(short row, vector<solverCell> &line) const;