Skip to content
Game 5 Unit 5 of 128 1 hr learning time

Move Validation

Error feedback when you try to claim an occupied cell. The game pushes back.

4% of Ink War

Try claiming a cell that’s already taken. In the previous unit, nothing happened. That’s confusing - did the game register your keypress? Did something break?

This unit adds clear feedback: when you try to claim an occupied cell, you hear an error buzz and the border flashes red. The turn doesn’t change. You know exactly what went wrong.

Run It

pasmonext --sna inkwar.asm inkwar.sna

Unit 5 Screenshot

Claim a few cells. Then try to claim one that’s already taken. You’ll hear a harsh buzz and see the border flash red. Your turn doesn’t switch - you get another chance.

Validation Logic

The updated try_claim routine now branches on the cell state:

The key change is replacing ret nz with a jump to error handling. If the cell is empty (state = 0), we proceed with the claim. If it’s occupied (state ≠ 0), we provide feedback and return without changing turns.

The Error Sound

A harsh, low-frequency buzz signals “that’s wrong”:

Compared to the pleasant rising tone for valid claims, this sound is deliberately unpleasant:

  • Longer delay (80 cycles) creates a lower pitch
  • Shorter duration (30 loops) keeps it brief
  • The result is a quick “BZZT” that says “nope”

The Border Flash

Visual feedback reinforces the audio:

The border flashes red three times, with black gaps between flashes. Then update_border restores the correct player colour. The flashing is fast enough to feel urgent but not so fast it’s imperceptible.

Why Both Audio and Visual?

Good feedback uses multiple channels:

  • Audio grabs attention even when you’re not looking at the screen
  • Visual confirms what happened and draws your eye to the relevant area
  • Together, they create unmistakable feedback

In 1980s arcades, this pattern was everywhere. Error sounds and screen flashes told players “try again” without breaking the flow.

Delay Timing

The delays use simple countdown loops:

            ld      bc, 8000
.delay:
            dec     bc
            ld      a, b
            or      c
            jr      nz, .delay

At 3.5 MHz, this gives roughly 8000 × 30 cycles ≈ 70ms per flash. We check b or c because DEC BC doesn’t set the zero flag - we need to explicitly test if BC has reached zero.

The Complete Code

Try This: Different Error Sound

; Higher pitch, longer duration (more annoying!)
sound_error:
            ld      b, 50               ; Longer
.se_loop:
            ; ... same inner loop ...
            ld      c, 30               ; Shorter delay = higher pitch

Try This: Screen Flash Instead

Instead of flashing the border, flash the whole screen by temporarily setting all attributes to red:

; Warning: This is more disruptive!
flash_screen_error:
            ld      hl, $5800
            ld      de, $5801
            ld      bc, 767
            ld      (hl), %01010000     ; Red paper
            ldir
            ; ... delay ...
            ; Then redraw everything

What You’ve Learnt

  • Input validation - Check before acting, provide feedback for invalid input
  • Multi-channel feedback - Combine audio and visual cues for clarity
  • Error sounds - Low frequency + short duration = unpleasant buzz
  • Border control - OUT ($FE), A with colour in bits 0-2
  • Delay loops - BC countdown for timing (DEC BC doesn’t set flags, need OR test)

What’s Next

In Unit 6, we’ll detect when the board is full and declare a winner. The game gets a proper ending.

What Changed

Unit 4 → Unit 5