commit b9787ae8778eb0425b43b6c9f7316413ba89b333 Author: dm1sh Date: Wed Apr 21 19:08:38 2021 +0500 First implementation diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b51ea71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +build/ \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..eca024b --- /dev/null +++ b/app.ts @@ -0,0 +1,175 @@ +type BoardT = T[][]; +type InitBoardReturnT = [HTMLTableElement, BoardT]; + +const isAlive = (val: T) => { + if (typeof val === "string") return val === "alive"; + + if (typeof val === "boolean") return val ? "alive" : "dead"; +}; + +const createBoard = (root: HTMLElement, size = 16): InitBoardReturnT => { + const boardHTML = document.createElement("table"); + const board: boolean[][] = []; + + for (let i = 0; i < size; i++) { + const boardRowHTML = document.createElement("tr"); + + board.push([]); + + for (let j = 0; j < size; j++) { + const boardCellHTML = document.createElement("td"); + + board[i].push(false); + boardCellHTML.className = "dead"; + + boardRowHTML.appendChild(boardCellHTML); + } + + boardHTML.appendChild(boardRowHTML); + } + + root.appendChild(boardHTML); + + return [boardHTML, board]; +}; + +type BoardOperationsT = { + setStatus: (x: number, y: number, val: boolean) => void; + syncStatus: () => void; + getSize: () => number; +}; + +const createOperators = ( + boardHTML: HTMLTableElement, + board: BoardT +): BoardOperationsT => ({ + setStatus: (x, y, val) => { + board[y][x] = val; + + const boardRowHTML = boardHTML.children.item(y); + if (boardRowHTML) { + const cellHTML = boardRowHTML.children.item(x); + if (cellHTML) { + cellHTML.className = isAlive(val) as string; + } + } + }, + syncStatus: () => { + for (let y = 0; y < board.length; y++) { + const boardRowHTML = boardHTML.children.item(y); + + if (boardRowHTML) + for (let x = 0; x < board.length; x++) { + const boardCellHTML = boardRowHTML.children.item(x); + + if (boardCellHTML) + board[y][x] = isAlive(boardCellHTML.className) as boolean; + } + } + }, + getSize: () => board.length, +}); + +function* randomBit(amount: number) { + const bytes = new Uint8Array(amount); + const QUOTA = 65536; + + for (let i = 0; i < amount; i += QUOTA) { + crypto.getRandomValues(bytes.subarray(i, i + Math.min(amount - i, QUOTA))); + } + + let n = 0; + + while (true) { + if (n >= amount) return false; + + const byteN = Math.floor(n / 8); + const bitPos = n % 8; + + yield Boolean((bytes[byteN] >> bitPos) & 1); + + n++; + } +} + +const initBoard = (op: BoardOperationsT) => { + const size = op.getSize(); + const getRandomBit = randomBit(size * size); + + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const val = getRandomBit.next().value; + op.setStatus(x, y, val); + } + } +}; + +const countNeighbours = (x: number, y: number, board: BoardT) => { + let k = 0; + + if (y > 0) { + if (board[y - 1][x]) k++; + if (x < board.length && board[y - 1][x + 1]) k++; + if (x > 0 && board[y - 1][x - 1]) k++; + } + + if (y < board.length - 1) { + if (board[y + 1][x]) k++; + if (x < board.length && board[y + 1][x + 1]) k++; + if (x > 0 && board[y + 1][x - 1]) k++; + } + + if (x > 0 && board[y][x - 1]) k++; + if (x < board.length && board[y][x + 1]) k++; + + return k; +}; + +const doStep = (op: BoardOperationsT, board: BoardT) => { + const size = op.getSize(); + + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + const nb = countNeighbours(x, y, board); + if (board[y][x] && (nb < 2 || nb > 3)) op.setStatus(x, y, false); + else if (!board[y][x] && nb === 3) op.setStatus(x, y, true); + } + } +}; + +export const initGame = (size: number) => { + const root = document.getElementById("board"); + + if (!root) { + console.error("Can not find root element"); + } else { + root.innerHTML = ""; + + const [boardHTML, board] = createBoard(root, size); + + const op = createOperators(boardHTML, board); + + initBoard(op); + + for (let t = 750; t < 100000; t += 750) + setTimeout(() => doStep(op, board), t); + } +}; + +const runButton = document.getElementById("run"); + +if (runButton) { + runButton.onclick = (e) => { + e.preventDefault(); + + const sizeInput = document.getElementById("size"); + + if (sizeInput) { + const { value } = sizeInput as HTMLInputElement; + + const size = value ? parseInt(value) : 16; + + initGame(size); + } + }; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..80d3cbe --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + + + + + Conway's game + + + + + + +
+ + + + \ No newline at end of file diff --git a/out.js b/out.js new file mode 100644 index 0000000..33ef580 --- /dev/null +++ b/out.js @@ -0,0 +1 @@ +(()=>{var a=t=>{if(typeof t=="string")return t==="alive";if(typeof t=="boolean")return t?"alive":"dead"},u=(t,n=16)=>{let o=document.createElement("table"),e=[];for(let s=0;s({setStatus:(o,e,s)=>{n[e][o]=s;let i=t.children.item(e);if(i){let r=i.children.item(o);r&&(r.className=a(s))}},syncStatus:()=>{for(let o=0;on.length});function*d(t){let n=new Uint8Array(t),o=65536;for(let s=0;s=t)return!1;let s=Math.floor(e/8),i=e%8;yield Boolean(n[s]>>i&1),e++}}var m=t=>{let n=t.getSize(),o=d(n*n);for(let e=0;e{let e=0;return n>0&&(o[n-1][t]&&e++,t0&&o[n-1][t-1]&&e++),n0&&o[n+1][t-1]&&e++),t>0&&o[n][t-1]&&e++,t{let o=t.getSize();for(let e=0;e3)?t.setStatus(s,e,!1):!n[e][s]&&i===3&&t.setStatus(s,e,!0)}},h=t=>{let n=document.getElementById("board");if(!n)console.error("Can not find root element");else{n.innerHTML="";let[o,e]=u(n,t),s=f(o,e);m(s);for(let i=750;i<1e5;i+=750)setTimeout(()=>p(s,e),i)}},c=document.getElementById("run");c&&(c.onclick=t=>{t.preventDefault();let n=document.getElementById("size");if(n){let{value:o}=n,e=o?parseInt(o):16;h(e)}});})(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d8dd219 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,63 @@ +{ + "name": "conways_game", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@types/node": "^14.14.41", + "esbuild": "^0.11.12", + "typescript": "^4.2.4" + } + }, + "node_modules/@types/node": { + "version": "14.14.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", + "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.11.12.tgz", + "integrity": "sha512-c8cso/1RwVj+fbDvLtUgSG4ZJQ0y9Zdrl6Ot/GAjyy4pdMCHaFnDMts5gqFnWRPLajWtEnI+3hlET4R9fVoZng==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + } + }, + "node_modules/typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "@types/node": { + "version": "14.14.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", + "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", + "dev": true + }, + "esbuild": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.11.12.tgz", + "integrity": "sha512-c8cso/1RwVj+fbDvLtUgSG4ZJQ0y9Zdrl6Ot/GAjyy4pdMCHaFnDMts5gqFnWRPLajWtEnI+3hlET4R9fVoZng==", + "dev": true + }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8f89eee --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "build": "esbuild app.ts --bundle --minify --outfile=out.js" + }, + "devDependencies": { + "@types/node": "^14.14.41", + "esbuild": "^0.11.12", + "typescript": "^4.2.4" + } +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..51f7056 --- /dev/null +++ b/style.css @@ -0,0 +1,12 @@ +td { + height: 5px; + width: 5px; +} + +.dead { + background-color: white; +} + +.alive { + background-color: black; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fb88b50 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +}