Last active
August 23, 2016 16:39
-
-
Save danielmewes/196f4e2d85fc7bb02522678bc6082a88 to your computer and use it in GitHub Desktop.
Conway's Game of Life in ReQL
This file contains 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
// This version precomputes neighbors in order to be more efficient | |
var initial = [ | |
"........................O...........", | |
"......................O.O...........", | |
"............OO......OO............OO", | |
"...........O...O....OO............OO", | |
"OO........O.....O...OO..............", | |
"OO........O...O.OO....O.O...........", | |
"..........O.....O.......O...........", | |
"...........O...O....................", | |
"............OO......................", | |
"....................................", | |
"...................................." | |
]; | |
var importGrid = (grid) => { | |
return grid.map( (line) => line.split("").map( (cell) => cell.eq(".").branch(0, 1) ) ); | |
}; | |
var printGrid = (grid) => { | |
return grid.fold("", (out, line) => out.add(line.fold("", (prefix, cell) => prefix.add(cell.eq(1).branch("#", " ")) ).add("\n")) ); | |
}; | |
var computeNeighbors = (grid, i, j) => { | |
var left = r.max([0, j.sub(1)]); | |
var right = r.min([grid(0).count().sub(1), j.add(1)]); | |
var up = r.max([0, i.sub(1)]); | |
var down = r.min([grid.count().sub(1), i.add(1)]); | |
// Generate all neighbors as pairs of [ni, nj] != [i, j] | |
return | |
r.range(up, down.add(1)) | |
.concatMap((ni) => | |
r.range(left, right.add(1)) | |
.map( (nj) => [ni, nj] ) | |
).filter( (pair) => pair.ne([i, j]) ) | |
.coerceTo('array'); | |
}; | |
// Precomputes an auxiliary grid where each cell has a list of | |
// neighbor coordinates. This is for better performance. | |
var precomputeNeighbors = (grid) => { | |
return grid.do( (grid) => | |
r.range(grid.count()) | |
.fold([], (out, i) => { | |
return out.append( r.range(grid(i).count()).fold([], (prefix, j) => { | |
return prefix.append(computeNeighbors(grid, i, j)); | |
})); | |
}) | |
); | |
}; | |
var evalCell = (state, i, j) => { | |
var neighbors = state('neighborGrid')(i)(j); | |
var liveNeighbors = neighbors.map( (pair) => state('grid')(pair(0))(pair(1)) ).sum(); | |
return liveNeighbors.do( (n) => | |
// A dead cell becomes alive if 3 neighbors are alive, | |
// A life cell stays alive if it has 2-3 neighbors | |
state('grid')(i)(j).eq(0).branch( | |
n.eq(3), | |
n.gt(1).and(n.lt(4)) | |
) | |
).branch(1, 0); | |
}; | |
// The changefeed on the table `trigger` acts as a trigger to generate | |
// the next generation. This is so that you can step through the generations | |
// in single steps by changing something in `trigger` (the actual change | |
// doesn't matter). Or you can run a small script that performs a write every | |
// n milliseconds to get a nice animation. | |
r.table('trigger').changes().fold( | |
{ grid: importGrid(r.expr(initial)), neighborGrid: precomputeNeighbors(importGrid(r.expr(initial))) }, | |
(state, _ignored) => { | |
return { | |
grid: r.range(state('grid').count()) | |
.map( (i) => { | |
return r.range(state('grid')(i).count()).map( (j) => evalCell(state, i, j) ).coerceTo('array'); | |
}) | |
.coerceTo('array'), | |
neighborGrid: state('neighborGrid') | |
}; | |
}, | |
{ | |
emit: (oldState, _ignored, newState) => [printGrid(oldState('grid'))] | |
} | |
) |
This file contains 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
var initial = [ | |
" * ", | |
" * * ", | |
" ** ", | |
" ", | |
" ", | |
" ", | |
" " | |
]; | |
var importGrid = (grid) => { | |
return grid.map( (line) => line.split("").map( (cell) => cell.eq(" ").branch(0, 1) ) ); | |
}; | |
var printGrid = (grid) => { | |
return grid.fold("", (out, line) => out.add(line.fold("", (prefix, cell) => prefix.add(cell.eq(1).branch("#", " ")) ).add("\n")) ); | |
}; | |
var evalCell = (grid, i, j) => { | |
var left = r.max([0, j.sub(1)]); | |
var right = r.min([grid(0).count().sub(1), j.add(1)]); | |
var up = r.max([0, i.sub(1)]); | |
var down = r.min([grid.count().sub(1), i.add(1)]); | |
// Generate all neighbors as pairs of [ni, nj] != [i, j] | |
var neighbors = | |
r.range(up, down.add(1)) | |
.concatMap((ni) => | |
r.range(left, right.add(1)) | |
.map( (nj) => [ni, nj] ) | |
).filter( (pair) => pair.ne([i, j]) ); | |
var liveNeighbors = neighbors.map( (pair) => grid(pair(0))(pair(1)) ).sum(); | |
return liveNeighbors.do( (n) => | |
// A dead cell becomes alive if 3 neighbors are alive, | |
// A life cell stays alive if it has 2-3 neighbors | |
grid(i)(j).eq(0).branch( | |
n.eq(3), | |
n.gt(1).and(n.lt(4)) | |
) | |
).branch(1, 0); | |
}; | |
/* | |
The changefeed on the table `trigger` acts as a trigger to generate | |
the next generation. This is so that you can step through the generations | |
in single steps by changing something in `trigger` (the actual change | |
doesn't matter). Or you can run a small script that performs a write every | |
n milliseconds to get a nice animation. | |
*/ | |
r.table('trigger').changes().fold( | |
importGrid(r.expr(initial)), | |
(grid, _ignored) => { | |
return r.range(grid.count()) | |
.map( (i) => { | |
return r.range(grid(i).count()).map( (j) => evalCell(grid, i, j) ).coerceTo('array'); | |
}) | |
.coerceTo('array'); | |
}, | |
{ | |
emit: (oldGrid, _ignored, newGrid) => [printGrid(oldGrid)] | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment