Published
Edited
Apr 4, 2019
6 stars
Insert cell
Insert cell
Insert cell
// Given a raw value (number of grains on a square),
// return an object with the new value and number of grains to send to the squares to the right and below
function topple(value) {
return value >= 2 ? {value:value-2, right:1, below:1} : {value, right:0, below:0};
}
Insert cell
Insert cell
Insert cell
Insert cell
// Square at (0,0)
a00 = {
let value = (this && this.value) || 0,
right = 0, below = 0;
value += +atrigger;
if (value >= 2) { value -= 2; right = 1; below = 1; }
return {value, right, below};
}
Insert cell
Insert cell
Insert cell
// Square at (1,1)
a11 = {
const value = (this && this.value) || 0;
return topple(value + a10.below + a01.right);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
observable = require('@observablehq/runtime@4')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
m = {
const rt = new observable.Runtime;
const m = rt.module();
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#fff';
ctx.fillRect(0,0,Lx*boxw,Ly*boxh);
for (let y=0; y<Ly; y++) {
for (let x=0; x<Lx; x++) {
m.variable(gridObserver(ctx, x, y)).define(`[${x},${y}]`, gridInputs(x,y), gridDefine(x,y));
}
}
return m;
}
Insert cell
Insert cell
function gridInputs(x,y) {
const inp = [];
if (x > 0) inp.push(`[${x-1},${y}]`);
if (y > 0) inp.push(`[${x},${y-1}]`);
return inp;
}
Insert cell
function gridDefine(x,y) {
if (x === 0 && y === 0) return () => ({value:0, right:0, below:0});
else if (x === 0) return x0y;
else if (y === 0) return xy0;
else return xy;
}
Insert cell
Insert cell
function x0y(left) {
const value = (this && this.value) || 0;
return topple(value + left.right);
}
Insert cell
function xy0(above) {
const value = (this && this.value) || 0;
return topple(value + above.below);
}
Insert cell
function xy(left, above) {
const value = (this && this.value) || 0;
return topple(value + left.right + above.below);
}
Insert cell
Insert cell
function gridObserver(ctx, x, y) {
return {
pending() {
ctx.fillStyle = '#ddd';
ctx.fillRect(boxw*x, boxh*y, boxw-1, boxh-1);
},
fulfilled({value}) {
switch (value) {
case 0: {
ctx.fillStyle = '#00f';
break;
}
case 1: {
ctx.fillStyle = '#f00';
break;
}
case 2: {
ctx.fillStyle = '#0f0';
break;
}
default: {
ctx.fillStyle = '#555';
}
}
ctx.fillRect(boxw*x, boxh*y, boxw-1, boxh-1);
},
rejected(error) {
console.log(`error in cell ${x}, ${y}`);
}
}
}
Insert cell
Insert cell
Insert cell
Insert cell
{
if (!pause)
m.redefine("[0,0]", function*() {
while (true) {
yield ({value:0, right:1, below:1});
}
});
else m.redefine("[0,0]", () => ({value:0, right:0, below:0}));
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
getAllPropertyNamesAsObj(m)
Insert cell
Insert cell
m._scope
Insert cell
Insert cell
getAllPropertyNamesAsObj(m._scope.get("[1,0]"))
Insert cell
Insert cell
getAllPropertyNamesAsObj(m._runtime)
Insert cell
Insert cell
// mutable (0,0)
mutable b00 = 0
Insert cell
{ // topple (0,0)
b00;
if (mutable b00 >= 2) {
mutable b00 -= 2;
mutable b01++;
mutable b10++;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
m2 = {
const rt = new observable.Runtime;
const m = rt.module();
const ctx = canvas2.getContext('2d');
ctx.fillStyle = '#fff';
ctx.fillRect(0,0,Lx2*boxw2,Ly2*boxh2);
for (let y=0; y<Ly2; y++) {
for (let x=0; x<Lx2; x++) {
const tag = `[${x},${y}]`;
// initial value at [x,y]
const init = 0; // (x * y +1)% 2 ; // (Math.random() < 0.1) ? Math.floor(Math.random()*5) : 0;
// mutable [x,y]
m.variable().define('mutable '+tag, ["Mutable"], (M) => new M(init));
// [x,y]
m.variable(gridObserver2(ctx, x, y)).define(tag, ["mutable "+tag], _ => _.generator);
// topple [x,y]
m.variable(noObs).define('topple '+tag, gridInputs2(x,y, +object.size), gridDefine2(x,y, +object.size));
}
}
// trigger cell, initially null
m.variable(noObs).define("trigger", () => null);
return m;
}
Insert cell
Insert cell
function gridInputs2(x,y,max) {
const tag = `[${x},${y}]`;
const inp = [tag, 'mutable '+tag];
if (max>2) {
if (x > 0) inp.push(`mutable [${x-1},${y}]`);
if (y > 0) inp.push(`mutable [${x},${y-1}]`);
}
if (x < Lx2-1) inp.push(`mutable [${x+1},${y}]`);
if (y < Ly2-1) inp.push(`mutable [${x},${y+1}]`);
if (max>4) {
if (x>0 && y>0) inp.push(`mutable [${x-1},${y-1}]`);
if (x>0 && y<Ly2-1) inp.push(`mutable [${x-1},${y+1}]`);
if (x<Lx2-1 && y>0) inp.push(`mutable [${x+1},${y-1}]`);
if (x<Lx2-1 && y<Ly2-1) inp.push(`mutable [${x+1},${y+1}]`);
}
return inp;
}
Insert cell
function gridDefine2(x,y,max) {
return function (val, mval, ...marr) {
if (mval.value >= max) {
mval.value -= max;
for (let j=0; j<marr.length; j++)
marr[j].value++;
}
}
}
Insert cell
Insert cell
{
const tag = `mutable [${+object.size > 2 ? `${Math.floor(Lx2/2)},${Math.floor(Lx2/2)}` : '0,0'}]`;
if (!pause2) {
m2.redefine("trigger", [tag], function*(m00) {
while (true) {
yield m00.value++;
}
});
}
else m2.redefine("trigger", () => null);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
m2._scope
Insert cell
getAllPropertyNamesAsObj(m2._scope.get("mutable [10,10]"))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {getAllPropertyNamesAsObj} from '048035bf9a9c263e'
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more