update,
This commit is contained in:
BIN
chris20202020/_ref/1.docx
Normal file
BIN
chris20202020/_ref/1.docx
Normal file
Binary file not shown.
BIN
chris20202020/_ref/1.pdf
Normal file
BIN
chris20202020/_ref/1.pdf
Normal file
Binary file not shown.
BIN
chris20202020/_ref/2.docx
Normal file
BIN
chris20202020/_ref/2.docx
Normal file
Binary file not shown.
BIN
chris20202020/_ref/2.pdf
Normal file
BIN
chris20202020/_ref/2.pdf
Normal file
Binary file not shown.
BIN
chris20202020/_ref/3.pdf
Normal file
BIN
chris20202020/_ref/3.pdf
Normal file
Binary file not shown.
144
chris20202020/_ref/Data package from December 23rd-1/1.html
Normal file
144
chris20202020/_ref/Data package from December 23rd-1/1.html
Normal file
File diff suppressed because one or more lines are too long
384
chris20202020/_ref/Data package from December 23rd-1/2.html
Normal file
384
chris20202020/_ref/Data package from December 23rd-1/2.html
Normal file
File diff suppressed because one or more lines are too long
104
chris20202020/_ref/Data package from December 23rd-1/3.html
Normal file
104
chris20202020/_ref/Data package from December 23rd-1/3.html
Normal file
File diff suppressed because one or more lines are too long
144
chris20202020/_ref/Data package from December 23rd/1.html
Normal file
144
chris20202020/_ref/Data package from December 23rd/1.html
Normal file
File diff suppressed because one or more lines are too long
384
chris20202020/_ref/Data package from December 23rd/2.html
Normal file
384
chris20202020/_ref/Data package from December 23rd/2.html
Normal file
File diff suppressed because one or more lines are too long
104
chris20202020/_ref/Data package from December 23rd/3.html
Normal file
104
chris20202020/_ref/Data package from December 23rd/3.html
Normal file
File diff suppressed because one or more lines are too long
9
chris20202020/_ref/game-of-life-js/README.md
Normal file
9
chris20202020/_ref/game-of-life-js/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
A JavaScript implementation of [Conway's Game of Life](http://web.stanford.edu/%7Ecdebs/GameOfLife/).
|
||||
|
||||
- represents the board as 2D array of cells
|
||||
- creates a second representation of board as 2D array of number of live neighbors
|
||||
- uses map, filter, and reduce for calculation/iteration
|
||||
|
||||
`index.html` displays a canvas version of the program, with play/pause and reload buttons. (Play/pause can be triggered with the `Space` or `Enter` keys. Reloading can be triggered with the `r` key.)
|
||||
|
||||
Also, can run on the terminal or in the browser console.
|
135
chris20202020/_ref/game-of-life-js/canvas.js
Normal file
135
chris20202020/_ref/game-of-life-js/canvas.js
Normal file
@@ -0,0 +1,135 @@
|
||||
console.log('Loading Game of Life');
|
||||
|
||||
const {
|
||||
newBoard,
|
||||
neighborCoordinatesForBoard,
|
||||
boardAsNumberOfNeighbors,
|
||||
isLive,
|
||||
isUnderPopulated,
|
||||
isOverPopulated,
|
||||
willContinue,
|
||||
canReproduce,
|
||||
SIZE
|
||||
} = life;
|
||||
|
||||
const playPause = document.querySelector('[data-play-pause]');
|
||||
const reload = document.querySelector('[data-reload]');
|
||||
|
||||
const startClass = 'fa-play';
|
||||
const pauseClass = 'fa-pause';
|
||||
|
||||
|
||||
const canvas = document.querySelector('#life');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const {height, width} = canvas;
|
||||
|
||||
const UPDATE_FREQUENCY = 30;
|
||||
const GRID_COUNT = SIZE;
|
||||
const CELL_SIZE = width/GRID_COUNT;
|
||||
|
||||
const COLORS = {
|
||||
// live: 'rgba(200, 0, 0, 1)',
|
||||
live: 'rgba(40, 169, 255, 0.75)',
|
||||
dead: 'rgba(255, 255, 255, 0.5)'
|
||||
};
|
||||
|
||||
// Uses function declaration for binding `this`
|
||||
// to the canvas context.
|
||||
function _renderBox (isLive, xGrid, yGrid) {
|
||||
this.lineWidth = 0.8;
|
||||
this.strokeStyle = isLive ? COLORS.live : COLORS.dead;
|
||||
this.strokeRect(xGrid*CELL_SIZE,
|
||||
yGrid*CELL_SIZE,
|
||||
CELL_SIZE,
|
||||
CELL_SIZE);
|
||||
}
|
||||
|
||||
//
|
||||
const liveAt = _renderBox.bind(ctx, true);
|
||||
const deadAt = _renderBox.bind(ctx, false);
|
||||
|
||||
const numberToIsLive = (number, cell) => {
|
||||
if (isLive(cell)) {
|
||||
if (isUnderPopulated(number)) {
|
||||
// return DEAD;
|
||||
return false;
|
||||
} else if (isOverPopulated(number)) {
|
||||
// return DEAD;
|
||||
return false
|
||||
} else if (willContinue(number)) {
|
||||
// return LIVE;
|
||||
return true;
|
||||
}
|
||||
} else if (canReproduce(number)){
|
||||
// return LIVE;
|
||||
return true;
|
||||
} else {
|
||||
// return DEAD;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Given rows or boards of cells and neighbor counts, calculate next states.
|
||||
const numberRowAsLiveDeadCells = (rowOfNumbers, rowOfCells) => rowOfNumbers.map((n, i) => numberToIsLive(n, rowOfCells[i]));
|
||||
const numberBoardAsLiveDeadCells = (boardOfNumbers, boardOfCells) => boardOfNumbers.map((r, i) => numberRowAsLiveDeadCells(r, boardOfCells[i]));
|
||||
|
||||
|
||||
const renderRow = (r, y) => r.map((c, i) => (c ? liveAt(i, y) : deadAt(i, y)) && c);
|
||||
const renderBoard = b => b.map(renderRow);
|
||||
|
||||
|
||||
let rafID;
|
||||
let board = newBoard(true);
|
||||
const coords = neighborCoordinatesForBoard(board);
|
||||
let neighbors; // The game board as number of live neighbors per cell.
|
||||
let isRunning = false;
|
||||
const main = () => {
|
||||
|
||||
if (isRunning) {
|
||||
// Given a board, calculate all the valid neighbor coordinates.
|
||||
neighbors = boardAsNumberOfNeighbors(board, coords); // Calculate live neighbor counts.
|
||||
board = numberBoardAsLiveDeadCells(neighbors, board); // Calculate next state of board.
|
||||
renderBoard(board);
|
||||
|
||||
setTimeout(() => {
|
||||
rafID = requestAnimationFrame(main);
|
||||
}, UPDATE_FREQUENCY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const togglePlaying = () => {
|
||||
if (isRunning) {
|
||||
cancelAnimationFrame(rafID);
|
||||
playPause.querySelector('i').classList.add(startClass);
|
||||
playPause.querySelector('i').classList.remove(pauseClass);
|
||||
} else {
|
||||
rafID = requestAnimationFrame(main);
|
||||
playPause.querySelector('i').classList.remove(startClass);
|
||||
playPause.querySelector('i').classList.add(pauseClass);
|
||||
}
|
||||
isRunning = !isRunning;
|
||||
};
|
||||
|
||||
const reloadBoard = () => board = newBoard(true);
|
||||
|
||||
playPause.addEventListener('click', togglePlaying);
|
||||
reload.addEventListener('click', reloadBoard);
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
console.log(e.keyCode);
|
||||
if (e.getModifierState('Control')) {
|
||||
return;
|
||||
}
|
||||
switch (e.keyCode) {
|
||||
case 32:
|
||||
case 13:
|
||||
e.preventDefault();
|
||||
togglePlaying()
|
||||
break;
|
||||
case 82:
|
||||
e.preventDefault();
|
||||
reloadBoard();
|
||||
break;
|
||||
}
|
||||
})
|
48
chris20202020/_ref/game-of-life-js/index.html
Normal file
48
chris20202020/_ref/game-of-life-js/index.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
background-color: rgba(40, 169, 255, 0.25);
|
||||
}
|
||||
canvas {
|
||||
border: 1px solid rgba(0, 0, 0, 0.7);
|
||||
background-color: #fff;
|
||||
}
|
||||
button {
|
||||
width: 80px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="life"
|
||||
width="420"
|
||||
height="420">
|
||||
Loading...
|
||||
</canvas>
|
||||
<div>
|
||||
<button data-play-pause>
|
||||
<i class="fa fa-play"></i>
|
||||
</button>
|
||||
<button data-reload>
|
||||
<i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<script src="life.js"></script>
|
||||
<script src="canvas.js"></script>
|
||||
</body>
|
||||
</html>
|
2
chris20202020/_ref/game-of-life-js/index.js
Normal file
2
chris20202020/_ref/game-of-life-js/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const life = require('./life');
|
||||
life();
|
172
chris20202020/_ref/game-of-life-js/life.js
Normal file
172
chris20202020/_ref/game-of-life-js/life.js
Normal file
@@ -0,0 +1,172 @@
|
||||
const life = (() => {
|
||||
const SIZE = 42; // Size of (square) board
|
||||
const INTERVAL = 300; // Frequency of screen updates
|
||||
const THRESHOLD = 33; // % chance a cell will be seeded with life
|
||||
|
||||
// Printable representations of cells
|
||||
let LIVE = "L";
|
||||
let DEAD = " ";
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// The rules
|
||||
/*
|
||||
- Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
|
||||
- Any live cell with two or three live neighbours lives on to the next generation.
|
||||
- Any live cell with more than three live neighbours dies, as if by overpopulation.
|
||||
- Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
|
||||
*/
|
||||
|
||||
const FEW = 2;
|
||||
const MANY = 3;
|
||||
const PLENTY = 3;
|
||||
|
||||
const isLive = c => c === LIVE;
|
||||
const isUnderPopulated = n => n < FEW;
|
||||
const isOverPopulated = n => n > MANY;
|
||||
const canReproduce = n => n === PLENTY;
|
||||
const willContinue = n => !(isUnderPopulated(n)) && !(isOverPopulated(n));
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
const getRandomInt = (max, min=0) => {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
|
||||
}
|
||||
|
||||
// New boards are rows of DEAD cells, optionally seeded with LIVE cells.
|
||||
const newRow = () => Array(SIZE).fill(DEAD);
|
||||
const newBoard = shouldSeed => {
|
||||
let board = newRow().map(newRow);
|
||||
if (shouldSeed) {
|
||||
board = board.map(row => row.map(_ => getRandomInt(100) < THRESHOLD ? LIVE : DEAD))
|
||||
}
|
||||
return board;
|
||||
}
|
||||
|
||||
// When counting live neighbors, make sure to stay within array bounds.
|
||||
const isWithinBounds = v => v >= 0 && v < SIZE;
|
||||
const areWithinBounds = (x, y) => isWithinBounds(x) && isWithinBounds(y);
|
||||
|
||||
// Given a coordinate pair, return an array of valid neighbor coordinate pairs.
|
||||
const neighborCoordinates = (x, y) => [
|
||||
[x-1, y-1], [x, y-1], [x+1, y-1],
|
||||
[x-1, y], [x+1, y],
|
||||
[x-1, y+1], [x, y+1], [x+1, y+1],
|
||||
].filter(xyArr => areWithinBounds(...xyArr));
|
||||
|
||||
// Functions to produce board containing coordinate pairs for each cell.
|
||||
const coordsForRow = (r, x=0) => r.map((_, y) => [x, y]);
|
||||
const coordsForBoard = b => b.map(coordsForRow);
|
||||
|
||||
// Functions to produce board containing array of valid neighbor coordinate pairs.
|
||||
const neighborCoordinatesForRow = (r, x=0) => coordsForRow(r, x).map(xyArr => neighborCoordinates(...xyArr));
|
||||
const neighborCoordinatesForBoard = b => b.map(neighborCoordinatesForRow);
|
||||
|
||||
// Retrieve value of cell at specified coordinates.
|
||||
const cellAtCoorinate = (board, x, y) => board[x][y];
|
||||
|
||||
// Given a board and an array of neighbor coordinates, retrieve the neighbor cells.
|
||||
const neighborCellsForCoordinateArray = (board, arrayOfNeighborCoors) => {
|
||||
return arrayOfNeighborCoors.map(neighborCoordsForCell => {
|
||||
return neighborCoordsForCell.map(coordsArray => {
|
||||
return coordsArray.map(xyArray => cellAtCoorinate(board, ...xyArray))
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
// Given a board and an array of neighbor coordinates, return a board with the live neighbor count
|
||||
// for each cell.
|
||||
const boardAsNumberOfNeighbors = (board, arrayOfNeighborCoords) => {
|
||||
return neighborCellsForCoordinateArray(board, arrayOfNeighborCoords).map(neighborCellsForRow => {
|
||||
return neighborCellsForRow.map(neighborCellsForCell => {
|
||||
return neighborCellsForCell
|
||||
.filter(isLive)
|
||||
.reduce((total, _) => total + 1, 0)
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Given a live neighbor count and a cell, calculate the cell's next state.
|
||||
const numberToLiveDead = (number, cell) => {
|
||||
if (isLive(cell)) {
|
||||
if (isUnderPopulated(number)) {
|
||||
return DEAD;
|
||||
} else if (isOverPopulated(number)) {
|
||||
return DEAD;
|
||||
} else if (willContinue(number)) {
|
||||
return LIVE;
|
||||
}
|
||||
} else if (canReproduce(number)){
|
||||
return LIVE;
|
||||
} else {
|
||||
return DEAD;
|
||||
}
|
||||
};
|
||||
|
||||
// Given rows or boards of cells and neighbor counts, calculate next states.
|
||||
const numberRowAsLiveDeadCells = (rowOfNumbers, rowOfCells) => rowOfNumbers.map((n, i) => numberToLiveDead(n, rowOfCells[i]));
|
||||
const numberBoardAsLiveDeadCells = (boardOfNumbers, boardOfCells) => boardOfNumbers.map((r, i) => numberRowAsLiveDeadCells(r, boardOfCells[i]));
|
||||
|
||||
// Functions for printing to the console.
|
||||
const printRow = r => r.join(' '); // A little horizontal space looks better
|
||||
const printBoard = b => {
|
||||
const boardAsString = b.map(printRow).join('\n');
|
||||
console.log(boardAsString);
|
||||
return boardAsString;
|
||||
};
|
||||
|
||||
// The game loop!
|
||||
const main = board => {
|
||||
// Given a board, calculate all the valid neighbor coordinates.
|
||||
const coords = neighborCoordinatesForBoard(board);
|
||||
|
||||
let neighbors; // The game board as number of live neighbors per cell.
|
||||
let generation = 0; // What generation we're on.
|
||||
let curr = ''; // Current generation as a string.
|
||||
let prev = ''; // For checking if this generation is same as current - 1.
|
||||
let prevMinusOne = ''; // For checking if this generation is same as current - 2.
|
||||
|
||||
let tick = setInterval(() => {
|
||||
neighbors = boardAsNumberOfNeighbors(board, coords); // Calculate live neighbor counts.
|
||||
board = numberBoardAsLiveDeadCells(neighbors, board); // Calculate next state of board.
|
||||
console.clear();
|
||||
prevMinusOne = prev.slice(); // Copy string representation of current - 2.
|
||||
prev = curr.slice(); // Copy string representation of current - 1.
|
||||
curr = printBoard(board); // Print board, saving string representation.
|
||||
console.log(`Generation ${generation}`);
|
||||
generation++;
|
||||
|
||||
// If the current generation is identical to one of the previous two,
|
||||
// then we've reached the end of the simulation.
|
||||
if (curr === prev || curr === prevMinusOne) {
|
||||
clearInterval(tick);
|
||||
}
|
||||
}, INTERVAL);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
|
||||
// This must be node!
|
||||
module.exports = () => {
|
||||
main(newBoard(true)); // Start game with new board, seeded with some live cells.
|
||||
}
|
||||
} else {
|
||||
LIVE = true;
|
||||
DEAD = false;
|
||||
|
||||
return {
|
||||
newBoard,
|
||||
neighborCoordinatesForBoard,
|
||||
boardAsNumberOfNeighbors,
|
||||
isLive,
|
||||
isUnderPopulated,
|
||||
isOverPopulated,
|
||||
willContinue,
|
||||
canReproduce,
|
||||
SIZE
|
||||
}
|
||||
}
|
||||
})();
|
6613
chris20202020/_ref/game-of-life-js/package-lock.json
generated
Normal file
6613
chris20202020/_ref/game-of-life-js/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
chris20202020/_ref/game-of-life-js/package.json
Normal file
17
chris20202020/_ref/game-of-life-js/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "js-game-of-life",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev": "nodemon index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"nodemon": "^1.17.1"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user