First implementation
This commit is contained in:
commit
b9787ae877
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
build/
|
175
app.ts
Normal file
175
app.ts
Normal 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
19
index.html
Normal 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
1
out.js
Normal 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
63
package-lock.json
generated
Normal 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
10
package.json
Normal 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
12
style.css
Normal file
@ -0,0 +1,12 @@
|
||||
td {
|
||||
height: 5px;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.dead {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.alive {
|
||||
background-color: black;
|
||||
}
|
10
tsconfig.json
Normal file
10
tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user