sourcegames/robot-chase/RobotChase.xtl

1⍝# Robot chase's rules, a library: robot-chase.xtl (scripted), play.xtl 2⍝# (at the terminal) and the web page all use these. A 16 by 16 board, 3⍝# you and 12 robots. Every turn you step (or wait, or teleport), then 4⍝# every robot steps one square toward you, all at once. Robots that 5⍝# land on the same square crash and leave a wreck; a robot that walks 6⍝# into a wreck is destroyed. Clear the board to win; meet a robot or a 7⍝# wreck and you lose. Ported from the COR24 BASIC robot-chase.bas. 8⍝# 9⍝# The game is one flat vector of numbers (so a page can hand it back 10⍝# in): player row and column (0 to 15), teleports left, turn, the last 11⍝# event, the status (0 playing, 1 won, 2 lost), then the robots' rows, 12⍝# their columns, which are alive (12 each), and the wrecks (256 cells). 13 14ᵇ⁼u̲se< "Board" 15 16⍝ :: Int 17⍝# How many robots. 18ˡn ← 12 19⍝ :: Int 20⍝# How many numbers come before the robots in the state. 21ˡhead ← 6 22 23⍝## Reading the state 24⍝ :: a -> a 25⍝# The player's row and column. 26ˡp̲os ← { s → 2 t̲ake s } 27⍝ :: a -> a 28⍝# Teleports left. 29ˡt̲ele ← { s → 3 s̲elect s } 30⍝ :: a -> a 31⍝# The turn number. 32ˡt̲urn ← { s → 4 s̲elect s } 33⍝ :: a -> a 34⍝# What happened last turn (a code for the message). 35ˡe̲vent ← { s → 5 s̲elect s } 36⍝ :: a -> a 37⍝# 0 playing, 1 won, 2 caught. 38ˡs̲tatus ← { s → 6 s̲elect s } 39⍝ :: a -> a 40⍝# The robots as a 2 by 12 matrix: row 1 their rows, row 2 their columns. 41ˡr̲obots ← { s → (2 c̲at ˡn) r̲eshape (2 × ˡn) t̲ake ˡhead d̲rop s } 42⍝ :: a -> a 43⍝# Which robots still run (1) or are wrecked (0). 44ˡa̲live ← { s → ˡn t̲ake (ˡhead + 2 × ˡn) d̲rop s } 45⍝ :: a -> a 46⍝# The wreck piles on the 16 by 16 field (1 a pile). 47ˡw̲recks ← { s → 16 16 r̲eshape -256 t̲ake s } 48⍝ :: Num a => a -> a 49⍝# A square's number, 1 to 256, from row and column (or many, as a 50⍝# 2-row matrix). 51ˡc̲ell ← { rc → 16 ᵇc̲ell rc } 52⍝ :: (Num a, Truthy a) => Int -> a -> a 53⍝# How much of w lands on each of the 256 squares, when item i of w is 54⍝# on square k[i]: a 256 by 12 table of square against robot, times w 55⍝# (an inner product). With w the live robots, the robots per square. 56ˡo̲nSquares ← { k w → ((r̲ange 256) '= t̲able k) '+ '× i̲nner w } 57 58⍝## A new game 59⍝ :: Any a => a -> Int 60⍝# 13 different squares at random (the order of 256 random keys), as a 2 61⍝# by 13 matrix of rows and columns: you on the first, a robot on each of 62⍝# the others. 63ˡn̲ew ← { x → 64 rc ← 16 ᵇr̲c (ˡn + 1) t̲ake g̲rade r̲oll! 256 r̲eshape 1000000 65 (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 66} 67 68⍝## The robots' turn 69⍝ :: Int -> Int 70⍝# Every live robot steps toward you at once: the sign of the difference 71⍝# (a fork, d [> - <] 0), a 2 by 12 matrix of -1, 0 and 1, added to their 72⍝# positions. Then: a robot on your square catches you; robots on a 73⍝# wreck die; robots sharing a square (more than one live robot there) 74⍝# crash and leave a wreck. 75ˡc̲hase ← { s → 76 a ← ˡa̲live s 77 w ← r̲avel ˡw̲recks s 78 d ← ((ˡp̲os s) 'l̲eft t̲able r̲ange ˡn) − ˡr̲obots s 79 R ← (ˡr̲obots s) + ((2 c̲at ˡn) r̲eshape a) × d [> − <] 0 80 k ← ˡc̲ell R 81 caught ← '∨ r̲/ a × k = ˡc̲ell ˡp̲os s 82 a1 ← a × n̲ot k s̲elect w 83 crash ← a1 × 1 < k s̲elect k ˡo̲nSquares a1 84 a2 ← a1 × n̲ot crash 85 w2 ← 1 × w ∨ 0 < k ˡo̲nSquares crash 86 caught = 1 ? (4 t̲ake s) c̲at 8 2 c̲at (r̲avel R) c̲at a c̲at w 87 (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 88} 89 90⍝## Your turn 91⍝ :: Num a => Unit -> a 92⍝# The numeric keypad: 7 8 9 up, 4 5 6 (5 waits), 1 2 3 down; the step 93⍝# for each key as a column of a 2 by 9 table (rows, columns). 94ˡk̲eys ← { @ → 2 9 r̲eshape 1 1 1 0 0 0 -1 -1 -1 -1 0 1 -1 0 1 -1 0 1 } 95 96⍝ :: Int -> Int -> Int 97⍝# What is on square q: 2 a wreck, 1 a live robot, 0 nothing. 98ˡo̲n ← { s q → 99 k ← ˡc̲ell q 100 w ← k s̲elect r̲avel ˡw̲recks s 101 r ← '∨ r̲/ (ˡa̲live s) × k = ˡc̲ell ˡr̲obots s 102 (2 × w) m̲ax r 103} 104 105⍝ :: Num a => a -> a -> a 106⍝# The state after your move: h is where you are (row, column), the 107⍝# teleports left, the event and the status; the turn counts up. 108ˡ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 } 109 110⍝ :: Int -> Int -> Int 111⍝# A turn: key 1 to 9 steps (5 waits), 0 teleports, 99 resigns; then, 112⍝# unless the game ended, the robots chase. The event says what happened: 113⍝# 0 nothing, 1 blocked by the wall, 2 stepped onto a wreck, 3 walked 114⍝# into a robot, 4 no teleports left, 5 teleported, 6 teleported into a 115⍝# wreck, 7 teleported into a robot, 8 caught, 9 resigned. 116ˡm̲ove ← { s c → 117 c = 99 ? s ˡm̲oved (ˡp̲os s) c̲at (ˡt̲ele s) c̲at 9 2 118 c = 0 ? ˡc̲hase ˡt̲eleport s 119 q ← (ˡp̲os s) + c s̲elect₂ ˡk̲eys @ 120 wall ← '∨ r̲/ (q < 0) ∨ q > 15 121 wall = 1 ? ˡc̲hase s ˡm̲oved (ˡp̲os s) c̲at (ˡt̲ele s) c̲at 1 0 122 there ← s ˡo̲n q 123 there = 2 ? s ˡm̲oved q c̲at (ˡt̲ele s) c̲at 2 2 124 there = 1 ? s ˡm̲oved q c̲at (ˡt̲ele s) c̲at 3 2 125 ˡc̲hase s ˡm̲oved q c̲at (ˡt̲ele s) c̲at 0 0 126} 127⍝ :: Int -> Int 128⍝# Teleport: the player to a random square, then the robots chase. 129ˡt̲eleport ← { s → 130 t ← ˡt̲ele s 131 t ≤ 0 ? s ˡm̲oved (ˡp̲os s) c̲at 0 c̲at 4 0 132 q ← 16 ᵇr̲c r̲oll! 256 133 there ← s ˡo̲n q 134 there = 2 ? s ˡm̲oved q c̲at (t − 1) c̲at 6 2 135 there = 1 ? s ˡm̲oved q c̲at (t − 1) c̲at 7 2 136 s ˡm̲oved q c̲at (t − 1) c̲at 5 0 137} 138 139⍝ :: Int -> Char 140⍝# What the last event was, as the BASIC said it. 141ˡm̲essage ← { s → 142 e ← ˡe̲vent s 143 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 ***" 144} 145 146⍝## What you see 147⍝ :: Int -> Int 148⍝# Every square's code at once, from three 0/1 boards: 1 empty, 2 a 149⍝# robot, 3 a wreck, 4 you, 5 where something got you. 150ˡc̲odes ← { s → 151 code ← 1 + (0 < (ˡc̲ell ˡr̲obots s) ˡo̲nSquares ˡa̲live s) + 2 × r̲avel ˡw̲recks s 152 kp ← ˡc̲ell ˡp̲os s 153 me ← (r̲ange 256) = kp 154 16 16 r̲eshape (code × n̲ot me) + me × 4 + 1 < kp s̲elect code 155} 156⍝ :: Int -> Char 157⍝# The board as text: . empty, R robot, * wreck, P you (X where 158⍝# something got you), spaced, with the BASIC's labels. 159ˡv̲iew ← { s → 160 lab ← 16 3 r̲eshape " 0 1 2 3 4 5 6 7 8 9 A B C D E F " 161 top ← 1 35 r̲eshape " 0 1 2 3 4 5 6 7 8 9 A B C D E F " 162 top c̲at lab c̲at₂ ᵇs̲paced (ˡc̲odes s) s̲elect ".R*PX" 163} 164⍝ :: Int -> Char 165⍝# The board as a picture X_eTaL draws (SVG text): the same characters, 166⍝# one per square of a 16 by 16 grid; []S_HOW shows it. 167ˡp̲icture ← { s → ⎕G̲RID (ˡc̲odes s) s̲elect ".R*PX" } 168⍝ :: Int -> Int 169⍝# The long-range scan: the board in 4 by 4 regions, as a 4 by 4 by 4 by 170⍝# 4 array (region row, row, region column, column) summed over the rows 171⍝# and columns inside each region: robots, wrecks, and where you are. 172ˡl̲rs ← { s → 173 robots ← (ˡc̲ell ˡr̲obots s) ˡo̲nSquares ˡa̲live s 174 me ← (r̲ange 256) = ˡc̲ell ˡp̲os s 175 (ʰr̲egions robots) c̲at₂ (ʰr̲egions r̲avel ˡw̲recks s) c̲at₂ ʰr̲egions me 176} 177⍝# The 256 squares as 4 by 4 regions, each summed (private). 178ʰr̲egions ← { v → '+ r̲/₂₄ 4 4 4 4 r̲eshape v }