sourceextensions/web/demos/life.xtl
1⍝# A live page: Conway's Life kept by an X_eTaL program and drawn as SVG,
2⍝# one generation per request. The program is the server's request loop.
3⍝# Run: just demo web life, then open http://127.0.0.1:8470/
4⍝# (New board, Glider; Quit ends the program)
5
6ʷᵇ⁼u̲se< "Web"
7
8⍝# Life (as in X_eTaL's demos/life.xtl): every cell plus its neighbors
9⍝# by rotating along both axes and summing; alive next at 3, or 4 if alive
10ᵘl̲ife ← { ('+ r̲/₁₂ -1 0 1 o̲-₁₂ ⍵) { (⍺ = 3) + ⍵ × ⍺ = 4 } ⍵ }
11
12⍝# an n by n board, about one cell in four alive; or one glider
13ᵘs̲eed ← { n → (n c̲at n) r̲eshape 1 = r̲oll! (n × n) r̲eshape 4 }
14⍝# (n * n - 3 is n times n - 3: the rows below the glider)
15ᵘg̲lider ← { n → (n c̲at n) r̲eshape (n t̲ake 0 1 0) c̲at (n t̲ake 0 0 1) c̲at (n t̲ake 1 1 1) c̲at (n × n − 3) r̲eshape 0 }
16
17⍝# the size a request asks for (?n=), 8 to 200, else 48
18ᵘs̲ize ← { s →
19 0 = t̲ally s ? 48
20 8 m̲ax 200 m̲in f̲loor f̲irst n̲umbers s
21}
22
23⍝# the board as SVG: one unit square per live cell ("M col row h1v1h-1z")
24⍝# boxed strings joined into one (from "", so no live cells is "")
25ᵘj̲oin ← { s → d̲isclose '{ e̲nclose (d̲isclose ⍺) c̲at d̲isclose ⍵ } r̲/ (e̲nclose "") c̲at s }
26ᵘs̲vg ← { b →
27 n ← t̲ally b
28 w ← f̲ormat n
29 d ← ᵘj̲oin '{ "M" c̲at (f̲ormat ⍵ m̲od n) c̲at " " c̲at (f̲ormat ⍵ d̲iv n) c̲at "h1v1h-1z" } m̲ap (w̲here r̲avel b) − 1
30 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 " c̲at w c̲at " " c̲at w c̲at "' shape-rendering='crispEdges'><rect width='" c̲at w c̲at "' height='" c̲at w c̲at "' fill='#14161a'/><path fill='#e8483b' d='" c̲at d c̲at "'/></svg>"
31}
32
33⍝# the page: the board, refreshed several times a second
34page ← "<!doctype html><html><head><meta charset='utf-8'><title>Life -- X_eTaL on the web</title><style>body{background:#0d0e10;color:#ddd;font-family:system-ui,sans-serif;text-align:center}img{width:min(90vw,600px);image-rendering:pixelated;border:1px solid #333}button{margin:.5em;padding:.4em 1em}</style></head><body><h1>Life, computed in X_eTaL</h1><p>Each frame is one request: the X_eTaL program steps the board and replies with SVG.</p><img id='b' src='/life.svg' alt='the board'><div><button onclick=nw()>New board</button><button onclick=gl()>Glider</button><button onclick=qt()>Quit</button></div><script>let on=true;function nw(){fetch('/new')}function gl(){fetch('/glider')}function qt(){on=false;fetch('/quit')}setInterval(()=>{if(on){document.getElementById('b').src='/life.svg?'+Date.now()}},150)</script></body></html>"
35
36⍝# one request: the board after answering it (an empty board: quit);
37⍝# x r_ight y is y, so a reply is sent and the board passed on
38ᵘa̲nswer ← { b →
39 0 = t̲ally b ? b
40 r ← ʷᵇn̲ext! 60
41 r m̲atch "none" ? b
42 r m̲atch "GET /" ? (200 ʷᵇr̲eply! page) r̲ight b
43 r m̲atch "GET /life.svg" ? (200 ʷᵇr̲eply! ᵘs̲vg b) r̲ight ᵘl̲ife b
44 r m̲atch "GET /new" ? (200 ʷᵇr̲eply! "a new board") r̲ight ᵘs̲eed ᵘs̲ize ʷᵇp̲aram "n"
45 r m̲atch "GET /glider" ? (200 ʷᵇr̲eply! "a glider") r̲ight ᵘg̲lider ᵘs̲ize ʷᵇp̲aram "n"
46 r m̲atch "GET /quit" ? (200 ʷᵇr̲eply! "bye") r̲ight 0 0 r̲eshape 0
47 (404 ʷᵇr̲eply! "no such page: " c̲at ʷᵇp̲ath @) r̲ight b
48}
49
50port ← ʷᵇs̲erve! 8470
51"serving http://127.0.0.1:" c̲at f̲ormat port
52last ← 100000 'ᵘa̲nswer p̲ower ᵘs̲eed 48
53port ← ʷᵇs̲top! @
54"bye"