Adding fertility parameter
[SugarCubes.git] / L8onWallace.pde
CommitLineData
f7f0bfe5 1class CubeState {
4861d6c4 2 // Index of cube in glucose.model.cubes
f7f0bfe5 3 public Integer index;
4861d6c4 4 // Boolean which describes if cube is alive.
f7f0bfe5 5 public boolean alive;
4861d6c4 6 // List of this cubes neighbors
f7f0bfe5
LW
7 public List<Integer> neighbors;
8
9 public CubeState(Integer index, boolean alive, List<Integer> neighbors) {
10 this.index = index;
11 this.alive = alive;
12 this.neighbors = neighbors;
13 }
14}
15
4861d6c4
LW
16class L8onLife extends SCPattern {
17 // Controls the rate of life algorithm ticks, in milliseconds
18 private BasicParameter rateParameter = new BasicParameter("RATE", 122.5, 0.0, 2000.0);
19 // Controls if the cubes should be randomized even if something changes. Set above 0.5 to randomize cube aliveness.
20 private BasicParameter randomParameter = new BasicParameter("RAND", 0.0);
21 // Controls the brightness of dead cubes.
22 private BasicParameter deadParameter = new BasicParameter("DEAD", 25.0, 0.0, 100.0);
23 // Controls the saturation.
24 private BasicParameter saturationParameter = new BasicParameter("SAT", 90.0, 0.0, 100.0);
61f677b8
LW
25 // Controls the number of neighbors needed to birth a new life.
26 private BasicParameter fertilityParameter = new BasicParameter("FERT", 2.0, 1.0, 5.0);
27
4861d6c4 28
64ed63c7
LW
29 public final double MIN_ALIVE_PROBABILITY = 0.2;
30 public final double MAX_ALIVE_PROBABILITY = 0.9;
4861d6c4 31
64ed63c7 32 private final SinLFO xPos = new SinLFO(0, model.xMax, 4500);
f7f0bfe5 33
4861d6c4
LW
34 // Contains the state of all cubes by index.
35 private List<CubeState> cube_states;
36 // Contains the amount of time since the last cycle of life.
37 private int time_since_last_run;
38 // Boolean describing if life changes were made during the current run.
39 private boolean any_changes_this_run;
64ed63c7
LW
40 // Hold the new lives
41 private List<Boolean> new_lives;
f7f0bfe5 42
4861d6c4 43 public L8onLife(GLucose glucose) {
f7f0bfe5 44 super(glucose);
4861d6c4
LW
45
46 //Print debug info about the cubes.
47 //outputCubeInfo();
48
f7f0bfe5
LW
49 initCubeStates();
50 time_since_last_run = 0;
51 any_changes_this_run = false;
64ed63c7 52 new_lives = new ArrayList<Boolean>();
4861d6c4
LW
53
54 addParameter(rateParameter);
55 addParameter(randomParameter);
56 addParameter(deadParameter);
61f677b8
LW
57 addParameter(saturationParameter);
58 addParameter(fertilityParameter);
f7f0bfe5 59 addModulator(xPos).trigger();
f7f0bfe5
LW
60 }
61
62 public void run(double deltaMs) {
63 Integer i = 0;
4861d6c4 64 CubeState cube_state;
f7f0bfe5 65
4861d6c4 66 any_changes_this_run = false;
64ed63c7 67 new_lives.clear();
f7f0bfe5
LW
68 time_since_last_run += deltaMs;
69
4861d6c4
LW
70 for (Cube cube : model.cubes) {
71 cube_state = this.cube_states.get(i);
64ed63c7 72
4861d6c4 73 if(shouldLightCube(cube_state)) {
f7f0bfe5
LW
74 lightLiveCube(cube);
75 } else {
76 lightDeadCube(cube);
4861d6c4
LW
77 }
78
79 i++;
f7f0bfe5
LW
80 }
81
4861d6c4
LW
82 boolean should_randomize_anyway = (randomParameter.getValuef() > 0.5);
83 if(should_randomize_anyway || !any_changes_this_run) {
f7f0bfe5 84 randomizeCubeStates();
64ed63c7
LW
85 } else {
86 applyNewLives();
f7f0bfe5
LW
87 }
88
4861d6c4
LW
89 if(time_since_last_run >= rateParameter.getValuef()) {
90 time_since_last_run = 0;
91 }
f7f0bfe5
LW
92 }
93
4861d6c4
LW
94 public void lightLiveCube(Cube cube) {
95 for (LXPoint p : cube.points) {
96 float hv = max(0, lx.getBaseHuef() - abs(p.x - xPos.getValuef()));
97 colors[p.index] = lx.hsb(
98 hv,
99 saturationParameter.getValuef(),
100 75
101 );
102 }
103 }
104
105 public void lightDeadCube(Cube cube) {
106 for (LXPoint p : cube.points) {
107 float hv = max(0, lx.getBaseHuef() - abs(p.x - xPos.getValuef()));
108 double dead_bright = deadParameter.getValuef() * Math.random();
109
110 colors[p.index] = lx.hsb(
111 hv,
112 saturationParameter.getValuef(),
113 dead_bright
114 );
115 }
116 }
117
f7f0bfe5
LW
118 public void outputCubeInfo() {
119 int i = 0;
120 for (Cube c : model.cubes) {
121 print("Cube " + i + ": " + c.x + "," + c.y + "," + c.z + "\n");
122 ++i;
123 }
124 print("Edgeheight: " + Cube.EDGE_HEIGHT + "\n");
125 print("Edgewidth: " + Cube.EDGE_WIDTH + "\n");
126 print("Channelwidth: " + Cube.CHANNEL_WIDTH + "\n");
127 }
128
129 private void initCubeStates() {
130 List<Integer> neighbors;
131 boolean alive = false;
132 CubeState cube_state;
133 this.cube_states = new LinkedList<CubeState>();
134 Integer i = 0;
135
136 for (Cube c : model.cubes) {
137 neighbors = findCubeNeighbors(c, i);
1c56d2f1 138 alive = true;
f7f0bfe5
LW
139 cube_state = new CubeState(i, alive, neighbors);
140 this.cube_states.add(cube_state);
141 ++i;
142 }
143 }
144
4861d6c4
LW
145 private void randomizeCubeStates() {
146 double prob_range = (1.0 - MIN_ALIVE_PROBABILITY) - (1.0 - MAX_ALIVE_PROBABILITY);
147 double prob = MIN_ALIVE_PROBABILITY + (prob_range * Math.random());
1c56d2f1 148
4861d6c4
LW
149 //print("Randomizing cubes! p = " + prob + "\n");
150
151 for (CubeState cube_state: this.cube_states) {
152 cube_state.alive = (Math.random() <= prob);
f7f0bfe5
LW
153 }
154 }
155
156 public List<Integer> findCubeNeighbors(Cube cube, Integer index) {
157 List<Integer> neighbors = new LinkedList<Integer>();
158 Integer i = 0;
159
160 for (Cube c : model.cubes) {
4861d6c4
LW
161 if(index != i) {
162 if(abs(c.x - cube.x) < (Cube.EDGE_WIDTH * 2) && abs(c.y - cube.y) < (Cube.EDGE_HEIGHT * 2)) {
163 //print("Cube " + i + " is a neighbor of " + index + "\n");
164 neighbors.add(i);
165 }
f7f0bfe5
LW
166 }
167
168 i++;
169 }
170
171 return neighbors;
172 }
173
4861d6c4
LW
174 public boolean shouldLightCube(CubeState cube_state) {
175 // Respect rate parameter.
176 if(time_since_last_run < rateParameter.getValuef()) {
177 any_changes_this_run = true;
64ed63c7 178 return cube_state.alive;
4861d6c4 179 } else {
64ed63c7
LW
180 boolean new_life = cycleOfLife(cube_state);
181 new_lives.add(new_life);
182 return new_life;
183 }
184 }
185
186 public void applyNewLives() {
187 int index = 0;
188 for(boolean liveliness: new_lives) {
189 CubeState cube_state = this.cube_states.get(index);
190 cube_state.alive = new_lives.get(index);
191 index++;
192 }
4861d6c4
LW
193 }
194
195 public boolean cycleOfLife(CubeState cube_state) {
196 Integer index = cube_state.index;
197 Integer alive_neighbor_count = countLiveNeighbors(cube_state);
f7f0bfe5 198 boolean before_alive = cube_state.alive;
64ed63c7 199 boolean after_alive = before_alive;
61f677b8 200 int fetility_count = (int) fertilityParameter.getValuef();
4861d6c4 201
f7f0bfe5
LW
202 if(cube_state.alive) {
203 if(alive_neighbor_count < 2 || alive_neighbor_count > 3) {
64ed63c7 204 after_alive = false;
f7f0bfe5 205 } else {
64ed63c7 206 after_alive = true;
f7f0bfe5
LW
207 }
208
209 } else {
61f677b8 210 if(alive_neighbor_count == fetility_count) {
64ed63c7 211 after_alive = true;
f7f0bfe5 212 } else {
64ed63c7 213 after_alive = false;
f7f0bfe5 214 }
64ed63c7
LW
215 }
216
217 if(before_alive != after_alive) {
4861d6c4 218 any_changes_this_run = true;
64ed63c7
LW
219 }
220
221 return after_alive;
f7f0bfe5
LW
222 }
223
224 public Integer countLiveNeighbors(CubeState cube_state) {
225 Integer count = 0;
226 CubeState neighbor_cube_state;
227
228 for(Integer neighbor_index: cube_state.neighbors) {
229 neighbor_cube_state = this.cube_states.get(neighbor_index);
230 if(neighbor_cube_state.alive) {
231 count++;
232 }
233 }
234
235 return count;
4861d6c4 236 }
f7f0bfe5 237}