Created
February 12, 2013 06:28
-
-
Save jordanorelli/4760619 to your computer and use it in GitHub Desktop.
conway's game of life, for the monome, written in chuck
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
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