Conway's Game of Life
by Retsam19
HTML
<label>
Min X: <input data-bind="value: xMin, valueUpdate:'input'"></input>
</label>
<label>
Max X: <input data-bind="value: xMax, valueUpdate:'input'"></input>
</label>
<button data-bind="
click: toggleRun,
text: running() ? 'Stop' : 'Run'
"></button>
<br />
<label>
Min Y: <input data-bind="value: yMin, valueUpdate:'input'"></input>
</label>
<label>
Max Y: <input data-bind="value: yMax, valueUpdate:'input'"></input>
</label>
<button data-bind="click: step">Step</button>
<div class="table" data-bind="foreach: {as: 'y', data: yRange}">
<div class="row" data-bind="foreach: {as: 'x', data: $root.xRange}">
<div class="cell" data-bind="
css: {
alive: $root.generation().isAlive({x: x, y:y}),
},
click: $root.toggleCell
"></div>
</div>
</div>
CSS
.table {
display: flex-inline;
flex-direction: column;
}
.row {
display: flex;
flex-shrink:1;
}
.cell {
border: 1px solid black;
width: 50px;
height: 50px;
}
.cell.alive {
background-color: green
}
JavaScript
var RUN_SPEED = 500; //Lower this to speed up Run mode
var vm = {};
vm.generation = ko.observable(new Generation([{x: 0, y: 0}, {x: 1, y: 0}, {x:2, y: 0},{x:2,y:1},{x: 1,y:2}]));
vm.xMin = ko.observable(-5);
vm.xMax = ko.observable(5);
vm.yMin = ko.observable(-5);
vm.yMax = ko.observable(5);
vm.xRange = ko.computed(function () {
var range = [];
for(var x=vm.xMin(); x<=vm.xMax(); x++) {
range.push(x);
}
return range;
});
vm.yRange = ko.computed(function () {
var range = [];
for(var y=vm.yMax(); y>=vm.yMin(); y--) {
range.push(y);
}
return range;
});
vm.toggleCell = function(data,e) {
var ctx = ko.contextFor(e.target);
vm.generation.valueWillMutate();
vm.generation().toggleAlive({x: ctx.x, y: ctx.y});
vm.generation.valueHasMutated();
}
vm.step = function() {
vm.generation(vm.generation().nextGeneration());
}
vm.running = ko.observable(false);
var runId;
vm.running.subscribe(function (nowRunning) {
if(nowRunning) {
vm.step();
runId = window.setInterval(vm.step, RUN_SPEED);
} else {
window.clearInterval(runId);
}
});
vm.toggleRun = function () {
vm.running(!vm.running());
}
ko.applyBindings(vm);
function Generation(cellList) {
var liveCells = {};
var self = this;
function init(cellList) {
cellList.map(self.setAlive);
}
this.setAlive = function(cell) {
var x = cell.x; var y = cell.y;
liveCells[x] = liveCells[x] || {};
liveCells[x][y] = true;
return true;
};
this.setDead = function(cell) {
var x = cell.x; var y = cell.y;
liveCells[x] = liveCells[x] || {};
liveCells[x][y] = false;
return false;
};
this.isAlive = function(cell) {
var x = cell.x; var y = cell.y;
return liveCells[x] && liveCells[x][y];
};
this.toggleAlive = function(cell) {
return self.isAlive(cell) ? self.setDead(cell) : self.setAlive(cell);
};
this.getLiveCells = function() {
var cells = [];
for(var x in...