Describe the turn before changing it

Pixel Pairs uses eight symbols twice, creating a deck of 16 tiles. A turn starts when the player reveals a first tile. The second eligible selection completes one move. A match stays open; a mismatch stays visible for 800 milliseconds, then closes. The move counter measures pairs of selections, not individual clicks.

These rules suggest the state you need: the deck, each tile's open and matched flags, the index of the first tile, a board lock, and a pending timer. Read pick with those values in mind. A visual card-flip animation is optional; correct turn state is what makes the game reliable.

Shuffle the deck fairly

The reset function duplicates the symbols and uses a backward Fisher–Yates shuffle. At each position, it selects one of the remaining positions uniformly, including the current position. That makes a deliberate, inspectable alternative to sorting with a random comparator.

const deck = [...symbols, ...symbols];
for (let i = deck.length - 1; i > 0; i--) {
  const j = Math.floor(Math.random() * (i + 1));
  [deck[i], deck[j]] = [deck[j], deck[i]];
}

This excerpt already exists in the download. For a theme change, replace the symbols while keeping eight distinct entries. Adding extra symbols also requires adjusting the completion condition and possibly the grid styling. Two visually identical entries would produce more than one intended pair, making the displayed rules ambiguous.

Reject invalid selections first

The first line of pick rejects input when the round is not running, the board is locked, or the tile is already open or matched. These checks protect the rules even if input arrives faster than the interface redraws. Clicking the first tile twice must not count as finding a matching pair.

When the second tile differs, the function captures both indexes in local variables before clearing first. The delayed callback uses those captured indexes. If it read a changing global selection instead, it could close the wrong cards after later input.

Tune the reveal time

Search for the 800 argument in the mismatch setTimeout. Try 1100 for a more relaxed game, then 550 for a faster one. Choose the time after reading and remembering two unfamiliar symbols on a small screen. An elaborate animation should fit within the reveal period rather than consume most of it.

Leave the board locked throughout the mismatch delay. After the callback closes the two cards, it unlocks the board and draws again. The current template rebuilds the tile buttons, so it explicitly restores keyboard focus to the relevant tile. Without that step, a keyboard user could lose their position after every selection.

Cancel work from the previous round

reset calls clearTimeout(timer) before creating a new deck. To see why, reveal a mismatch and immediately restart. A stale callback must not run against the new tile array. Clearing timers is part of resetting state, just like returning the move count to zero.

  • Click the same tile twice; moves should remain zero.
  • Select two different tiles, then repeatedly select a third during the delay.
  • Restart during that delay and wait longer than 800 milliseconds.
  • Finish all eight pairs and confirm no new selections are accepted.
  • Play using Tab and Enter and check where focus moves.

For an extension, add a move target without stopping play. Show whether the player beat their target after the eighth pair. This changes the objective while leaving the reliable match-resolution state machine intact.

Put this lesson into practice: download Pixel Pairs or play the original before making your changes.

Keep building