├── .gitignore ├── LICENSE ├── README.md ├── css ├── main.css └── reset.css ├── fonts ├── LibreBaskerville-Regular.ttf └── OFL.txt ├── img ├── Cute People Icon v2.png ├── Kappa.png ├── License.txt └── tsodinPog.png ├── index.html ├── js ├── index.js ├── index.js.map ├── stb_image.js └── stb_image.js.map ├── nbors.js ├── package-lock.json ├── package.json ├── ts ├── index.ts └── stb_image.ts ├── tsconfig.json └── wasm └── stb_image.wasm /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cellular Automata 2 | 3 | This is a small experiment with Cellular Automata. The idea is to generalize 4 | [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) 5 | style Cellular Automaton as a Transition Table and play with procedurally 6 | generated Transition Tables. 7 | 8 | ## Quick Start 9 | 10 | ```console 11 | $ npm install 12 | $ ./node_modules/.bin/tsc 13 | $ iexplore.exe index.html 14 | ``` 15 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'LibreBaskerville'; 3 | src: url('../fonts/LibreBaskerville-Regular.ttf') format('truetype'); 4 | } 5 | 6 | body { 7 | background: #181818; 8 | color: #F0F0F0; 9 | font-family: LibreBaskerville; 10 | margin: auto; 11 | max-width: 960px; 12 | font-size: 16px; 13 | } 14 | 15 | h1 { 16 | font-size: 40px; 17 | text-align: center; 18 | padding-top: 40px; 19 | padding-bottom: 40px; 20 | } 21 | 22 | .canvas-with-controls { 23 | display: flex; 24 | } 25 | 26 | .controls { 27 | padding-left: 20px; 28 | } 29 | 30 | fieldset { 31 | padding-bottom: 20px; 32 | } 33 | 34 | legend { 35 | padding-bottom: 10px; 36 | } 37 | -------------------------------------------------------------------------------- /css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /fonts/LibreBaskerville-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/autocell/e8d801a250ff72dcb05dc4a35b14af74f2913421/fonts/LibreBaskerville-Regular.ttf -------------------------------------------------------------------------------- /fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Pablo Impallari (www.impallari.com|impallari@gmail.com), 2 | Copyright (c) 2012, Rodrigo Fuenzalida (www.rfuenzalida.com|hello@rfuenzalida.com), with Reserved Font Name Libre Baskerville. 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /img/Cute People Icon v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/autocell/e8d801a250ff72dcb05dc4a35b14af74f2913421/img/Cute People Icon v2.png -------------------------------------------------------------------------------- /img/Kappa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/autocell/e8d801a250ff72dcb05dc4a35b14af74f2913421/img/Kappa.png -------------------------------------------------------------------------------- /img/License.txt: -------------------------------------------------------------------------------- 1 | Cute People Icon by Arbiyan (darkagent) 2 | This zip containt original and large size picture in .png format 3 | and including file project using JPixel software. 4 | All asset using DawnBringer 16 Palette 5 | 6 | ------------------------------ 7 | 8 | License (CC-BY 3.0) 9 | 10 | You may use these graphics in personal and commercial projects. 11 | Credit Arbiyan (darkagent). -------------------------------------------------------------------------------- /img/tsodinPog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/autocell/e8d801a250ff72dcb05dc4a35b14af74f2913421/img/tsodinPog.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cellular Automata 4 | 5 | 6 | 7 | 8 |

Cellular Automata

9 |
10 | 11 |
12 |
13 | 14 | 15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | class Board { 12 | constructor(width, height, cell = 0) { 13 | this.width = width; 14 | this.height = height; 15 | this.cells = Array(width * height).fill(cell); 16 | } 17 | get(x, y) { 18 | return this.cells[y * this.width + x]; 19 | } 20 | set(x, y, cell) { 21 | return this.cells[y * this.width + x] = cell; 22 | } 23 | } 24 | function mod(a, b) { 25 | return (a % b + b) % b; 26 | } 27 | function countNbors(board, states, x0, y0) { 28 | const nbors = Array(states).fill(0); 29 | for (let dy = -1; dy <= 1; ++dy) { 30 | for (let dx = -1; dx <= 1; ++dx) { 31 | if (dy != 0 || dx != 0) { 32 | const y = mod(y0 + dy, board.height); 33 | const x = mod(x0 + dx, board.width); 34 | nbors[board.get(x, y)]++; 35 | } 36 | } 37 | } 38 | return nbors.join(""); 39 | } 40 | const Seeds = [ 41 | { 42 | "transitions": { 43 | "62": 1, 44 | }, 45 | "default": 0, 46 | "color": "#202020", 47 | }, 48 | { 49 | "transitions": {}, 50 | "default": 0, 51 | "color": "#FF5050", 52 | }, 53 | ]; 54 | const GoL = [ 55 | { 56 | "transitions": { 57 | "53": 1, 58 | }, 59 | "default": 0, 60 | "color": "#202020", 61 | }, 62 | { 63 | "transitions": { 64 | "62": 1, 65 | "53": 1, 66 | }, 67 | "default": 0, 68 | "color": "#FF5050", 69 | }, 70 | ]; 71 | const BB = [ 72 | // 0 - Dead 73 | { 74 | "transitions": { 75 | "026": 1, 76 | "125": 1, 77 | "224": 1, 78 | "323": 1, 79 | "422": 1, 80 | "521": 1, 81 | "620": 1, 82 | }, 83 | "default": 0, 84 | "color": "#202020", 85 | }, 86 | // 1 - Live 87 | { 88 | "transitions": {}, 89 | "default": 2, 90 | "color": "#FF5050", 91 | }, 92 | // 2 - Dying 93 | { 94 | "transitions": {}, 95 | "default": 0, 96 | "color": "#50FF50", 97 | } 98 | ]; 99 | function computeNextBoard(automaton, current, next) { 100 | console.assert(current.width == next.width); 101 | console.assert(current.height == next.height); 102 | for (let y = 0; y < current.height; ++y) { 103 | for (let x = 0; x < current.width; ++x) { 104 | const nbors = countNbors(current, automaton.length, x, y); 105 | const state = automaton[current.get(x, y)]; 106 | next.set(x, y, state.transitions[nbors]); 107 | if (next.get(x, y) === undefined) { 108 | next.set(x, y, state.default); 109 | } 110 | } 111 | } 112 | } 113 | function render(ctx, automaton, board) { 114 | const CELL_WIDTH = ctx.canvas.width / board.width; 115 | const CELL_HEIGHT = ctx.canvas.height / board.height; 116 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 117 | for (let y = 0; y < board.height; ++y) { 118 | for (let x = 0; x < board.width; ++x) { 119 | const rx = x * CELL_WIDTH; 120 | const ry = y * CELL_HEIGHT; 121 | ctx.fillStyle = automaton[board.get(x, y)].color; 122 | ctx.fillRect(rx, ry, CELL_WIDTH, CELL_HEIGHT); 123 | } 124 | } 125 | } 126 | function bytesAsHexString(bytes) { 127 | let result = ""; 128 | for (let i = 0; i < bytes.length; ++i) { 129 | result += bytes[i].toString(16).padStart(2, '0').toUpperCase(); 130 | } 131 | return result; 132 | } 133 | function transcendentalApprehensionOfImage(image, rx, ry, rw, rh) { 134 | const board = new Board(rw, rh); 135 | let count = 0; 136 | const colorToState = {}; 137 | for (let y = ry; y < ry + rh; ++y) { 138 | for (let x = rx; x < rx + rw; ++x) { 139 | const pixel = new Uint8Array(image.data.buffer, (y * image.width + x) * 4, 4); 140 | const color = bytesAsHexString(pixel); 141 | if (colorToState[color] === undefined) { 142 | colorToState[color] = count++; 143 | } 144 | board.set(x - rx, y - ry, colorToState[color]); 145 | } 146 | } 147 | const automaton = Object.keys(colorToState).map((color) => { 148 | return { 149 | "color": `#${color}`, 150 | "default": 0, 151 | "transitions": {}, 152 | }; 153 | }); 154 | return [board, automaton]; 155 | } 156 | function getElementByIdOrError(id) { 157 | const element = document.getElementById(id); 158 | if (element === null) { 159 | throw new Error(`Could not find element ${id}`); 160 | } 161 | return element; 162 | } 163 | function meltdown(board, automaton) { 164 | for (let y = 0; y < board.height; ++y) { 165 | for (let x = 0; x < board.width; ++x) { 166 | const nbors = countNbors(board, automaton.length, x, y); 167 | const state = board.get(x, y); 168 | for (let i = 0; i < automaton.length; ++i) { 169 | if (automaton[i].transitions[nbors] === undefined) { 170 | automaton[i].transitions[nbors] = state; 171 | } 172 | } 173 | } 174 | } 175 | } 176 | function cycleRules(board, automaton) { 177 | const nborStates = {}; 178 | for (let y = 0; y < board.height; ++y) { 179 | for (let x = 0; x < board.width; ++x) { 180 | const nbors = countNbors(board, automaton.length, x, y); 181 | if (nborStates[nbors] === undefined) { 182 | nborStates[nbors] = new Set(); 183 | } 184 | nborStates[nbors].add(board.get(x, y)); 185 | } 186 | } 187 | for (let nbor of Object.keys(nborStates)) { 188 | const states = Array.from(nborStates[nbor]); 189 | for (let i = 0; i < states.length; ++i) { 190 | const state1 = states[i]; 191 | const state2 = states[(i + 1) % states.length]; 192 | automaton[state1].transitions[nbor] = state2; 193 | } 194 | } 195 | } 196 | function allNborsFreqs(state, xs = [], result = []) { 197 | const s = xs.reduce((a, b) => a + b, 0); 198 | if (state <= 0) { 199 | if (s === 8) { 200 | result.push([xs.join(""), xs.indexOf(Math.max(...xs))]); 201 | } 202 | return result; 203 | } 204 | for (let x = 0; x <= 8 - s; ++x) { 205 | xs.push(x); 206 | allNborsFreqs(state - 1, xs, result); 207 | xs.pop(); 208 | } 209 | return result; 210 | } 211 | function freqRules(automaton) { 212 | const nborsFreqs = allNborsFreqs(automaton.length); 213 | for (let i = 0; i < automaton.length; ++i) { 214 | for (let [nbor, freq] of nborsFreqs) { 215 | automaton[i].transitions[nbor] = freq; 216 | } 217 | } 218 | } 219 | window.onload = () => __awaiter(void 0, void 0, void 0, function* () { 220 | const imgPath = "img/Cute People Icon v2.png"; 221 | // const imgPath = "img/Kappa.png"; 222 | // const imgPath = "img/tsodinPog.png"; 223 | const cute = yield stbi_load_from_url(imgPath); 224 | const [cuteBoard, cuteAutomaton] = transcendentalApprehensionOfImage(cute, 0, 0, cute.width, cute.height); 225 | meltdown(cuteBoard, cuteAutomaton); 226 | // cycleRules(cuteBoard, cuteAutomaton); 227 | // freqRules(cuteAutomaton); 228 | const palette = getElementByIdOrError("palette"); 229 | palette.width = 150; 230 | const paletteCtx = palette.getContext("2d"); 231 | if (paletteCtx === null) { 232 | throw new Error(`Could not initialize 2d context`); 233 | } 234 | let currentState = 1; 235 | let hoveredState = null; 236 | const PALETTE_COLS = 6; 237 | const PALETTE_SIZE = palette.width / PALETTE_COLS; 238 | palette.height = Math.ceil(cuteAutomaton.length / PALETTE_COLS) * PALETTE_SIZE; 239 | const redrawPalette = () => { 240 | paletteCtx.clearRect(0, 0, palette.width, palette.height); 241 | for (let i = 0; i < cuteAutomaton.length; ++i) { 242 | const y = Math.floor(i / PALETTE_COLS); 243 | const x = i % PALETTE_COLS; 244 | paletteCtx.fillStyle = cuteAutomaton[i].color; 245 | paletteCtx.fillRect(x * PALETTE_SIZE, y * PALETTE_SIZE, PALETTE_SIZE, PALETTE_SIZE); 246 | const thicc = 3; 247 | if (i == currentState) { 248 | paletteCtx.strokeStyle = "white"; 249 | paletteCtx.lineWidth = thicc; 250 | paletteCtx.strokeRect(x * PALETTE_SIZE + thicc / 2, y * PALETTE_SIZE + thicc / 2, PALETTE_SIZE - thicc, PALETTE_SIZE - thicc); 251 | } 252 | else if (i == hoveredState) { 253 | paletteCtx.strokeStyle = "gray"; 254 | paletteCtx.lineWidth = thicc; 255 | paletteCtx.strokeRect(x * PALETTE_SIZE + thicc / 2, y * PALETTE_SIZE + thicc / 2, PALETTE_SIZE - thicc, PALETTE_SIZE - thicc); 256 | } 257 | } 258 | }; 259 | palette.addEventListener("mousemove", (e) => { 260 | const x = Math.floor(e.offsetX / PALETTE_SIZE); 261 | const y = Math.floor(e.offsetY / PALETTE_SIZE); 262 | const state = y * PALETTE_COLS + x; 263 | if (state < cuteAutomaton.length) { 264 | hoveredState = state; 265 | } 266 | else { 267 | hoveredState = null; 268 | } 269 | redrawPalette(); 270 | }); 271 | palette.addEventListener("click", (e) => { 272 | const x = Math.floor(e.offsetX / PALETTE_SIZE); 273 | const y = Math.floor(e.offsetY / PALETTE_SIZE); 274 | const state = y * PALETTE_COLS + x; 275 | if (state < cuteAutomaton.length) { 276 | currentState = state; 277 | redrawPalette(); 278 | } 279 | }); 280 | redrawPalette(); 281 | const app = getElementByIdOrError("app"); 282 | app.width = 800; 283 | const ctx = app.getContext("2d"); 284 | if (ctx === null) { 285 | throw new Error(`Could not initialize 2d context`); 286 | } 287 | const next = getElementByIdOrError("next"); 288 | const play = getElementByIdOrError("play"); 289 | let currentAutomaton = cuteAutomaton; 290 | let currentBoard = cuteBoard; 291 | let nextBoard = new Board(currentBoard.width, currentBoard.height); 292 | app.height = app.width * (currentBoard.height / currentBoard.width); 293 | app.addEventListener("mousemove", (e) => { 294 | if (e.buttons & 1) { 295 | const CELL_WIDTH = app.width / currentBoard.width; 296 | const CELL_HEIGHT = app.height / currentBoard.height; 297 | const x = Math.floor(e.offsetX / CELL_WIDTH); 298 | const y = Math.floor(e.offsetY / CELL_HEIGHT); 299 | currentBoard.set(x, y, currentState); 300 | render(ctx, currentAutomaton, currentBoard); 301 | } 302 | }); 303 | app.addEventListener("mousedown", (e) => { 304 | const CELL_WIDTH = app.width / currentBoard.width; 305 | const CELL_HEIGHT = app.height / currentBoard.height; 306 | const x = Math.floor(e.offsetX / CELL_WIDTH); 307 | const y = Math.floor(e.offsetY / CELL_HEIGHT); 308 | currentBoard.set(x, y, currentState); 309 | render(ctx, currentAutomaton, currentBoard); 310 | }); 311 | const nextState = () => { 312 | computeNextBoard(currentAutomaton, currentBoard, nextBoard); 313 | [currentBoard, nextBoard] = [nextBoard, currentBoard]; 314 | render(ctx, currentAutomaton, currentBoard); 315 | }; 316 | next.addEventListener("click", nextState); 317 | const PLAY_PERIOD = 100; 318 | let playInterval = null; //setInterval(nextState, PLAY_PERIOD); 319 | play.addEventListener("click", () => { 320 | if (playInterval === null) { 321 | playInterval = setInterval(nextState, PLAY_PERIOD); 322 | } 323 | else { 324 | clearInterval(playInterval); 325 | playInterval = null; 326 | } 327 | play.innerText = playInterval === null ? "Play" : "Pause"; 328 | }); 329 | play.innerText = playInterval === null ? "Play" : "Pause"; 330 | render(ctx, currentAutomaton, currentBoard); 331 | }); 332 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /js/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /js/stb_image.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | const STB_IMAGE_WASM_PATH = "wasm/stb_image.wasm"; 12 | const stb_image_raw = WebAssembly.instantiateStreaming(fetch(STB_IMAGE_WASM_PATH), { 13 | env: {}, 14 | }).then((w) => { 15 | const memory = w.instance.exports.memory; 16 | // TODO: grow the memory automatically as needed 17 | memory.grow(10); 18 | return { 19 | "memory": memory, 20 | "stbi_load_from_memory": w.instance.exports.stbi_load_from_memory, 21 | "malloc": w.instance.exports.malloc, 22 | "heap_reset": w.instance.exports.heap_reset, 23 | }; 24 | }); 25 | function stbi_load_from_arraybuffer(arrayBuffer) { 26 | return __awaiter(this, void 0, void 0, function* () { 27 | const buffer = new Uint8Array(yield arrayBuffer); 28 | const stb_image = yield stb_image_raw; 29 | // TODO: maybe we should expose all of this memory management to the user so we don't have to do the copy below 30 | stb_image.heap_reset(); 31 | const len = buffer.length; 32 | const buf = stb_image.malloc(len); 33 | new Uint8Array(stb_image.memory.buffer, buf, len).set(buffer); 34 | const x = stb_image.malloc(4); 35 | const y = stb_image.malloc(4); 36 | const pixels = stb_image.stbi_load_from_memory(buf, len, x, y, 0, 4); 37 | const w = new Uint32Array(stb_image.memory.buffer, x, 1)[0]; 38 | const h = new Uint32Array(stb_image.memory.buffer, y, 1)[0]; 39 | const imageData = new Uint8ClampedArray(w * h * 4); 40 | // Copying the image data cause the next call to stb_image.heap_reset() above will erase it. 41 | imageData.set(new Uint8ClampedArray(stb_image.memory.buffer, pixels, w * h * 4)); 42 | return new ImageData(imageData, w); 43 | }); 44 | } 45 | function stbi_load_from_url(url) { 46 | return __awaiter(this, void 0, void 0, function* () { 47 | const response = yield fetch(url); 48 | return stbi_load_from_arraybuffer(response.arrayBuffer()); 49 | }); 50 | } 51 | //# sourceMappingURL=stb_image.js.map -------------------------------------------------------------------------------- /js/stb_image.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"stb_image.js","sourceRoot":"","sources":["../ts/stb_image.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAelD,MAAM,aAAa,GAA2B,WAAW,CAAC,oBAAoB,CAAC,KAAK,CAAC,mBAAmB,CAAC,EAAE;IACvG,GAAG,EAAE,EAAE;CACV,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;IACV,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAA4B,CAAC;IAC/D,gDAAgD;IAChD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO;QACH,QAAQ,EAAE,MAAM;QAChB,uBAAuB,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,qBAAmD;QAC/F,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAqB;QAClD,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,UAA6B;KACjE,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,SAAe,0BAA0B,CAAC,WAA+C;;QACrF,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,WAAW,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC;QACtC,+GAA+G;QAC/G,SAAS,CAAC,UAAU,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1B,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,CAAC,CAAC;QAC/C,4FAA4F;QAC5F,SAAS,CAAC,GAAG,CAAC,IAAI,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAC,CAAC,GAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,OAAO,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC;CAAA;AAED,SAAe,kBAAkB,CAAC,GAAgB;;QAC9C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,0BAA0B,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9D,CAAC;CAAA"} -------------------------------------------------------------------------------- /nbors.js: -------------------------------------------------------------------------------- 1 | // 1, 9, 45, 165 2 | // 1: 1 3 | // 2: 9 4 | // 3: 45 5 | // 4: 165 6 | // 5: 495 7 | 8 | // automaton[state].transitions[nbor] = state1 9 | 10 | function allNborsFreqs(state, xs = [], result = []) { 11 | const s = xs.reduce((a, b) => a + b, 0); 12 | if (state <= 0) { 13 | if (s === 8) { 14 | result.push([xs.join(""), xs.indexOf(Math.max(...xs))]); 15 | } 16 | return result; 17 | } 18 | for (let x = 0; x <= 8 - s; ++x) { 19 | xs.push(x); 20 | allNbors(state - 1, xs, result); 21 | xs.pop(); 22 | } 23 | return result; 24 | } 25 | 26 | let result = allNbors(10); 27 | console.log(result); 28 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autocell", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "autocell", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "typescript": "^5.0.4" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.0.4", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", 18 | "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", 19 | "dev": true, 20 | "bin": { 21 | "tsc": "bin/tsc", 22 | "tsserver": "bin/tsserver" 23 | }, 24 | "engines": { 25 | "node": ">=12.20" 26 | } 27 | } 28 | }, 29 | "dependencies": { 30 | "typescript": { 31 | "version": "5.0.4", 32 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", 33 | "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", 34 | "dev": true 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autocell", 3 | "version": "1.0.0", 4 | "description": "Cellular Automata Experiment", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Alexey Kutepov ", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "typescript": "^5.0.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ts/index.ts: -------------------------------------------------------------------------------- 1 | type Cell = number; 2 | 3 | class Board { 4 | public width: number; 5 | public height: number; 6 | public cells: Cell[]; 7 | 8 | constructor(width: number, height: number, cell: number = 0) { 9 | this.width = width; 10 | this.height = height; 11 | this.cells = Array(width*height).fill(cell); 12 | } 13 | 14 | get(x: number, y: number): Cell { 15 | return this.cells[y*this.width + x]; 16 | } 17 | 18 | set(x: number, y: number, cell: Cell) { 19 | return this.cells[y*this.width + x] = cell; 20 | } 21 | } 22 | 23 | function mod(a: number, b: number): number { 24 | return (a%b + b)%b 25 | } 26 | 27 | type Nbors = string; 28 | 29 | function countNbors(board: Board, states: number, x0: number, y0: number): Nbors { 30 | const nbors = Array(states).fill(0); 31 | for (let dy = -1; dy <= 1; ++dy) { 32 | for (let dx = -1; dx <= 1; ++dx) { 33 | if (dy != 0 || dx != 0) { 34 | const y = mod(y0 + dy, board.height); 35 | const x = mod(x0 + dx, board.width); 36 | nbors[board.get(x, y)]++; 37 | } 38 | } 39 | } 40 | return nbors.join(""); 41 | } 42 | 43 | interface State { 44 | "color": string; 45 | "default": number; 46 | "transitions": { 47 | [key: string]: number; 48 | } 49 | } 50 | 51 | type Automaton = State[]; 52 | 53 | const Seeds: Automaton = [ 54 | { 55 | "transitions": { 56 | "62": 1, 57 | }, 58 | "default": 0, 59 | "color": "#202020", 60 | }, 61 | { 62 | "transitions": {}, 63 | "default": 0, 64 | "color": "#FF5050", 65 | }, 66 | ]; 67 | 68 | const GoL: Automaton = [ 69 | { 70 | "transitions": { 71 | "53": 1, 72 | }, 73 | "default": 0, 74 | "color": "#202020", 75 | }, 76 | { 77 | "transitions": { 78 | "62": 1, 79 | "53": 1, 80 | }, 81 | "default": 0, 82 | "color": "#FF5050", 83 | }, 84 | ]; 85 | 86 | const BB: Automaton = [ 87 | // 0 - Dead 88 | { 89 | "transitions": { 90 | "026": 1, 91 | "125": 1, 92 | "224": 1, 93 | "323": 1, 94 | "422": 1, 95 | "521": 1, 96 | "620": 1, 97 | }, 98 | "default": 0, 99 | "color": "#202020", 100 | }, 101 | // 1 - Live 102 | { 103 | "transitions": {}, 104 | "default": 2, 105 | "color": "#FF5050", 106 | }, 107 | // 2 - Dying 108 | { 109 | "transitions": {}, 110 | "default": 0, 111 | "color": "#50FF50", 112 | } 113 | ]; 114 | 115 | function computeNextBoard(automaton: Automaton, current: Board, next: Board) { 116 | console.assert(current.width == next.width); 117 | console.assert(current.height == next.height); 118 | for (let y = 0; y < current.height; ++y) { 119 | for (let x = 0; x < current.width; ++x) { 120 | const nbors = countNbors(current, automaton.length, x, y); 121 | const state = automaton[current.get(x, y)]; 122 | next.set(x, y, state.transitions[nbors]); 123 | if (next.get(x, y) === undefined) { 124 | next.set(x, y, state.default); 125 | } 126 | } 127 | } 128 | } 129 | 130 | function render(ctx: CanvasRenderingContext2D, automaton: Automaton, board: Board) { 131 | const CELL_WIDTH = ctx.canvas.width/board.width; 132 | const CELL_HEIGHT = ctx.canvas.height/board.height; 133 | 134 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 135 | for (let y = 0; y < board.height; ++y) { 136 | for (let x = 0; x < board.width; ++x) { 137 | const rx = x*CELL_WIDTH; 138 | const ry = y*CELL_HEIGHT; 139 | ctx.fillStyle = automaton[board.get(x, y)].color; 140 | ctx.fillRect(rx, ry, CELL_WIDTH, CELL_HEIGHT); 141 | } 142 | } 143 | } 144 | 145 | function bytesAsHexString(bytes: Uint8Array): string { 146 | let result = ""; 147 | for (let i = 0; i < bytes.length; ++i) { 148 | result += bytes[i].toString(16).padStart(2, '0').toUpperCase(); 149 | } 150 | return result; 151 | } 152 | 153 | function transcendentalApprehensionOfImage(image: ImageData, rx: number, ry: number, rw: number, rh: number): [Board, Automaton] { 154 | const board = new Board(rw, rh); 155 | let count = 0; 156 | const colorToState: { [key: string]: number } = {}; 157 | for (let y = ry; y < ry + rh; ++y) { 158 | for (let x = rx; x < rx + rw; ++x) { 159 | const pixel = new Uint8Array(image.data.buffer, (y*image.width + x)*4, 4); 160 | const color = bytesAsHexString(pixel); 161 | if (colorToState[color] === undefined) { 162 | colorToState[color] = count++; 163 | } 164 | 165 | board.set(x - rx, y - ry, colorToState[color]); 166 | } 167 | } 168 | 169 | const automaton = Object.keys(colorToState).map((color) => { 170 | return { 171 | "color": `#${color}`, 172 | "default": 0, 173 | "transitions": {}, 174 | }; 175 | }); 176 | 177 | return [board, automaton]; 178 | } 179 | 180 | function getElementByIdOrError(id: string): T { 181 | const element = document.getElementById(id) as T; 182 | if (element === null) { 183 | throw new Error(`Could not find element ${id}`); 184 | } 185 | return element; 186 | } 187 | 188 | function meltdown(board: Board, automaton: Automaton) { 189 | for (let y = 0; y < board.height; ++y) { 190 | for (let x = 0; x < board.width; ++x) { 191 | const nbors = countNbors(board, automaton.length, x, y); 192 | const state = board.get(x, y); 193 | for (let i = 0; i < automaton.length; ++i) { 194 | if (automaton[i].transitions[nbors] === undefined) { 195 | automaton[i].transitions[nbors] = state; 196 | } 197 | } 198 | } 199 | } 200 | } 201 | 202 | function cycleRules(board: Board, automaton: Automaton) { 203 | const nborStates: { [key: string]: Set } = {}; 204 | for (let y = 0; y < board.height; ++y) { 205 | for (let x = 0; x < board.width; ++x) { 206 | const nbors = countNbors(board, automaton.length, x, y); 207 | if (nborStates[nbors] === undefined) { 208 | nborStates[nbors] = new Set(); 209 | } 210 | nborStates[nbors].add(board.get(x, y)); 211 | } 212 | } 213 | 214 | for (let nbor of Object.keys(nborStates)) { 215 | const states = Array.from(nborStates[nbor]); 216 | for (let i = 0; i < states.length; ++i) { 217 | const state1 = states[i]; 218 | const state2 = states[(i+1)%states.length]; 219 | automaton[state1].transitions[nbor] = state2; 220 | } 221 | } 222 | } 223 | 224 | function allNborsFreqs(state: number, xs: number[] = [], result: [string, number][] = []): [string, number][] { 225 | const s = xs.reduce((a, b) => a + b, 0); 226 | if (state <= 0) { 227 | if (s === 8) { 228 | result.push([xs.join(""), xs.indexOf(Math.max(...xs))]); 229 | } 230 | return result; 231 | } 232 | for (let x = 0; x <= 8 - s; ++x) { 233 | xs.push(x); 234 | allNborsFreqs(state - 1, xs, result); 235 | xs.pop(); 236 | } 237 | return result; 238 | } 239 | 240 | function freqRules(automaton: Automaton) { 241 | const nborsFreqs = allNborsFreqs(automaton.length); 242 | 243 | for (let i = 0; i < automaton.length; ++i) { 244 | for (let [nbor, freq] of nborsFreqs) { 245 | automaton[i].transitions[nbor] = freq; 246 | } 247 | } 248 | } 249 | 250 | window.onload = async () => { 251 | const imgPath = "img/Cute People Icon v2.png"; 252 | // const imgPath = "img/Kappa.png"; 253 | // const imgPath = "img/tsodinPog.png"; 254 | const cute = await stbi_load_from_url(imgPath); 255 | const [cuteBoard, cuteAutomaton] = transcendentalApprehensionOfImage(cute, 0, 0, cute.width, cute.height); 256 | 257 | meltdown(cuteBoard, cuteAutomaton); 258 | // cycleRules(cuteBoard, cuteAutomaton); 259 | // freqRules(cuteAutomaton); 260 | 261 | const palette = getElementByIdOrError("palette"); 262 | palette.width = 150; 263 | const paletteCtx = palette.getContext("2d"); 264 | if (paletteCtx === null) { 265 | throw new Error(`Could not initialize 2d context`); 266 | } 267 | 268 | let currentState = 1; 269 | let hoveredState: number | null = null; 270 | const PALETTE_COLS = 6; 271 | const PALETTE_SIZE = palette.width/PALETTE_COLS; 272 | 273 | palette.height = Math.ceil(cuteAutomaton.length/PALETTE_COLS)*PALETTE_SIZE; 274 | 275 | const redrawPalette = () => { 276 | paletteCtx.clearRect(0, 0, palette.width, palette.height); 277 | for (let i = 0; i < cuteAutomaton.length; ++i) { 278 | const y = Math.floor(i/PALETTE_COLS); 279 | const x = i%PALETTE_COLS; 280 | paletteCtx.fillStyle = cuteAutomaton[i].color; 281 | paletteCtx.fillRect(x*PALETTE_SIZE, y*PALETTE_SIZE, PALETTE_SIZE, PALETTE_SIZE); 282 | const thicc = 3; 283 | if (i == currentState) { 284 | paletteCtx.strokeStyle = "white"; 285 | paletteCtx.lineWidth = thicc; 286 | paletteCtx.strokeRect(x*PALETTE_SIZE + thicc/2, y*PALETTE_SIZE + thicc/2, PALETTE_SIZE - thicc, PALETTE_SIZE - thicc); 287 | } else if (i == hoveredState) { 288 | paletteCtx.strokeStyle = "gray"; 289 | paletteCtx.lineWidth = thicc; 290 | paletteCtx.strokeRect(x*PALETTE_SIZE + thicc/2, y*PALETTE_SIZE + thicc/2, PALETTE_SIZE - thicc, PALETTE_SIZE - thicc); 291 | } 292 | } 293 | }; 294 | 295 | palette.addEventListener("mousemove", (e) => { 296 | const x = Math.floor(e.offsetX/PALETTE_SIZE); 297 | const y = Math.floor(e.offsetY/PALETTE_SIZE); 298 | const state = y*PALETTE_COLS + x; 299 | 300 | if (state < cuteAutomaton.length) { 301 | hoveredState = state; 302 | } else { 303 | hoveredState = null; 304 | } 305 | redrawPalette(); 306 | }); 307 | 308 | palette.addEventListener("click", (e) => { 309 | const x = Math.floor(e.offsetX/PALETTE_SIZE); 310 | const y = Math.floor(e.offsetY/PALETTE_SIZE); 311 | const state = y*PALETTE_COLS + x; 312 | 313 | if (state < cuteAutomaton.length) { 314 | currentState = state; 315 | redrawPalette(); 316 | } 317 | }); 318 | 319 | redrawPalette(); 320 | 321 | const app = getElementByIdOrError("app"); 322 | app.width = 800; 323 | const ctx = app.getContext("2d"); 324 | if (ctx === null) { 325 | throw new Error(`Could not initialize 2d context`); 326 | } 327 | 328 | const next = getElementByIdOrError("next"); 329 | const play = getElementByIdOrError("play"); 330 | 331 | let currentAutomaton = cuteAutomaton; 332 | let currentBoard: Board = cuteBoard; 333 | let nextBoard: Board = new Board(currentBoard.width, currentBoard.height); 334 | 335 | app.height = app.width*(currentBoard.height/currentBoard.width); 336 | 337 | app.addEventListener("mousemove", (e) => { 338 | if (e.buttons&1) { 339 | const CELL_WIDTH = app.width/currentBoard.width; 340 | const CELL_HEIGHT = app.height/currentBoard.height; 341 | 342 | const x = Math.floor(e.offsetX/CELL_WIDTH); 343 | const y = Math.floor(e.offsetY/CELL_HEIGHT); 344 | 345 | currentBoard.set(x, y, currentState); 346 | render(ctx, currentAutomaton, currentBoard); 347 | } 348 | }); 349 | 350 | app.addEventListener("mousedown", (e) => { 351 | const CELL_WIDTH = app.width/currentBoard.width; 352 | const CELL_HEIGHT = app.height/currentBoard.height; 353 | 354 | const x = Math.floor(e.offsetX/CELL_WIDTH); 355 | const y = Math.floor(e.offsetY/CELL_HEIGHT); 356 | 357 | currentBoard.set(x, y, currentState); 358 | render(ctx, currentAutomaton, currentBoard); 359 | }); 360 | 361 | const nextState = () => { 362 | computeNextBoard(currentAutomaton, currentBoard, nextBoard); 363 | [currentBoard, nextBoard] = [nextBoard, currentBoard]; 364 | render(ctx, currentAutomaton, currentBoard); 365 | }; 366 | 367 | next.addEventListener("click", nextState); 368 | 369 | const PLAY_PERIOD = 100; 370 | let playInterval: number | null = null;//setInterval(nextState, PLAY_PERIOD); 371 | 372 | play.addEventListener("click", () => { 373 | if (playInterval === null) { 374 | playInterval = setInterval(nextState, PLAY_PERIOD); 375 | } else { 376 | clearInterval(playInterval); 377 | playInterval = null; 378 | } 379 | play.innerText = playInterval === null ? "Play" : "Pause"; 380 | }); 381 | play.innerText = playInterval === null ? "Play" : "Pause"; 382 | 383 | render(ctx, currentAutomaton, currentBoard); 384 | }; 385 | -------------------------------------------------------------------------------- /ts/stb_image.ts: -------------------------------------------------------------------------------- 1 | const STB_IMAGE_WASM_PATH = "wasm/stb_image.wasm"; 2 | 3 | type pointer = number; 4 | 5 | type stbi_load_from_memory_type = (buf: pointer, len: number, x: number, y: number, channels_in_file: pointer, desired_channels: number) => pointer; 6 | type malloc_type = (s: number) => pointer; 7 | type heap_reset_type = () => void; 8 | 9 | interface Stb_Image_Raw { 10 | stbi_load_from_memory: stbi_load_from_memory_type; 11 | malloc: malloc_type; 12 | heap_reset: heap_reset_type; 13 | memory: WebAssembly.Memory; 14 | } 15 | 16 | const stb_image_raw: Promise = WebAssembly.instantiateStreaming(fetch(STB_IMAGE_WASM_PATH), { 17 | env: {}, 18 | }).then((w) => { 19 | const memory = w.instance.exports.memory as WebAssembly.Memory; 20 | // TODO: grow the memory automatically as needed 21 | memory.grow(10); 22 | return { 23 | "memory": memory, 24 | "stbi_load_from_memory": w.instance.exports.stbi_load_from_memory as stbi_load_from_memory_type, 25 | "malloc": w.instance.exports.malloc as malloc_type, 26 | "heap_reset": w.instance.exports.heap_reset as heap_reset_type, 27 | }; 28 | }); 29 | 30 | async function stbi_load_from_arraybuffer(arrayBuffer: ArrayBuffer | Promise): Promise { 31 | const buffer = new Uint8Array(await arrayBuffer); 32 | const stb_image = await stb_image_raw; 33 | // TODO: maybe we should expose all of this memory management to the user so we don't have to do the copy below 34 | stb_image.heap_reset(); 35 | const len = buffer.length; 36 | const buf = stb_image.malloc(len); 37 | new Uint8Array(stb_image.memory.buffer, buf, len).set(buffer); 38 | const x = stb_image.malloc(4); 39 | const y = stb_image.malloc(4); 40 | const pixels = stb_image.stbi_load_from_memory(buf, len, x, y, 0, 4); 41 | const w = new Uint32Array(stb_image.memory.buffer, x, 1)[0]; 42 | const h = new Uint32Array(stb_image.memory.buffer, y, 1)[0]; 43 | const imageData = new Uint8ClampedArray(w*h*4); 44 | // Copying the image data cause the next call to stb_image.heap_reset() above will erase it. 45 | imageData.set(new Uint8ClampedArray(stb_image.memory.buffer, pixels, w*h*4)); 46 | return new ImageData(imageData, w); 47 | } 48 | 49 | async function stbi_load_from_url(url: RequestInfo): Promise { 50 | const response = await fetch(url); 51 | return stbi_load_from_arraybuffer(response.arrayBuffer()); 52 | } 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["es2017", "dom"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | "outDir": "./js/", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /wasm/stb_image.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/autocell/e8d801a250ff72dcb05dc4a35b14af74f2913421/wasm/stb_image.wasm --------------------------------------------------------------------------------