Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active February 18, 2018 03:38
Show Gist options
  • Save mathdoodle/14f7989526d8d040007b47c1cfbe1e7c to your computer and use it in GitHub Desktop.
Save mathdoodle/14f7989526d8d040007b47c1cfbe1e7c to your computer and use it in GitHub Desktop.
Conway's Game of Life

Conway's Game of Life

Overview

Copyright (c) 2015-2017 David Geo Holmes.

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)
}
}
<!DOCTYPE html>
<html>
<head>
<base href='/'>
<script src='https://jspm.io/system@0.19.34.js'></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>
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)
{
"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"
]
}
body {
background-color: blue;
font-family: Roboto, Arial, sans-serif;
color: #333333;
}
canvas {
background-color: #000000;
}
{
"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
}
{
"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