Building an Interactive Sudoku Game

8 August 2024 (2y ago)

I spent a few weeks wanting to flex my TypeScript, so I built a small Sudoku site. I called it Sudoka, Good and Honest Sudoku. This is the first of three posts pulling it apart: the data model here, the puzzle generation next, and the interactive board last.

I started with the types, which is backwards from how I usually build. Normally I'd get something on screen first and tidy up later. But with learning stricter Typescript I wanted better typing, fill a 9x9 grid so every row, column, and 3x3 box holds each digit 1 to 9 exactly once, and when the whole thing is rules, getting the data model right first means a lot of bugs simply can't happen.

The cell

Everything starts with one cell. It needs to know where it is, what it holds, and one thing that turns out to matter more than it looks:

type Cell = {
    row: number;
    col: number;
    value: number | null;
    editable?: boolean;
};

value is a number 1 to 9, or null when the cell is empty. The most important field is editable. A Sudoku has two kinds of cell: the clues you're given and can't touch, and the ones you fill in. That single boolean is the whole distinction, and it ends up driving almost everything later, which cells accept input, which get styled differently, which the win-check should care about. One flag, a lot of downstream weight.

The board

The board is just a grid of cells, so the type is honest about that:

type Board = Cell[][];

A two-dimensional array of Cell. Nothing clever, but now anything that touches the board, validation, rendering, generation, speaks the same language.

An empty board

With the types in place, building an empty board is a one-liner of intent: a 9x9 grid where every cell knows its position, holds nothing, and is editable.

const createBoard = (): Board => {
    return Array.from({ length: 9 }, (_, row) =>
        Array.from({ length: 9 }, (_, col) => ({
            row: row,
            col: col,
            value: null,
            editable: true,
        }))
    );
};

That's the foundation. It looks like almost nothing, which is the point, the types did the thinking up front so this is just filling in the shape.

Next post: the part that fills this empty grid with an actual puzzle, which turns out to be the same problem as solving one.