{
   "labels" : [
      "gui",
      "algorithmic",
      "cellular automaton"
   ],
   "id" : "1-4Xf",
   "is_private" : null,
   "code" : "(\r\ns.waitForBoot({\r\n\r\nvar scale, width, height, n, space, w, cells, cellCreator, clearFunc;\r\n\r\n// sound source\r\nSynthDef(\\blip, {|freq|\r\n\tvar rel = TRand.kr(0.05,4,1);\r\n\tvar s = SinOsc.ar(freq*[0.9,0.99,1,1.01,1.1], mul:[0.05,0.2,1,0.2,0.05]).mean;\r\n\tvar e = EnvGen.ar(Env.perc(1e-4,rel,0.75), 1, doneAction:2) ** 4;\r\n\tOut.ar(0, s*e!2);\r\n}).add;\r\n\r\ns.sync;\r\n\r\nscale = (12*[3,4,5,6] +.x Scale.egyptian.degrees.flatten).midicps;\r\n\r\n// GUI\r\nwidth = 600;\r\nheight = width;\r\nn = 13;\r\nspace = width/n;\r\n\r\n// make a new window\r\nw = Window.new(\"automata\", Rect(100,100,width,height));\r\n\r\n// make empty data structure to hold program state\r\ncells = [];\r\n\r\ncellCreator = {|row, col, id|\r\n\tvar x = ();\r\n\tx.id = id;\r\n\tx.pos = [row, col];\r\n\tx.dir = [0,1];\r\n\r\n\tx.updateCell = {|self|\r\n\t\t// reverse direction if at boundary\r\n\t\tif( self.pos[0]==n && self.dir[0]==1, {\r\n\t\t\tself.dir[0] = -1;\r\n\t\t\tSynth(\\blip, [\\freq, scale.wrapAt(self.pos[1]-1)]);\r\n\t\t});\r\n\t\tif( self.pos[1]==n && self.dir[1]==1, {\r\n\t\t\tself.dir[1] = -1;\r\n\t\t\tSynth(\\blip, [\\freq, scale.wrapAt(12*self.pos[0]-1)]);\r\n\t\t});\r\n\t\tif( self.pos[0]==1 && self.dir[0] == -1, {\r\n\t\t\tself.dir[0] = 1;\r\n\t\t\tSynth(\\blip, [\\freq, scale.wrapAt(24*self.pos[1]-1)]);\r\n\t\t});\r\n\t\tif( self.pos[1]==1 && self.dir[1] == -1, {\r\n\t\t\tself.dir[1] = 1;\r\n\t\t\tSynth(\\blip, [\\freq, scale.wrapAt(36*self.pos[0]-1)]);\r\n\t\t});\r\n\r\n\t\t// advance position\r\n\t\tself.pos = self.pos + self.dir;\r\n\t};\r\n\r\n\tx.changeDir = {|self|\r\n\t\tswitch(self.dir,\r\n\t\t\t[1,0], {self.dir = [0,-1]},\r\n\t\t\t[-1,0], {self.dir = [0,1]},\r\n\t\t\t[0,1], {self.dir = [-1,0]},\r\n\t\t\t[0,-1], {self.dir = [1,0]}\r\n\t\t);\r\n\t};\r\n\r\n\tx;\r\n};\r\n\r\nclearFunc = {\r\n\tn.do{|i|\r\n\t\tn.do{|j|\r\n\t\t\tb[i][j].value=0;\r\n\t\t};\r\n\t};\r\n};\r\n\r\n\r\n// fill window with buttons\r\nb =\r\nn.collect{|i|\r\n\tn.collect{|j|\r\n\t\tButton(w, Rect(space*i, space*j, space, space))\r\n\t\t.states_([[\"\", Color.black, Color.gray], [\"\", Color.black, Color.red]])\r\n\t\t.action_({|x|\r\n\t\t\tif(x.value == 1, {\r\n\t\t\t\tcells = cells ++ [cellCreator.value(i+1, j+1, cells.size)];\r\n\t\t\t})\r\n\t\t});\r\n\t}\r\n};\r\n\r\nw.drawFunc = Routine{\r\n\tloop{\r\n\t\tvar ncells = cells.size;\r\n\r\n\t\tclearFunc.();\r\n\r\n\t\t// update cells\r\n\t\tcells.do{ |x|\r\n\t\t\tx.updateCell;\r\n\t\t\tb[x.pos[0]-1][x.pos[1]-1].value = 1;\r\n\t\t};\r\n\r\n\t\t// check for collision paths\r\n\t\t(ncells-1).do{|i|\r\n\t\t\t(ncells-i-1).do{|j|\r\n\t\t\t\tvar k = j+i+1;\r\n\t\t\t\tif( (cells[i].pos == cells[k].pos) && (cells[i].dir == (-1*cells[k].dir)),\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcells[i].changeDir;\r\n\t\t\t\t\t\tcells[k].changeDir;\r\n\t\t\t\t\t}\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t};\r\n\t\t//\"------------------\".postln;\r\n\t\t0.yield;\r\n\t}\r\n};\r\n\r\n{ while { w.isClosed.not } { w.refresh; 0.125.wait; } }.fork(AppClock);\r\n\r\nw.front;\r\n\r\n}.fork(AppClock);\r\n);\r\n\r\n)",
   "name" : "Cellular Automaton",
   "author" : "coreyker",
   "ancestor_list" : [],
   "description" : "A 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:\r\n\r\n1. Initially move downward;\r\n2. If a boundary is encountered or if two agents collide, reverse direction;\r\n3. If two agents attempt to occupy the same cell, each agent should rotate 90 degrees to its left and continue moving\r\n\r\nSound is generated whenever an agent encounters a boundary, and different boundary locations correspond to different notes on a scale"
}
