Skip to content

Instantly share code, notes, and snippets.

@jordanorelli
Created February 12, 2013 06:28
Show Gist options
  • Save jordanorelli/4760619 to your computer and use it in GitHub Desktop.
Save jordanorelli/4760619 to your computer and use it in GitHub Desktop.
conway's game of life, for the monome, written in chuck
110.0 => float rootFreq;
20 => float toneSteps;
10::ms => dur genStep;
24787 => int monomeIn;
17102 => int monomeOut;
"/example" => string monomePrefix;
"127.0.0.1" => string monomeHost;
fun float[][] toneGrid(int width, int height, int columnStep, int rowStep, float baseFreq, float octaveSteps) {
float tones[width][height];
Math.pow(2, 1.0/ octaveSteps $ float) => float toneStep;
for(0 => int row; row < height; row++) {
baseFreq * Math.pow(toneStep, row * rowStep) => float rowBase;
for(0 => int col; col < width; col++) {
rowBase * Math.pow(toneStep, col * columnStep)
=> tones[col][row];
}
}
return tones;
}
class Monome {
string prefix;
string hostname;
OscRecv recv;
OscSend snd;
// PROTECTED FINAL
//
// Initialization: sets up the monome and listens for incoming messages.
fun void init(string _prefix, string _hostname, int in, int out) {
preInit();
_prefix => prefix;
_hostname => hostname;
OscSend m;
m.setHost(hostname, out);
m @=> snd;
in => recv.port;
recv.listen();
spork ~ keyListener();
spork ~ tiltListener();
all(0);
postInit();
}
// PUBLIC
//
// preInit is called before the init cycle finishes.
//
fun void preInit() {}
// PUBLIC
//
// postInit is called after the init cycle finishes.
//
fun void postInit() {}
// PUBLIC
//
// key is called once for every time a key is pressed on the Monome.
// Override this method in a child class to define how to handle key
// presses from the monome.
fun void key(int x, int y, int z) {
<<< prefix, "key", x, y, z >>>;
}
// PUBLIC
//
// position change on tilt sensor n, integer (8-bit) values (x, y, z).
// This method is called once for each OSC message received from the monome
// with regards to its tilt.
fun void tilt(int n, int x, int y, int z) {
<<< prefix, "tilt", n, x, y, z >>>;
}
// PRIVATE
//
// key press handler. This is invoked automatically by the Monome
// constructor, and should not be called. This method starts an infinite
// loop and listens for incoming osc messages from the monome. When
// messages are received, the monome object's "key" method is called.
//
fun void keyListener() {
recv.event(prefix+"/grid/key", "iii") @=> OscEvent e;
while(true) {
e => now;
while(e.nextMsg() != 0) {
e.getInt() => int x;
e.getInt() => int y;
e.getInt() => int z;
key(x, y, z);
}
}
}
// PRIVATE
//
// tilt sensor handler. This is invoked automatically by the Monome
// constructor, and should not be called. This method starts an infinite
// loop and listens for incoming osc messages from the monome. When
// messages are received, the monome object's "tilt" method is called.
//
fun void tiltListener() {
recv.event(prefix+"/tilt", "iiii") @=> OscEvent e;
while(true) {
e => now;
while(e.nextMsg() != 0) {
e.getInt() => int n;
e.getInt() => int x;
e.getInt() => int y;
e.getInt() => int z;
tilt(n, x, y, z);
}
}
}
// PROTECTED FINAL
//
// set led at (x,y) to state s (0 or 1).
//
fun void set(int x, int y, int s) {
snd.startMsg(prefix+"/grid/led/set", "iii");
snd.addInt(x);
snd.addInt(y);
snd.addInt(s);
me.yield();
}
// PROTECTED FINAL
//
// set all leds to state s (0 or 1).
//
fun void all(int i) {
snd.startMsg(prefix+"/grid/led/all", "i");
snd.addInt(i);
me.yield();
}
}
class Cell {
float freq;
SinOsc s => ADSR env => dac;
int x;
int y;
false => int state;
false => int nextState;
false => int highlife;
Cell @ neighbors[8];
Monome @ grid;
0.1 => s.gain;
env.keyOff();
env.set(20::ms, 100::ms, 0.7, 20::ms);
fun void init(float f, Monome @ _grid, int _x, int _y) {
f => s.freq;
_grid @=> grid;
_x => x;
_y => y;
}
fun void on() {
true => state;
env.keyOn();
grid.set(x, y, state);
}
fun void off() {
false => state;
env.keyOff();
grid.set(x, y, state);
}
fun void toggle() {
if(state) {
off();
} else {
on();
}
}
fun void setState(int targetState) {
if(state != targetState) {
toggle();
}
}
fun int countNeighbors() {
0 => int count;
for(0 => int i; i < 8; i++) {
if(neighbors[i].state) {
count++;
}
}
return count;
}
fun void getNextState() {
countNeighbors() => int neighborcount;
if(state) {
neighborcount == 2 || neighborcount == 3 => nextState;
} else {
if(highlife) {
neighborcount == 3 || neighborcount == 6 => nextState;
} else {
neighborcount == 3 => nextState;
}
}
}
fun void pushNextState() {
if(nextState != state) {
if(nextState) {
on();
} else {
off();
}
}
nextState => state;
}
fun void toggleHighlife() {
if(highlife) {
false => highlife;
} else {
true => highlife;
}
}
}
class Life extends Monome {
float tones[8][8];
Cell @ cells[8][8];
false => int running;
false => int highlife;
int saveState[8][8];
fun void preInit() {
toneGrid(8, 8, 8, 1, rootFreq, toneSteps) @=> tones;
for(0 => int x; x < 8; x++) {
for(0 => int y; y < 8; y++) {
new Cell @=> Cell cell;
cell.init(tones[x][y], this, x, y);
cell @=> cells[x][y];
}
}
for(0 => int x; x < 8; x++) {
for(0 => int y; y < 8; y++) {
setNeighbors(x, y);
}
}
}
fun void setNeighbors(int x, int y) {
wrap(x - 1) => int left_x;
wrap(x + 1) => int right_x;
wrap(y - 1) => int up_y;
wrap(y + 1) => int down_y;
[
cells[left_x][up_y],
cells[x][up_y],
cells[right_x][up_y],
cells[left_x][y],
cells[right_x][y],
cells[left_x][down_y],
cells[x][down_y],
cells[right_x][down_y]
] @=> cells[x][y].neighbors;
}
fun void postInit() {
all(0);
spork ~ live();
}
fun void live() {
true => running;
while(running) {
step();
genStep => now;
}
}
fun void pause() {
if(running) {
<<< "pause" >>>;
false => running;
} else {
<<< "unpause" >>>;
spork ~ live();
}
}
fun void manualStep() {
if(running) {
pause();
}
step();
}
fun void step() {
for(0 => int i; i < 8; i++) {
for(0 => int j; j < 8; j++) {
cells[i][j].getNextState();
}
}
for(0 => int i; i < 8; i++) {
for(0 => int j; j < 8; j++) {
cells[i][j].pushNextState();
}
}
}
fun void play(int x, int y) {
SinOsc s => ADSR env => dac;
0.1 => s.gain;
tones[x][y] => s.freq;
env.set(20::ms, 100::ms, 0.7, 20::ms);
env.keyOn();
40::ms => now;
env.keyOff();
40::ms => now;
}
fun int wrap(int x) {
if(x > 7) return 0;
if(x < 0) return 7;
return x;
}
fun int countNeighbors(int x, int y) {
return cells[x][y].countNeighbors();
}
fun void key(int x, int y, int z) {
if(z == 0) {
return;
}
toggle(x, y);
}
fun void toggle(int x, int y) {
cells[x][y].toggle();
}
fun void save() {
for(0 => int i; i < 8; i++) {
for(0 => int j; j < 8; j++) {
cells[i][j].state => saveState[i][j];
}
}
}
fun void load() {
for(0 => int i; i < 8; i++) {
for(0 => int j; j < 8; j++) {
cells[i][j].setState(saveState[i][j]);
}
}
}
fun void clear() {
for(0 => int i; i < 8; i++) {
for(0 => int j; j < 8; j++) {
cells[i][j].setState(false);
}
}
}
fun void toggleHighlife() {
if(highlife) {
false => highlife;
<<< "highlife off" >>>;
} else {
true => highlife;
<<< "highlife on" >>>;
}
for(0 => int i; i < 8; i++) {
for(0 => int j; j < 8; j++) {
cells[i][j].toggleHighlife();
}
}
}
}
new Life @=> Life m;
m.init(monomePrefix, monomeHost, monomeIn, monomeOut);
fun void keyboardHandler() {
Hid hi;
HidMsg msg;
if(!hi.openKeyboard(0)) {
<<< "can't open keyboard" >>>;
return;
}
while(true) {
hi => now;
while(hi.recv(msg)) {
if(msg.isButtonDown()) {
if(msg.which == 40) {
m.pause();
} else if(msg.which == 44) {
<<< "step" >>>;
m.manualStep();
} else if(msg.which == 22) {
// "s" to save
<<< "save" >>>;
m.save();
} else if(msg.which == 15) {
// "l" to load
<<< "load" >>>;
m.load();
} else if(msg.which == 6) {
// "c" to clear
<<< "clear" >>>;
m.clear();
} else if(msg.which == 11) {
// "h" to toggle highlife
m.toggleHighlife();
} else {
<<< msg.which >>>;
}
}
}
}
}
spork ~ keyboardHandler();
while(true) { 1::second => now; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment