VOOZH about

URL: https://dev.to/bwanachairman/i-built-a-crossword-solver-without-react-and-survived-to-tell-the-tale-f38

⇱ I built a crossword solver without React and survived to tell the tale 😅 - DEV Community


For the past 3 years, my brain has been wired to:

  • npx create-vite@latest
  • useState, useEffect, useCallback
  • components inside components inside components

So when I decided to build a crossword puzzle solver the "vanilla way" — no framework, no JSX, no hot reload — I felt naked.

But here we are. With raw 2D arrays, nested loops, and the humbling realization that I still struggle with grid[r][c+len].

The Slots Extractor (I'm actually proud of this)

function extractSlots(grid) {
 const slots = [];
 for (let r = 0; r < grid.length; r++) {
 for (let c = 0; c < grid[0].length; c++) {
 if (grid[r][c] === '.') continue;

 // Across word?
 if (c === 0 || grid[r][c-1] === '.') {
 let len = 0;
 while (grid[r][c+len] && grid[r][c+len] !== '.') len++;
 if (len > 1) slots.push({row:r, col:c, len, dir:'across'});
 }

 // Down word?
 if (r === 0 || grid[r-1][c] === '.') {
 let len = 0;
 while (grid[r+len] && grid[r+len][c] !== '.') len++;
 if (len > 1) slots.push({row:r, col:c, len, dir:'down'});
 }
 }
 }
 return slots;
}

The Embarrassing Bug I Spent 2 Hours On

// My original canPlace function (BROKEN):
function canPlace(slot, word, grid) {
 for (let i = 0; i < slot.len; i++) {
 // ... check logic ...
 return true // ← 💀 inside the loop! Only checked first letter!
 }
}

Yes. I really did that. Returned true after checking only the first character. My puzzle was accepting "abc" into a slot that needed "axc" because it saw the 'a' matched and dipped.

Shoutout to Phil Arturo 🙌

Phil, you kept telling me: "You need to understand the fundamentals, not just the frameworks."

I laughed then. I cry-laughed while debugging that return true at 11pm.

You were right. Vanilla JS humbles you. But it also teaches you how to think in loops, indices, and grid coordinates without a linter holding your hand.

The Fixed Version (Works Now, I Swear)

function canPlace(slot, word, grid) {
 const {row, col, len, dir} = slot;
 if (word.length !== len) return false;

 for (let i = 0; i < len; i++) {
 let r = row, c = col;
 if (dir === 'across') c = col + i;
 else r = row + i;

 const gridChar = grid[r][c];
 const wordChar = word[i];

 if (gridChar !== '.' && gridChar !== wordChar) {
 return false; // Conflict!
 }
 }
 return true; // ← OUTSIDE the loop, where it belongs
}

Final Takeaway

React is awesome. Vite is lightning fast. But once in a while, build something with just JavaScript, a terminal, and console.log debugging.

You'll feel stupid for 30 minutes. Then you'll feel like a god.

And you'll never put a return inside a loop again. 🤞

Thanks again Phil Arturo — this one's for you.


vanillajs #crossword #webdev #reactdev #fundamentals