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

Smarter AI - Adjacent Priority

The AI builds connected territory by prioritising cells next to its existing claims.

8% of Ink War

Random moves work, but they don’t feel like playing against an opponent. The AI scatters claims across the board with no plan. This unit gives the AI its first strategic behaviour: prefer cells adjacent to existing territory.

The result is an AI that builds connected regions — clumps of blue cells that expand outward. It’s still beatable, but it feels more like a real opponent.

Run It

pasmonext --sna inkwar.asm inkwar.sna

Unit 10 Screenshot

Watch how the AI’s claims cluster together. Instead of random scattered cells, it builds from its first claim outward.

The Strategy

When humans play territory games, they instinctively expand from existing claims. Isolated cells are vulnerable; connected regions are stronger. The AI now does the same:

  1. Scan every empty cell on the board
  2. For each empty cell, count how many adjacent cells the AI owns
  3. Pick the cell with the highest count
  4. If no adjacent cells exist, fall back to random

This simple heuristic produces surprisingly natural-looking play.

Finding the Best Adjacent Cell

The core algorithm scans all 64 cells and tracks the best candidate:

We track two values: best_cell (the index of the best cell found so far) and best_score (how many AI neighbours it has). Each empty cell gets scored, and we keep the highest.

If no cell has any AI neighbours (score remains 0), we fall back to find_random_empty_cell — the same function from Unit 9.

Counting Adjacent Cells

For each candidate cell, we need to count how many neighbours belong to the AI:

This checks the four orthogonal neighbours (up, down, left, right). Edge detection prevents reading outside the board — row 0 has no up neighbour, column 7 has no right neighbour.

The helper function .caa_check_cell converts row/column to a board index and checks if that cell belongs to Player 2 (the AI).

Updating AI Move Selection

The change to ai_make_move is minimal — just call the new function instead of the random one:

Everything else stays the same: convert the cell index to row/column, claim the cell, switch players, update the display.

Why This Works

Adjacent priority creates emergent behaviour:

  • Early game: The AI’s first move is random (no existing territory), but subsequent moves cluster around it
  • Mid game: The AI expands its territory naturally, filling in gaps
  • Late game: When surrounded by enemy cells, it still finds the best local option

The AI doesn’t think ahead or consider defence — it just prefers cells next to its own. Yet this simple rule produces play that looks intelligent.

The Complete Code

Try This: Diagonal Neighbours

The current code only checks orthogonal neighbours (up, down, left, right). You could also check diagonal neighbours:

; Check up-left (row-1, col-1)
ld      a, b
or      a
jr      z, .skip_up_left
ld      a, c
or      a
jr      z, .skip_up_left
; ... check cell at (row-1, col-1)

This would make the AI value corner positions differently.

What You’ve Learnt

  • Cell scoring — Evaluating board positions numerically
  • Neighbour counting — Checking adjacent cells with boundary detection
  • Fallback strategies — Using random when heuristics don’t apply
  • Emergent behaviour — Simple rules producing complex-looking play

What’s Next

In Unit 11, we’ll add defensive behaviour — the AI will recognise when the human is building territory and try to block them.

What Changed

Unit 9 → Unit 10