librarygames/robot-chase/RobotChase.xtl

Robot chase's rules, a library: robot-chase.xtl (scripted), play.xtl (at the terminal) and the web page all use these. A 16 by 16 board, you and 12 robots. Every turn you step (or wait, or teleport), then every robot steps one square toward you, all at once. Robots that land on the same square crash and leave a wreck; a robot that walks into a wreck is destroyed. Clear the board to win; meet a robot or a wreck and you lose. Ported from the COR24 BASIC robot-chase.bas.

The game is one flat vector of numbers (so a page can hand it back in): player row and column (0 to 15), teleports left, turn, the last event, the status (0 playing, 1 won, 2 lost), then the robots' rows, their columns, which are alive (12 each), and the wrecks (256 cells).

source · imports b: lib/Board.xtl

ˡn : Int

value · line 18

How many robots.

ˡn ← 12

ˡhead : Int

value · line 21

How many numbers come before the robots in the state.

ˡhead ← 6

Reading the state

ˡp̲os : a -> a

function · line 26

The player's row and column.

ˡp̲os ← { s → 2 t̲ake s }

ˡt̲ele : a -> a

function · line 29

Teleports left.

ˡt̲ele ← { s → 3 s̲elect s }

ˡt̲urn : a -> a

function · line 32

The turn number.

ˡt̲urn ← { s → 4 s̲elect s }

ˡe̲vent : a -> a

function · line 35

What happened last turn (a code for the message).

ˡe̲vent ← { s → 5 s̲elect s }

ˡs̲tatus : a -> a

function · line 38

0 playing, 1 won, 2 caught.

ˡs̲tatus ← { s → 6 s̲elect s }

ˡr̲obots : a -> a

function · line 41

The robots as a 2 by 12 matrix: row 1 their rows, row 2 their columns.

ˡr̲obots ← { s → (2 c̲at ˡn) r̲eshape (2 × ˡn) t̲ake ˡhead d̲rop s }

ˡa̲live : a -> a

function · line 44

Which robots still run (1) or are wrecked (0).

ˡa̲live ← { s → ˡn t̲ake (ˡhead + 2 × ˡn) d̲rop s }

ˡw̲recks : a -> a

function · line 47

The wreck piles on the 16 by 16 field (1 a pile).

ˡw̲recks ← { s → 16 16 r̲eshape -256 t̲ake s }

ˡc̲ell : Num a => a -> a

function · line 51

A square's number, 1 to 256, from row and column (or many, as a 2-row matrix).

ˡc̲ell ← { rc → 16 ᵇc̲ell rc }

ˡo̲nSquares : (Num a, Truthy a) => Int -> a -> a

function · line 56

How much of w lands on each of the 256 squares, when item i of w is on square k[i]: a 256 by 12 table of square against robot, times w (an inner product). With w the live robots, the robots per square.

ˡo̲nSquares ← { k w → ((r̲ange 256) '= t̲able k) '+ '× i̲nner w }

A new game

ˡn̲ew : Any a => a -> Int

function · line 63

13 different squares at random (the order of 256 random keys), as a 2 by 13 matrix of rows and columns: you on the first, a robot on each of the others.

ˡn̲ew ← { x →
  rc ← 16 ᵇr̲c (ˡn + 1) t̲ake g̲rade r̲oll! 256 r̲eshape 1000000
  (1 s̲elect₂ rc) c̲at 3 0 0 0 c̲at (r̲avel 1 d̲rop₂ rc) c̲at (ˡn r̲eshape 1) c̲at 256 r̲eshape 0
}

The robots' turn

ˡc̲hase : Int -> Int

function · line 75

Every live robot steps toward you at once: the sign of the difference (a fork, d [> - <] 0), a 2 by 12 matrix of -1, 0 and 1, added to their positions. Then: a robot on your square catches you; robots on a wreck die; robots sharing a square (more than one live robot there) crash and leave a wreck.

ˡc̲hase ← { s →
  a ← ˡa̲live s
  w ← r̲avel ˡw̲recks s
  d ← ((ˡp̲os s) 'l̲eft t̲able r̲ange ˡn) − ˡr̲obots s
  R ← (ˡr̲obots s) + ((2 c̲at ˡn) r̲eshape a) × d [> − <] 0
  k ← ˡc̲ell R
  caught ← '∨ r̲/ a × k = ˡc̲ell ˡp̲os s
  a1 ← a × n̲ot k s̲elect w
  crash ← a1 × 1 < k s̲elect k ˡo̲nSquares a1
  a2 ← a1 × n̲ot crash
  w2 ← 1 × w ∨ 0 < k ˡo̲nSquares crash
  caught = 1 ? (4 t̲ake s) c̲at 8 2 c̲at (r̲avel R) c̲at a c̲at w
  (4 t̲ake s) c̲at (ˡe̲vent s) c̲at (0 = '+ r̲/ a2) c̲at (r̲avel R) c̲at a2 c̲at w2
}
Used in: ˡm̲ove

Your turn

ˡk̲eys : Num a => Unit -> a

function · line 94

The numeric keypad: 7 8 9 up, 4 5 6 (5 waits), 1 2 3 down; the step for each key as a column of a 2 by 9 table (rows, columns).

ˡk̲eys ← { @ → 2 9 r̲eshape 1 1 1 0 0 0 -1 -1 -1 -1 0 1 -1 0 1 -1 0 1 }
Used in: ˡm̲ove

ˡo̲n : Int -> Int -> Int

function · line 98

What is on square q: 2 a wreck, 1 a live robot, 0 nothing.

ˡo̲n ← { s q →
  k ← ˡc̲ell q
  w ← k s̲elect r̲avel ˡw̲recks s
  r ← '∨ r̲/ (ˡa̲live s) × k = ˡc̲ell ˡr̲obots s
  (2 × w) m̲ax r
}

ˡm̲oved : Num a => a -> a -> a

function · line 108

The state after your move: h is where you are (row, column), the teleports left, the event and the status; the turn counts up.

ˡm̲oved ← { s h → (3 t̲ake h) c̲at (1 + ˡt̲urn s) c̲at (-2 t̲ake h) c̲at ˡhead d̲rop s }

ˡm̲ove : Int -> Int -> Int

function · line 116

A turn: key 1 to 9 steps (5 waits), 0 teleports, 99 resigns; then, unless the game ended, the robots chase. The event says what happened: 0 nothing, 1 blocked by the wall, 2 stepped onto a wreck, 3 walked into a robot, 4 no teleports left, 5 teleported, 6 teleported into a wreck, 7 teleported into a robot, 8 caught, 9 resigned.

ˡm̲ove ← { s c →
  c = 99 ? s ˡm̲oved (ˡp̲os s) c̲at (ˡt̲ele s) c̲at 9 2
  c = 0 ? ˡc̲hase ˡt̲eleport s
  q ← (ˡp̲os s) + c s̲elect₂ ˡk̲eys @
  wall ← '∨ r̲/ (q < 0) ∨ q > 15
  wall = 1 ? ˡc̲hase s ˡm̲oved (ˡp̲os s) c̲at (ˡt̲ele s) c̲at 1 0
  there ← s ˡo̲n q
  there = 2 ? s ˡm̲oved q c̲at (ˡt̲ele s) c̲at 2 2
  there = 1 ? s ˡm̲oved q c̲at (ˡt̲ele s) c̲at 3 2
  ˡc̲hase s ˡm̲oved q c̲at (ˡt̲ele s) c̲at 0 0
}
Used in: ᵘt̲urn, t, lost

ˡt̲eleport : Int -> Int

function · line 129

Teleport: the player to a random square, then the robots chase.

ˡt̲eleport ← { s →
  t ← ˡt̲ele s
  t ≤ 0 ? s ˡm̲oved (ˡp̲os s) c̲at 0 c̲at 4 0
  q ← 16 ᵇr̲c r̲oll! 256
  there ← s ˡo̲n q
  there = 2 ? s ˡm̲oved q c̲at (t − 1) c̲at 6 2
  there = 1 ? s ˡm̲oved q c̲at (t − 1) c̲at 7 2
  s ˡm̲oved q c̲at (t − 1) c̲at 5 0
}
Used in: ˡm̲ove

ˡm̲essage : Int -> Char

function · line 141

What the last event was, as the BASIC said it.

ˡm̲essage ← { s →
  e ← ˡe̲vent s
  d̲isclose (1 + e) s̲elect "" "BLOCKED BY WALL." "YOU STEPPED ONTO WRECKAGE." "YOU WALKED INTO A ROBOT." "NO TELEPORTS LEFT." "*BEEP* TELEPORTED." "TELEPORTED INTO WRECKAGE!" "TELEPORTED INTO A ROBOT!" "A ROBOT CAUGHT YOU!" "*** COMMAND RESIGNED ***"
}

What you see

ˡc̲odes : Int -> Int

function · line 150

Every square's code at once, from three 0/1 boards: 1 empty, 2 a robot, 3 a wreck, 4 you, 5 where something got you.

ˡc̲odes ← { s →
  code ← 1 + (0 < (ˡc̲ell ˡr̲obots s) ˡo̲nSquares ˡa̲live s) + 2 × r̲avel ˡw̲recks s
  kp ← ˡc̲ell ˡp̲os s
  me ← (r̲ange 256) = kp
  16 16 r̲eshape (code × n̲ot me) + me × 4 + 1 < kp s̲elect code
}

ˡv̲iew : Int -> Char

function · line 159

The board as text: . empty, R robot, * wreck, P you (X where something got you), spaced, with the BASIC's labels.

ˡv̲iew ← { s →
  lab ← 16 3 r̲eshape " 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F "
  top ← 1 35 r̲eshape "   0 1 2 3 4 5 6 7 8 9 A B C D E F "
  top c̲at lab c̲at₂ ᵇs̲paced (ˡc̲odes s) s̲elect ".R*PX"
}

ˡp̲icture : Int -> Char

function · line 167

The board as a picture X_eTaL draws (SVG text): the same characters, one per square of a 16 by 16 grid; []S_HOW shows it.

ˡp̲icture ← { s → ⎕G̲RID (ˡc̲odes s) s̲elect ".R*PX" }
Used in: ᵘs̲how

ˡl̲rs : Int -> Int

function · line 172

The long-range scan: the board in 4 by 4 regions, as a 4 by 4 by 4 by 4 array (region row, row, region column, column) summed over the rows and columns inside each region: robots, wrecks, and where you are.

ˡl̲rs ← { s →
  robots ← (ˡc̲ell ˡr̲obots s) ˡo̲nSquares ˡa̲live s
  me ← (r̲ange 256) = ˡc̲ell ˡp̲os s
  (ʰr̲egions robots) c̲at₂ (ʰr̲egions r̲avel ˡw̲recks s) c̲at₂ ʰr̲egions me
}

ʰr̲egions : Int -> Int

function (private) · line 178

The 256 squares as 4 by 4 regions, each summed (private).

ʰr̲egions ← { v → '+ r̲/₂₄ 4 4 4 4 r̲eshape v }
Used in: ˡl̲rs