# Demonstrates: a board of cells (place with at_x), choosing from the free cells, winning patterns
# as a constant table, and "the winner is whoever just moved".
game: neighbours
label: Neighbours
players: "2 players"
num_players: 2
mode: competitive
summary: >
  A strip of 5 cells. Players take turns claiming a free cell. The first player to hold two
  adjacent cells wins. If the strip fills up with no such pair, the game is a draw.

constants:
  CELLS: 5
  PAIRS: [[0, 1], [1, 2], [2, 3], [3, 4]]   # adjacent cells; on a grid this would list rows, columns and diagonals

state:
  zones:
    # One element per cell; None = free. On an R x C grid, cell (r, c) is index r * C + c.
    strip: {kind: track, owner: global, visibility: public, init_x: "[None] * CELLS"}
  vars:
    current: {scope: global, type: ref, init: 0}

flow:
  id: game
  label: Game
  kind: loop
  over: players in turn
  over_x: players
  as: current
  order: fixed
  note: "no until: the game stops through the end conditions"
  children:
    - {id: move, label: Claim a cell, kind: step, actor: current player, actor_x: current, do: claim}

actions:
  claim:
    effect:
      # One action with a choice inside its effect: the step must then have actor_x (who chooses).
      - {op: choose, options: free cells, options_x: "[i for i in range(CELLS) if strip[i] is None]", bind: cell}
      # at_x (or a literal number in at) places at that position; without at it goes at the end.
      - {op: place, what: current player, what_x: current, to: strip, at_x: cell}

end:
  # The end check runs right after the step, before the loop moves on, so current is still the
  # player who just moved. End conditions cannot see local names (such as cell); use variables.
  - id: neighbours
    if: "the player who just moved holds two adjacent cells"
    if_x: "any(strip[a] == current and strip[b] == current for a, b in PAIRS)"
    result: win
    who_x: current
  - {id: full, if: "strip is full with no adjacent pair", if_x: "None not in strip", result: draw}
