«Cellular Automaton» by coreyker
on 10 Mar'15 07:48 inA supercollider implementation of algorithmic composition using cellular automaton (please see: http://www.earslap.com/page/otomata.html for my original inspiration). This code displays a grid of buttons, which can be clicked to generate new agents which behave according to a simple set of rules, that can lead to interesting emergent patterns:
- Initially move downward;
- If a boundary is encountered or if two agents collide, reverse direction;
- If two agents attempt to occupy the same cell, each agent should rotate 90 degrees to its left and continue moving
Sound is generated whenever an agent encounters a boundary, and different boundary locations correspond to different notes on a scale
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
( s.waitForBoot({ var scale, width, height, n, space, w, cells, cellCreator, clearFunc; // sound source SynthDef(\blip, {|freq| var rel = TRand.kr(0.05,4,1); var s = SinOsc.ar(freq*[0.9,0.99,1,1.01,1.1], mul:[0.05,0.2,1,0.2,0.05]).mean; var e = EnvGen.ar(Env.perc(1e-4,rel,0.75), 1, doneAction:2) ** 4; Out.ar(0, s*e!2); }).add; s.sync; scale = (12*[3,4,5,6] +.x Scale.egyptian.degrees.flatten).midicps; // GUI width = 600; height = width; n = 13; space = width/n; // make a new window w = Window.new("automata", Rect(100,100,width,height)); // make empty data structure to hold program state cells = []; cellCreator = {|row, col, id| var x = (); x.id = id; x.pos = [row, col]; x.dir = [0,1]; x.updateCell = {|self| // reverse direction if at boundary if( self.pos[0]==n && self.dir[0]==1, { self.dir[0] = -1; Synth(\blip, [\freq, scale.wrapAt(self.pos[1]-1)]); }); if( self.pos[1]==n && self.dir[1]==1, { self.dir[1] = -1; Synth(\blip, [\freq, scale.wrapAt(12*self.pos[0]-1)]); }); if( self.pos[0]==1 && self.dir[0] == -1, { self.dir[0] = 1; Synth(\blip, [\freq, scale.wrapAt(24*self.pos[1]-1)]); }); if( self.pos[1]==1 && self.dir[1] == -1, { self.dir[1] = 1; Synth(\blip, [\freq, scale.wrapAt(36*self.pos[0]-1)]); }); // advance position self.pos = self.pos + self.dir; }; x.changeDir = {|self| switch(self.dir, [1,0], {self.dir = [0,-1]}, [-1,0], {self.dir = [0,1]}, [0,1], {self.dir = [-1,0]}, [0,-1], {self.dir = [1,0]} ); }; x; }; clearFunc = { n.do{|i| n.do{|j| b[i][j].value=0; }; }; }; // fill window with buttons b = n.collect{|i| n.collect{|j| Button(w, Rect(space*i, space*j, space, space)) .states_([["", Color.black, Color.gray], ["", Color.black, Color.red]]) .action_({|x| if(x.value == 1, { cells = cells ++ [cellCreator.value(i+1, j+1, cells.size)]; }) }); } }; w.drawFunc = Routine{ loop{ var ncells = cells.size; clearFunc.(); // update cells cells.do{ |x| x.updateCell; b[x.pos[0]-1][x.pos[1]-1].value = 1; }; // check for collision paths (ncells-1).do{|i| (ncells-i-1).do{|j| var k = j+i+1; if( (cells[i].pos == cells[k].pos) && (cells[i].dir == (-1*cells[k].dir)), { cells[i].changeDir; cells[k].changeDir; } ) } }; //"------------------".postln; 0.yield; } }; { while { w.isClosed.not } { w.refresh; 0.125.wait; } }.fork(AppClock); w.front; }.fork(AppClock); ); )
reception
Very nice!
nice work
Really cool! I've put some 10+ agents in and am now happily listening to some nice patterns indeed :D