-
-
Save richdouglasevans/0f9a57e5a52b13e93c0c03630165ecd8 to your computer and use it in GitHub Desktop.
Code for Game of Life from the Comonad article.
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
const { tagged } = require("daggy"); | |
const Pair = tagged("Pair", ["_1", "_2"]); | |
//+ data Store p s = Store (p -> s) p | |
const Store = tagged("Store", ["lookup", "pointer"]); | |
Array.prototype.equals = function(that) { | |
return ( | |
this.length === that.length && | |
this.every((x, i) => (x.equals ? x.equals(that[i]) : x === that[i])) | |
); | |
}; | |
Function.prototype.map = function(f) { | |
return x => f(this(x)); | |
}; | |
Store.prototype.peek = function(p) { | |
return this.seek(p).extract(); | |
}; | |
Store.prototype.seek = function(p) { | |
return Store(this.lookup, p); | |
}; | |
Store.prototype.map = function(f) { | |
return Store(this.lookup.map(f), this.pointer); | |
}; | |
Store.prototype.extend = function(f) { | |
const { lookup, pointer } = this; | |
return Store(p => f(Store(lookup, p)), pointer); | |
}; | |
Store.prototype.extract = function() { | |
return this.lookup(this.pointer); | |
}; | |
// GAME OF LIFE // | |
let start = [ | |
[true, true, false, false], | |
[true, false, true, false], | |
[true, false, false, true], | |
[true, false, true, false] | |
]; | |
const Game = Store( | |
({ _1: x, _2: y }) => (y in start && x in start[y] ? start[y][x] : false), | |
Pair(0, 0) | |
); | |
// What will the current cell be next time? | |
//+ isSurvivor :: Store (Pair Int Int) Bool | |
//+ -> Bool | |
const isSurvivor = store => { | |
const { _1: x, _2: y } = store.pointer; | |
const neighbours = [ | |
Pair(x - 1, y - 1), | |
Pair(x, y - 1), | |
Pair(x + 1, y - 1), | |
Pair(x - 1, y), | |
Pair(x + 1, y), | |
Pair(x - 1, y + 1), | |
Pair(x, y + 1), | |
Pair(x + 1, y + 1) | |
] | |
.map(x => store.peek(x)) | |
.filter(x => x).length; // Ignore dead cells | |
return ( | |
neighbours === 3 || // Fine either way | |
(store.extract() && neighbours === 2) | |
); | |
}; | |
//- IMPURITIES | |
const loop = setInterval(() => { | |
// Get the next step. | |
const next = Game.extend(isSurvivor); | |
// Extract the whole result. | |
const result = start.map((row, y) => | |
row.map((column, x) => next.lookup(Pair(x, y))) | |
); | |
// Stop looping if we freeze. | |
if (start.equals(result)) clearInterval(loop); | |
// Pretty print the result. | |
console.log(result.map(row => row.map(column => (column ? "X" : " ")))); | |
// FORGIVE ME, PADRE. In order to get this example working, I did | |
// cheat a LITTLE bit. At this point, the initial board is updated, | |
// so the result next time will be different. If you want to see a | |
// better idea: https://github.com/puffnfresh/game-of-comonads.js | |
start = result; | |
console.log(); | |
}, 100); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment