First implementation

This commit is contained in:
Dmitriy Shishkov 2021-04-21 19:08:38 +05:00
commit b9787ae877
No known key found for this signature in database
GPG Key ID: 7CAE12ED13853CAC
8 changed files with 292 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
build/

175
app.ts Normal file
View File

@ -0,0 +1,175 @@
type BoardT<T = boolean> = T[][];
type InitBoardReturnT = [HTMLTableElement, BoardT];
const isAlive = <T>(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);
}
};
}

19
index.html Normal file
View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Conway's game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<input type="number" name="size" id="size">
<button id="run">Start</button>
<div id="board"></div>
<script src="./out.js"></script>
</body>
</html>

1
out.js Normal file
View File

@ -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<n;s++){let i=document.createElement("tr");e.push([]);for(let r=0;r<n;r++){let l=document.createElement("td");e[s].push(!1),l.className="dead",i.appendChild(l)}o.appendChild(i)}return t.appendChild(o),[o,e]},f=(t,n)=>({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;o<n.length;o++){let e=t.children.item(o);if(e)for(let s=0;s<n.length;s++){let i=e.children.item(s);i&&(n[o][s]=a(i.className))}}},getSize:()=>n.length});function*d(t){let n=new Uint8Array(t),o=65536;for(let s=0;s<t;s+=o)crypto.getRandomValues(n.subarray(s,s+Math.min(t-s,o)));let e=0;for(;;){if(e>=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<n;e++)for(let s=0;s<n;s++){let i=o.next().value;t.setStatus(s,e,i)}},T=(t,n,o)=>{let e=0;return n>0&&(o[n-1][t]&&e++,t<o.length&&o[n-1][t+1]&&e++,t>0&&o[n-1][t-1]&&e++),n<o.length-1&&(o[n+1][t]&&e++,t<o.length&&o[n+1][t+1]&&e++,t>0&&o[n+1][t-1]&&e++),t>0&&o[n][t-1]&&e++,t<o.length&&o[n][t+1]&&e++,e},p=(t,n)=>{let o=t.getSize();for(let e=0;e<o;e++)for(let s=0;s<o;s++){let i=T(s,e,n);n[e][s]&&(i<2||i>3)?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)}});})();

63
package-lock.json generated Normal file
View File

@ -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
}
}
}

10
package.json Normal file
View File

@ -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"
}
}

12
style.css Normal file
View File

@ -0,0 +1,12 @@
td {
height: 5px;
width: 5px;
}
.dead {
background-color: white;
}
.alive {
background-color: black;
}

10
tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}