Copyright (c) 2015-2017 David Geo Holmes.
Last active
February 18, 2018 03:38
-
-
Save mathdoodle/14f7989526d8d040007b47c1cfbe1e7c to your computer and use it in GitHub Desktop.
Conway's Game of Life
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class Game { | |
} | |
export interface ModelOptions { | |
threshold?: number | |
} | |
export class Model { | |
public rows: number | |
public columns: number | |
public data: boolean[] = [] | |
private threshold: number | |
constructor(rows: number, columns: number, options: ModelOptions = {}) { | |
this.threshold = typeof options.threshold === 'number' ? options.threshold : 0.5 | |
this.rows = rows | |
this.columns = columns | |
for (let row = 0; row < rows; row++) { | |
for (let column = 0; column < columns; column++) { | |
this.data[this.indexFromPosition(row, column)] = Math.random() > this.threshold | |
} | |
} | |
} | |
indexFromPosition(row: number, column: number): number { | |
return (row % this.rows) * this.columns + (column % this.columns) | |
} | |
isAlive(row: number, column: number): boolean { | |
return this.data[this.indexFromPosition(row, column)] | |
} | |
neighbors(row: number, column: number): number { | |
let count = 0 | |
if (this.isAlive(row - 1, column - 1)) { | |
count += 1 | |
} | |
if (this.isAlive(row - 1, column)) { | |
count += 1 | |
} | |
if (this.isAlive(row - 1, column + 1)) { | |
count += 1 | |
} | |
if (this.isAlive(row, column - 1)) { | |
count += 1 | |
} | |
if (this.isAlive(row, column + 1)) { | |
count += 1 | |
} | |
if (this.isAlive(row + 1, column - 1)) { | |
count += 1 | |
} | |
if (this.isAlive(row + 1, column)) { | |
count += 1 | |
} | |
if (this.isAlive(row + 1, column + 1)) { | |
count += 1 | |
} | |
return count | |
} | |
step(): void { | |
const next = this.data.map(x => x) | |
for (let row = 0; row < this.rows; row++) { | |
for (let column = 0; column < this.columns; column++) { | |
const index = this.indexFromPosition(row, column) | |
const N = this.neighbors(row, column) | |
if (this.isAlive(row, column)) { | |
if (N < 2) { | |
next[index] = false | |
} | |
else if (N > 3) { | |
next[index] = false | |
} | |
else { | |
// Do nothing (stay alive). | |
} | |
} | |
else { | |
if (N === 3) { | |
next[index] = true | |
} | |
else { | |
// Do nothing (stay dead). | |
} | |
} | |
} | |
} | |
for (let row = 0; row < this.rows; row++) { | |
for (let column = 0; column < this.columns; column++) { | |
const index = this.indexFromPosition(row, column) | |
this.data[index] = next[index] | |
} | |
} | |
} | |
} | |
export interface ViewOptions { | |
margin?: number | |
} | |
export class View { | |
private model: Model | |
private canvas: HTMLCanvasElement | |
private ctxt2d: CanvasRenderingContext2D | |
private margin: number | |
private tileX: number | |
private tileY: number | |
private stepX: number | |
private stepY: number | |
constructor(canvasId: string, model: Model, options: ViewOptions = {}) { | |
this.margin = typeof options.margin === 'number' ? options.margin : 1 | |
this.model = model | |
this.canvas = document.getElementById(canvasId) as HTMLCanvasElement | |
this.ctxt2d = this.canvas.getContext('2d') as CanvasRenderingContext2D | |
this.stepX = this.canvas.width / model.columns | |
this.stepY = this.canvas.height / model.rows | |
this.tileX = (this.canvas.width / model.columns) - 2 * this.margin | |
this.tileY = (this.canvas.height / model.rows) - 2 * this.margin | |
} | |
/** | |
* Render the model. | |
*/ | |
update() { | |
// Redraw the view | |
this.ctxt2d.fillStyle = '#444444' | |
this.ctxt2d.fillRect(0, 0, this.canvas.width, this.canvas.height) | |
for (let row = 0; row < this.model.rows; row++) { | |
for (let column = 0; column < this.model.columns; column++) { | |
this.colorRect(row, column) | |
} | |
} | |
} | |
private colorRect(row: number, column: number): void { | |
this.ctxt2d.fillStyle = this.model.isAlive(row, column) ? '#555577' : '#AAAACC' | |
const x = this.margin + column * this.stepX | |
const y = this.margin + row * this.stepY | |
this.ctxt2d.fillRect(x, y, this.tileX, this.tileY) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<base href='/'> | |
<script src='https://jspm.io/[email protected]'></script> | |
<link rel='stylesheet' href='style.css'> | |
</head> | |
<body> | |
<canvas id='game-canvas' width='600' height='600'></canvas> | |
<script> | |
System.defaultJSExtensions = true | |
System.import('./main') | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Model, View } from './game' | |
const model = new Model(30, 30, { threshold: 0.8 }) | |
const view = new View('game-canvas', model, { margin: 1 }) | |
let frame = 0 | |
const animate = function() { | |
if (frame === 60) { | |
model.step() | |
view.update() | |
frame = 0 | |
} | |
frame += 1 | |
window.requestAnimationFrame(animate) | |
} | |
window.requestAnimationFrame(animate) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"description": "Conway's Game of Life", | |
"dependencies": { | |
"DomReady": "1.0.0" | |
}, | |
"hideConfigFiles": true, | |
"linting": true, | |
"name": "conway's-game-of-life", | |
"version": "0.9.0", | |
"author": "David Geo Holmes", | |
"keywords": [ | |
"mathdoodle" | |
] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: blue; | |
font-family: Roboto, Arial, sans-serif; | |
color: #333333; | |
} | |
canvas { | |
background-color: #000000; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{ | |
"allowJs": true, | |
"checkJs": true, | |
"declaration": true, | |
"emitDecoratorMetadata": true, | |
"experimentalDecorators": true, | |
"jsx": "react", | |
"module": "system", | |
"noImplicitAny": true, | |
"noImplicitReturns": true, | |
"noImplicitThis": true, | |
"noUnusedLocals": true, | |
"noUnusedParameters": true, | |
"preserveConstEnums": true, | |
"removeComments": false, | |
"skipLibCheck": true, | |
"sourceMap": true, | |
"strictNullChecks": true, | |
"suppressImplicitAnyIndexErrors": true, | |
"target": "es5", | |
"traceResolution": true | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"rules": { | |
"array-type": [ | |
true, | |
"array" | |
], | |
"curly": false, | |
"comment-format": [ | |
true, | |
"check-space" | |
], | |
"eofline": true, | |
"forin": true, | |
"jsdoc-format": true, | |
"new-parens": true, | |
"no-conditional-assignment": false, | |
"no-consecutive-blank-lines": true, | |
"no-construct": true, | |
"no-for-in-array": true, | |
"no-inferrable-types": [ | |
true | |
], | |
"no-magic-numbers": false, | |
"no-shadowed-variable": true, | |
"no-string-throw": true, | |
"no-trailing-whitespace": [ | |
true, | |
"ignore-jsdoc" | |
], | |
"no-var-keyword": true, | |
"one-variable-per-declaration": [ | |
true, | |
"ignore-for-loop" | |
], | |
"prefer-const": true, | |
"prefer-for-of": true, | |
"prefer-function-over-method": false, | |
"prefer-method-signature": true, | |
"radix": true, | |
"semicolon": [ | |
true, | |
"never" | |
], | |
"trailing-comma": [ | |
true, | |
{ | |
"multiline": "never", | |
"singleline": "never" | |
} | |
], | |
"triple-equals": true, | |
"use-isnan": true | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment