Ian Watkins iankwatkins
Cheat Sheet

Top Tips From the Course

The transferable ideas and the JavaScript traps, on one page. Keep it open while you build; revisit it for your next game.

The one pattern under every game

state render input update state render→ …

The game lives in data (state). The screen is a render of that data. Input changes state; you re-render. Never reach into the screen to fake a change. Change the data and redraw, so the screen is always an honest mirror of the state.

Ten habits that transfer to any game

  1. State is the truth. Anything on screen must live in a variable, get set when it changes, and get cleared on reset. Even scheduled work counts (cancel pending timers with clearTimeout).
  2. render() owns the whole screen. One function reads state and draws everything (board, status, score). One place to look when something's wrong.
  3. State is plural. A game tracks several variables, not just the playing field: whose turn, is it over, the score, the difficulty. Each gets its own home.
  4. Order encodes the rules. Place a mark → check win → check draw → flip turn. Win beats draw beats continue. The sequence is the rulebook.
  5. Use a value before you change it. Most ordering bugs come from advancing state (flipping the player) before you've read it. Read first, then advance.
  6. Rules as data. The eight winning lines are an array you loop over, not a wall of ifs. Shorter, readable, and portable to a bigger board.
  7. One variable, one concept. Don't make a variable do two jobs (the mark on the board and the display name). Derive views from state with a lookup.
  8. Return data, not a flag. A function that returns the winning line is more useful than one returning true. You can still test it for truthiness.
  9. Decide vs act. Strategy functions return a choice; one shared function performs it. Many ways to decide, one way to act.
  10. Simulate → check → undo. To judge a move, try it on the board, evaluate, then restore. This scales from a one-move win check all the way up to recursive minimax.

The five parts of the game, and their jobs

Part Job Example
State Everything needed to resume the game. Plain variables. board, currentPlayer, isGameOver, winningLine, scores, difficulty
render() Read state, draw the whole screen. Rebuild cells from board; show status and score
Input handler Validate, change state, then render. handleClick(i)
Rules Guards and checks for what's allowed and what just happened. checkWin, isBoardFull, guard clauses
Reset Restore every state variable to its start, then render. resetGame()

Minimax in four lines

score endings:  computer win +10 | human win -10 | draw 0
computer's turn -> take the MAX of the replies (wants high)
opponent's turn -> take the MIN of the replies (assume best play)
base case       -> game over: return that ending's score

Recursion here is simulate-check-undo where "check" is another simulate-check-undo, until a finished game returns a real number that bubbles back up the tree. Because it assumes you always play your best, it never walks into a loss.

JavaScript traps that fail SILENTLY

The big theme of the whole course: a lot of bad JavaScript and CSS produces no error, it just quietly does nothing. When something doesn't work and you see no error, suspect a typo and open your browser's developer tools (the Console and the element inspector).
Trap Fix / rule
arr.push[i] with square brackets () calls a function, [] looks up a property. Use arr.push(i).
const el = element.addEventListener(...) addEventListener returns undefined. Split "grab the element" from "attach the listener."
Re-declaring const x in the same scope A SyntaxError blanks the entire file (it fails before running). If nothing renders, check the Console for a red SyntaxError.
Using a variable declared inside { } outside those braces Block scope: a let/const only lives inside the braces it was declared in.
if (!choice) when 0 is a valid value 0 is "falsy." Use if (choice === null) when zero is a real, meaningful value.
Passing fn() instead of fn to a listener or timer Pass the function (no parentheses) to be called later. fn() runs it now and passes its return value.
color: var var(--ink), transform: uppercase, or a mistyped #id Invalid CSS is ignored with no error. Inspect the element; bad lines show struck-through in the Styles panel.
Adding a class with className = "win" That replaces all classes. Use classList.add("win") to add one without wiping the rest.

Reusing this skeleton for your next game (Snake)

Tic-tac-toe Snake
state: a board array state: the snake body (array of cells) + food position + direction
input: a click runs handleClick input: arrow keys change the direction
update: happens on each click update: happens on a timer (setInterval), so the game animates itself
render: rebuild cells from the board render: draw the snake and food from state
rules: win and draw checks rules: hit a wall or yourself = game over; eat food = grow

Same skeleton. The only genuinely new idea in Snake is that the update step fires on a clock instead of waiting for input. You already own everything else.