Word Search++

A browser-based word search puzzle built on the Canvas 2D API. Pick a category or use the Random pool, set your grid size, and find all hidden words to complete the puzzle. Visit /puzzles/wordsearch.

Architecture

The puzzle is entirely client-side JavaScript. The server provides:

  1. The template (templates/wordsearch.html) — page layout with controls (category, grid size, generate), the canvas area, and a word-list sidebar.
  2. The game script (static/js/wordsearch.js) — ~690 lines handling grid generation, canvas rendering, mouse/touch interaction, timer tracking, and the Random-word API fetch. Exports pure functions for Node.js testing.
  3. The game route (src/routers/wordsearch.py) — serves the template and the /api/wordsearch/random-words endpoint.
  4. The word list (data/words/english.txt) — ~132K curated English words (3–10 letters, lowercase alpha only) filtered from the system dictionary.

Grid Generation

When the user clicks Generate, the following happens:

  1. An N×N grid (e.g. 20×20) is created as a 2D array of null cells.
  2. A random subset of words is selected from the active category. For the Random category, words are fetched from /api/wordsearch/random-words?count=X where X = max(5, min(40, floor(size / 2))).
  3. Words are shuffled, sorted longest-first (harder to place), and the top X are kept.
  4. Each word is placed by randomly choosing a starting cell and a direction (8 directions: N, S, E, W, NE, NW, SE, SW). Up to 300 attempts are made per word.
  5. Placement is rejected if any letter in the word would conflict with an existing letter in the grid. Intersections where letters match are allowed.
  6. Remaining empty cells are filled with random uppercase A–Z letters.
  7. The grid is rendered on the canvas and the word list appears in the sidebar.

Word placement algorithm

for each word (sorted longest-first):
    for up to 300 attempts:
        pick random direction (0–7)
        pick random starting cell
        if word fits within grid bounds and all intersecting cells match:
            place the word
            record cell positions
            break

Direction mapping:

Index Direction Offset (dr, dc)
0 Right (0, +1)
1 Down-Right (+1, +1)
2 Down (+1, 0)
3 Down-Left (+1, -1)
4 Left (0, -1)
5 Up-Left (-1, -1)
6 Up (-1, 0)
7 Up-Right (-1, +1)

Canvas Rendering

The canvas fills its container at 100% width with auto height. Cell size is calculated as max(10, min(50, availableWidth / cols)) — capped at 50px so cells don't become comically large on small grids. The grid is horizontally centred within the canvas.

Colours are read from CSS custom properties (--bs-body-bg, --bs-body-color, --bs-primary, --bs-success, --bs-border-color) to match the active Bootswatch theme. When the theme changes, a load event listener on the theme stylesheet triggers a re-render.

HiDPI support: the canvas buffer is scaled by window.devicePixelRatio while CSS display size stays at 100%, ensuring crisp rendering on Retina displays.

Interaction

Selecting letters

Click and drag across the canvas to highlight a contiguous line of cells. The selection is constrained to the 8 major axes (horizontal, vertical, 45° diagonal) by determining the dominant direction from the start cell to the current mouse position.

Confirming a word

Click a word in the sidebar word list. The highlighted selection is read left-to-right and right-to-left and compared to the clicked word. If either direction matches, the word is marked as found — cells on the grid turn green and the word in the list is strikethrough.

Direction hints

By default, direction indicators (arrow badges) next to each word are hidden. Click Reveal Hints to show them; click Hide Hints to hide them again.

Timer

A timer starts when the puzzle is generated. It ticks in the status bar (0h 0m 0s format) and stops when all words are found. A completion modal shows your time, word count, and grid size. The timer also stops if you click Reveal All.

Word Categories

Category Source Word count
Animals Curated list 39
Countries Curated list 30
Programming Curated list 30
Space Curated list 26
Food Curated list 32
Random data/words/english.txt ~132K

The Random category fetches a fresh subset from the server each time, guaranteeing variety.

Files

File Role
templates/wordsearch.html Game template (layout, styles, modals)
static/js/wordsearch.js Game engine (grid generation, rendering, interaction)
src/routers/wordsearch.py GET /puzzles/wordsearch + GET /api/wordsearch/random-words
data/words/english.txt Curated 132K-word pool for Random mode
tests/test_routes.py Route tests for puzzle pages and API endpoint