Examples

Game of Life

About

Conway's Game of Life.

quillo run life.qwe --watch then open http://localhost:8080

The board is a list of rows of 0s and 1s in a component's state, and next makes a generation from the one before: a plain function, which runs the same on the server and in the browser. While the game runs, the render asks for a timer with every(1000 / speed, step); pausing stops asking, which stops it. Click a cell to flip it.

Run it

$ quillo run life.qwe --watch

Source

life.qwe
let W = 48let H = 32// Patterns, as rows of text: `#` is a live cell.let patterns = [    { name: "Glider gun", top: 4, left: 4, rows: [        "........................#...........",        "......................#.#...........",        "............##......##............##",        "...........#...#....##............##",        "##........#.....#...##..............",        "##........#...#.##....#.#...........",        "..........#.....#.......#...........",        "...........#...#....................",        "............##......................",    ] },    { name: "Gliders", top: 2, left: 2, rows: [        ".#.......#.......#.......#",        "..#.......#.......#.......#",        "###.....###.....###.....###",    ] },    { name: "Pulsar", top: 9, left: 17, rows: [        "..###...###..",        ".............",        "#....#.#....#",        "#....#.#....#",        "#....#.#....#",        "..###...###..",        ".............",        "..###...###..",        "#....#.#....#",        "#....#.#....#",        "#....#.#....#",        ".............",        "..###...###..",    ] },    { name: "R-pentomino", top: 14, left: 22, rows: [        ".##",        "##.",        ".#.",    ] },]fn empty() = range(0, H) | map((_) => range(0, W) | map((_) => 0))// The pattern's live cells, the rest dead.fn board_of(p: map) =    range(0, H) | map((y) => range(0, W) | map((x) => {        let row = p.rows[y - p.top]        let inside = y >= p.top && y - p.top < len(p.rows) && x >= p.left && x - p.left < len(row ?? "")        inside && row[x - p.left] == "#" ? 1 : 0    }))fn randomized() = range(0, H) | map((_) => range(0, W) | map((_) => random() < 0.25 ? 1 : 0))// Cells off one edge come back on the other.fn cell(board: list, y: int, x: int) = board[(y + H) % H][(x + W) % W]fn next(board: list) =    range(0, H) | map((y) => range(0, W) | map((x) => {        let n = cell(board, y - 1, x - 1) + cell(board, y - 1, x) + cell(board, y - 1, x + 1) +            cell(board, y, x - 1) + cell(board, y, x + 1) +            cell(board, y + 1, x - 1) + cell(board, y + 1, x) + cell(board, y + 1, x + 1)        n == 3 || (n == 2 && board[y][x] == 1) ? 1 : 0    }))fn flip(board: list, y: int, x: int) =    range(0, H) | map((r) => r != y ? board[r] : range(0, W) | map((c) => c == x ? 1 - board[r][c] : board[r][c]))component Life() {    state board = board_of(first(patterns))    state running = false    state speed = 8     // generations per second    state generation = 0    let step = () => {        board = next(board)        generation += 1    }    if running {        every(1000 / speed, step)    }    let population = board | map((row) => sum(row)) | sum    <section.life>        <div.controls>            <button.primary @click={() => { running = !running }}>{running ? "Pause" : "Play"}</button>            <button @click={step} disabled={running}>Step</button>            <label.speed>                Speed                <input type="range" min="1" max="100" value={speed}                    @input={(e) => { speed = int(e.value) ?? speed }}/>                <span.per-second>{speed}/s</span>            </label>            <span.stats>Generation <b.generation>{generation}</b> · <b.population>{population}</b> alive</span>        </div>        <div.board style={`grid-template-columns: repeat(${W}, 1fr)`}>            {for y in range(0, H) => for x in range(0, W) =>                <i class={board[y][x] == 1 && "on"} @click={() => { board = flip(board, y, x) }}></i>}        </div>        <div.controls>            {for p in patterns =>                <button key={p.name} @click={() => {                    board = board_of(p)                    generation = 0                }}>{p.name}</button>}            <button @click={() => {                board = randomized()                generation = 0            }}>Random</button>            <button @click={() => {                board = empty()                generation = 0                running = false            }}>Clear</button>        </div>    </section>}let style = css`body { font: 15px/1.4 system-ui, sans-serif; max-width: 52rem; margin: 2rem auto; padding: 0 1rem; color: #222; }.controls { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: .75rem 0; }button { font: inherit; padding: .3rem .8rem; border-radius: 6px; border: 1px solid #ccc; background: white; cursor: pointer; }button.primary { background: #222; color: white; border-color: #222; min-width: 5rem; }button.active { border-color: #222; font-weight: 600; }button:disabled { opacity: .4; cursor: default; }.speed { display: inline-flex; align-items: center; gap: .5rem; color: #555; }.speed input { width: 9rem; accent-color: #1a7f37; }.per-second { min-width: 2.5rem; font-variant-numeric: tabular-nums; }.stats { margin-left: auto; color: #666; font-variant-numeric: tabular-nums; }.board { display: grid; gap: 1px; background: #e8e8e8; border: 1px solid #e8e8e8; border-radius: 4px; overflow: hidden; }.board i { aspect-ratio: 1; background: white; cursor: pointer; }.board i.on { background: #1a7f37; }@media (prefers-color-scheme: dark) {  body { background: #161616; color: #ddd; }  button { background: #222; color: #ddd; border-color: #444; }  button.primary { background: #ddd; color: #111; }  .board { background: #2a2a2a; border-color: #2a2a2a; } .board i { background: #161616; } .board i.on { background: #3fb950; }}`route GET / =    <html>        <head>            <meta charset="utf-8"/>            <meta name="viewport" content="width=device-width, initial-scale=1"/>            <title>Game of Life</title>            <style>{style}</style>        </head>        <body>            <h1>Game of Life</h1>            <Life/>        </body>    </html>

See life.qwe on GitHub

Thread
10Useful? Like it, or add to its thread below.

Thread 0 posts

On Game of Life: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.