1 | /** |
---|
2 | * @file Simulation Module |
---|
3 | * @author Patryk Gliszczynski, with further modifications |
---|
4 | * @version 1.0 |
---|
5 | */ |
---|
6 | |
---|
7 | class Simulation { |
---|
8 | /** |
---|
9 | * Class reponsible for the control of simulation flow. |
---|
10 | */ |
---|
11 | |
---|
12 | constructor(world) { |
---|
13 | this.world = world; |
---|
14 | |
---|
15 | this.genPoolText = this.getText("Genotype Pool", EVOLUTION_METHOD_TEXT_POSITION); |
---|
16 | this.mutationText = this.getText("Mutation", EVOLUTION_METHOD_TEXT_POSITION); |
---|
17 | this.crossoverText = this.getText("Crossover", EVOLUTION_METHOD_TEXT_POSITION); |
---|
18 | this.cloningText = this.getText("Cloning", EVOLUTION_METHOD_TEXT_POSITION); |
---|
19 | this.mutatedText = this.getText("Mutated", EVOLUTION_METHOD_TEXT_POSITION); |
---|
20 | this.offspringText = this.getText("Offspring", EVOLUTION_METHOD_TEXT_POSITION); |
---|
21 | this.clonedText = this.getText("Cloned", EVOLUTION_METHOD_TEXT_POSITION); |
---|
22 | this.fitnessText = this.getText("Fitness", Config.Simulation.Element.Position.FITNESS_TEXT); |
---|
23 | |
---|
24 | this.world.scene.add(this.genPoolText); |
---|
25 | this.world.scene.add(this.mutationText); |
---|
26 | this.world.scene.add(this.crossoverText); |
---|
27 | this.world.scene.add(this.cloningText); |
---|
28 | this.world.scene.add(this.mutatedText); |
---|
29 | this.world.scene.add(this.offspringText); |
---|
30 | this.world.scene.add(this.clonedText); |
---|
31 | this.world.scene.add(this.fitnessText); |
---|
32 | |
---|
33 | this.state = State.INITIAL; |
---|
34 | this.timing = Config.Simulation.Timing.INITIAL; |
---|
35 | } |
---|
36 | |
---|
37 | run() { |
---|
38 | switch (this.state) { |
---|
39 | |
---|
40 | case State.INITIAL: |
---|
41 | this.showText(this.genPoolText); |
---|
42 | |
---|
43 | this.state = State.EVOLUTION_METHOD_SELECTION; |
---|
44 | this.timing = Config.Simulation.Timing.INITIAL; |
---|
45 | break; |
---|
46 | |
---|
47 | case State.EVOLUTION_METHOD_SELECTION: |
---|
48 | this.moveCamera(Config.Simulation.Camera.Position.TABLE); |
---|
49 | |
---|
50 | this.timing = Config.Simulation.Timing.EVOLUTION_METHOD_SELECTION; |
---|
51 | let randomVal = Math.random() |
---|
52 | if (randomVal < Config.Simulation.MUTATION_PROBABILITY) { |
---|
53 | this.state = State.MUTATION_GENOTYPE_SELECTION; |
---|
54 | } else if (randomVal < Config.Simulation.MUTATION_PROBABILITY + |
---|
55 | Config.Simulation.CROSSOVER_PROBABILITY){ |
---|
56 | this.state = State.CROSSOVER_GENOTYPE_SELECTION; |
---|
57 | } else { |
---|
58 | this.state = State.CLONING_GENOTYPE_SELECTION; |
---|
59 | } |
---|
60 | this.evolutionMethod = this.state; |
---|
61 | break; |
---|
62 | |
---|
63 | case State.MUTATION_GENOTYPE_SELECTION: |
---|
64 | this.showText(this.mutationText) |
---|
65 | this.hideText(this.genPoolText); |
---|
66 | |
---|
67 | this.mutationIdx = parseInt(Math.random() * Config.Simulation.GENOTYPES.length); |
---|
68 | this.mutationArrow = this.getArrow(this.mutationIdx).mesh; |
---|
69 | this.world.scene.add(this.mutationArrow); |
---|
70 | |
---|
71 | this.timing = Config.Simulation.Timing.GENOTYPE_SELECTION; |
---|
72 | this.state = State.MUTATION_RESULT; |
---|
73 | break; |
---|
74 | |
---|
75 | case State.MUTATION_RESULT: |
---|
76 | this.evolvedGenotype = this.getMutatedGenotype(this.world.table.genotypes[this.mutationIdx]); |
---|
77 | this.evolvedFitness = Math.max(0, (parseFloat(this.world.table.fitnessPlanks[this.mutationIdx].fitness) + |
---|
78 | (Math.random() * 20 - 10)).toFixed(2)); |
---|
79 | this.showEvolutionResult(); |
---|
80 | |
---|
81 | this.timing = Config.Simulation.Timing.EVOLUTION_RESULT; |
---|
82 | this.state = State.SHOW_FRAMSTICK; |
---|
83 | break; |
---|
84 | |
---|
85 | case State.MUTATION_RETURN: |
---|
86 | this.moveCamera(Config.Simulation.Camera.Position.TABLE); |
---|
87 | this.evolutionReturn() |
---|
88 | |
---|
89 | this.hideText(this.mutationText); |
---|
90 | this.hideText(this.mutatedText); |
---|
91 | this.world.scene.remove(this.mutationArrow); |
---|
92 | this.world.scene.remove(this.evolvedFramstick); |
---|
93 | |
---|
94 | this.timing = Config.Simulation.Timing.EVOLUTION_RETURN; |
---|
95 | this.state = State.INITIAL; |
---|
96 | break; |
---|
97 | |
---|
98 | case State.CLONING_GENOTYPE_SELECTION: |
---|
99 | this.showText(this.cloningText); |
---|
100 | this.hideText(this.genPoolText); |
---|
101 | |
---|
102 | this.cloningIdx = parseInt(Math.random() * Config.Simulation.GENOTYPES.length); |
---|
103 | this.cloningArrow = this.getArrow(this.cloningIdx).mesh; |
---|
104 | this.world.scene.add(this.cloningArrow); |
---|
105 | |
---|
106 | this.timing = Config.Simulation.Timing.GENOTYPE_SELECTION; |
---|
107 | this.state = State.CLONING_RESULT; |
---|
108 | break; |
---|
109 | |
---|
110 | case State.CLONING_RESULT: |
---|
111 | this.evolvedGenotype = this.world.table.genotypes[this.cloningIdx]; |
---|
112 | this.evolvedFitness = Math.max(0, (parseFloat(this.world.table.fitnessPlanks[this.cloningIdx].fitness) + |
---|
113 | (Math.random() * 2 - 1)).toFixed(2)); |
---|
114 | this.showEvolutionResult(); |
---|
115 | |
---|
116 | this.timing = Config.Simulation.Timing.EVOLUTION_RESULT; |
---|
117 | this.state = State.SHOW_FRAMSTICK; |
---|
118 | break; |
---|
119 | |
---|
120 | case State.CLONING_RETURN: |
---|
121 | this.moveCamera(Config.Simulation.Camera.Position.TABLE); |
---|
122 | this.evolutionReturn() |
---|
123 | |
---|
124 | this.hideText(this.cloningText); |
---|
125 | this.hideText(this.clonedText); |
---|
126 | this.world.scene.remove(this.cloningArrow); |
---|
127 | this.world.scene.remove(this.evolvedFramstick); |
---|
128 | |
---|
129 | this.timing = Config.Simulation.Timing.EVOLUTION_RETURN; |
---|
130 | this.state = State.INITIAL; |
---|
131 | break; |
---|
132 | |
---|
133 | case State.CROSSOVER_GENOTYPE_SELECTION: |
---|
134 | this.showText(this.crossoverText) |
---|
135 | this.hideText(this.genPoolText); |
---|
136 | |
---|
137 | this.crossoverIdx1 = parseInt(Math.random() * Config.Simulation.GENOTYPES.length); |
---|
138 | do { |
---|
139 | this.crossoverIdx2 = parseInt(Math.random() * Config.Simulation.GENOTYPES.length); |
---|
140 | } while (this.crossoverIdx1 == this.crossoverIdx2); |
---|
141 | |
---|
142 | this.crossoverArrow1 = this.getArrow(this.crossoverIdx1).mesh; |
---|
143 | this.world.scene.add(this.crossoverArrow1); |
---|
144 | this.crossoverArrow2 = this.getArrow(this.crossoverIdx2).mesh; |
---|
145 | this.world.scene.add(this.crossoverArrow2); |
---|
146 | |
---|
147 | this.timing = Config.Simulation.Timing.GENOTYPE_SELECTION; |
---|
148 | this.state = State.CROSSOVER_RESULT; |
---|
149 | break; |
---|
150 | |
---|
151 | case State.CROSSOVER_RESULT: |
---|
152 | this.evolvedGenotype = this.getCrossoveredGenotype(this.world.table.genotypes[this.crossoverIdx1], |
---|
153 | this.world.table.genotypes[this.crossoverIdx2]); |
---|
154 | this.evolvedFitness = ((parseFloat(this.world.table.fitnessPlanks[this.crossoverIdx1].fitness) + |
---|
155 | parseFloat(this.world.table.fitnessPlanks[this.crossoverIdx2].fitness)) / 2).toFixed(2); |
---|
156 | this.showEvolutionResult(); |
---|
157 | |
---|
158 | this.timing = Config.Simulation.Timing.EVOLUTION_RESULT; |
---|
159 | this.state = State.SHOW_FRAMSTICK; |
---|
160 | break; |
---|
161 | |
---|
162 | case State.CROSSOVER_RETURN: |
---|
163 | this.moveCamera(Config.Simulation.Camera.Position.TABLE); |
---|
164 | this.evolutionReturn() |
---|
165 | |
---|
166 | this.hideText(this.crossoverText); |
---|
167 | this.hideText(this.offspringText); |
---|
168 | this.world.scene.remove(this.crossoverArrow1); |
---|
169 | this.world.scene.remove(this.crossoverArrow2); |
---|
170 | this.world.scene.remove(this.evolvedFramstick.mesh); |
---|
171 | |
---|
172 | this.timing = Config.Simulation.Timing.EVOLUTION_RETURN; |
---|
173 | this.state = State.INITIAL; |
---|
174 | break; |
---|
175 | |
---|
176 | case State.SHOW_FRAMSTICK: |
---|
177 | this.moveCamera(Config.Simulation.Camera.Position.FRAMSTICK); |
---|
178 | |
---|
179 | this.evolvedFramstick = new Framstick(this.evolvedGenotype); |
---|
180 | this.world.scene.add(this.evolvedFramstick.mesh); |
---|
181 | |
---|
182 | this.timing = Config.Simulation.Timing.SHOW_FRAMSTICK; |
---|
183 | this.state = State.DESTROY_FRAMSTICK; |
---|
184 | break; |
---|
185 | |
---|
186 | case State.DESTROY_FRAMSTICK: |
---|
187 | this.evolvedFramstick.scaleUpSize = 0.01; |
---|
188 | this.evolvedFramstick.scaleDownSize = 0.01; |
---|
189 | |
---|
190 | this.showText(this.fitnessText); |
---|
191 | this.rotateText(this.fitnessText, Config.Simulation.Element.Angle.SIDE_VIEW); |
---|
192 | this.evolvedFitnessPlank = new FitnessPlank(this.evolvedFitness, 10); |
---|
193 | this.evolvedFitnessPlank.mesh.position.set(0, 0, 0); |
---|
194 | this.evolvedFitnessPlank.textMesh.position.set(0, 0, 0); |
---|
195 | this.evolvedFitnessPlank.move(Config.Simulation.Element.Position.EVOLVED_FITNESS_PLANK); |
---|
196 | this.evolvedFitnessPlank.rotate(Config.Simulation.Element.Angle.SIDE_VIEW); |
---|
197 | this.world.scene.add(this.evolvedFitnessPlank.mesh) |
---|
198 | this.world.scene.add(this.evolvedFitnessPlank.textMesh) |
---|
199 | |
---|
200 | this.timing = Config.Simulation.Timing.DESTROY_FRAMSTICK; |
---|
201 | if (this.evolutionMethod == State.MUTATION_GENOTYPE_SELECTION) { |
---|
202 | this.state = State.MUTATION_RETURN; |
---|
203 | } else if (this.evolutionMethod == State.CROSSOVER_GENOTYPE_SELECTION) { |
---|
204 | this.state = State.CROSSOVER_RETURN; |
---|
205 | } else if (this.evolutionMethod == State.CLONING_GENOTYPE_SELECTION) { |
---|
206 | this.state = State.CLONING_RETURN; |
---|
207 | } |
---|
208 | break; |
---|
209 | } |
---|
210 | |
---|
211 | setTimeout(this.run.bind(this), this.timing); |
---|
212 | } |
---|
213 | |
---|
214 | getMutatedGenotype(genotype) { |
---|
215 | let genoF4 = new Module.Geno_f4(); |
---|
216 | let genoOperatorsHelper = new Module.GenoOperatorsHelper(genoF4); |
---|
217 | let genetics = new Module.PreconfiguredGenetics(); |
---|
218 | let iteration = 0; |
---|
219 | |
---|
220 | do { |
---|
221 | if (genoOperatorsHelper.mutate(genotype.replace("/*4*/", "")) == 0) { |
---|
222 | let mutated = genoOperatorsHelper.getLastMutateGeno(); |
---|
223 | let newGenotype = "/*4*/ " + mutated; |
---|
224 | |
---|
225 | let stringObj = new Module.SString(); |
---|
226 | stringObj.set(newGenotype); |
---|
227 | let genoObj = new Module.Geno(stringObj); |
---|
228 | |
---|
229 | if (genoObj.isValid()) { |
---|
230 | genotype = newGenotype; |
---|
231 | iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS; |
---|
232 | } |
---|
233 | |
---|
234 | Module.destroy(stringObj); |
---|
235 | Module.destroy(genoObj); |
---|
236 | } else { |
---|
237 | iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS; |
---|
238 | } |
---|
239 | iteration += 1; |
---|
240 | } while(iteration < Config.Simulation.EVOLUTION_MAX_REPETITIONS); |
---|
241 | |
---|
242 | Module.destroy(genoF4); |
---|
243 | Module.destroy(genoOperatorsHelper); |
---|
244 | return genotype; |
---|
245 | } |
---|
246 | |
---|
247 | getCrossoveredGenotype(genotype1, genotype2) { |
---|
248 | let genoF4 = new Module.Geno_f4(); |
---|
249 | let genoOperatorsHelper = new Module.GenoOperatorsHelper(genoF4); |
---|
250 | let genetics = new Module.PreconfiguredGenetics(); |
---|
251 | let iteration = 0; |
---|
252 | |
---|
253 | do { |
---|
254 | if (genoOperatorsHelper.crossOver(genotype1.replace("/*4*/", ""), |
---|
255 | genotype2.replace("/*4*/", "")) == 0) { |
---|
256 | let crossovered = genoOperatorsHelper.getLastCrossGeno1(); |
---|
257 | let newGenotype = "/*4*/ " + crossovered; |
---|
258 | |
---|
259 | let stringObj = new Module.SString(); |
---|
260 | stringObj.set(newGenotype); |
---|
261 | let genoObj = new Module.Geno(stringObj); |
---|
262 | |
---|
263 | if (genoObj.isValid()) { |
---|
264 | genotype1 = newGenotype; |
---|
265 | iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS; |
---|
266 | } |
---|
267 | |
---|
268 | Module.destroy(stringObj); |
---|
269 | Module.destroy(genoObj); |
---|
270 | } else { |
---|
271 | iteration = Config.Simulation.EVOLUTION_MAX_REPETITIONS; |
---|
272 | } |
---|
273 | iteration += 1; |
---|
274 | } while(iteration < Config.Simulation.EVOLUTION_MAX_REPETITIONS); |
---|
275 | |
---|
276 | Module.destroy(genoF4); |
---|
277 | Module.destroy(genoOperatorsHelper); |
---|
278 | return genotype1; |
---|
279 | } |
---|
280 | |
---|
281 | showEvolutionResult() { |
---|
282 | this.evolvedGenotypePlank = new GenotypePlank(this.evolvedGenotype, 10); |
---|
283 | this.evolvedGenotypePlank.move(Config.Simulation.Element.Position.EVOLVED_GENOTYPE_PLANK); |
---|
284 | this.evolvedGenotypePlank.rotate(Config.Simulation.Element.Angle.SIDE_VIEW); |
---|
285 | this.world.scene.add(this.evolvedGenotypePlank.mesh); |
---|
286 | this.world.scene.add(this.evolvedGenotypePlank.textMesh); |
---|
287 | |
---|
288 | if (this.evolutionMethod == State.MUTATION_GENOTYPE_SELECTION) { |
---|
289 | this.showText(this.mutatedText); |
---|
290 | this.moveText(this.mutatedText, Config.Simulation.Element.Position.RESULT_TEXT); |
---|
291 | this.rotateText(this.mutatedText, Config.Simulation.Element.Angle.SIDE_VIEW); |
---|
292 | } else if (this.evolutionMethod == State.CROSSOVER_GENOTYPE_SELECTION) { |
---|
293 | this.showText(this.offspringText); |
---|
294 | this.moveText(this.offspringText, Config.Simulation.Element.Position.RESULT_TEXT); |
---|
295 | this.rotateText(this.offspringText, Config.Simulation.Element.Angle.SIDE_VIEW); |
---|
296 | } else if (this.evolutionMethod == State.CLONING_GENOTYPE_SELECTION) { |
---|
297 | this.showText(this.clonedText); |
---|
298 | this.moveText(this.clonedText, Config.Simulation.Element.Position.RESULT_TEXT); |
---|
299 | this.rotateText(this.clonedText, Config.Simulation.Element.Angle.SIDE_VIEW); |
---|
300 | } |
---|
301 | } |
---|
302 | |
---|
303 | evolutionReturn() { |
---|
304 | let replaceIdx = parseInt(Math.random() * Config.Simulation.GENOTYPES.length); |
---|
305 | let genotypePlank = this.world.table.genotypePlanks[replaceIdx]; |
---|
306 | let fitnessPlank = this.world.table.fitnessPlanks[replaceIdx]; |
---|
307 | |
---|
308 | this.world.scene.remove(genotypePlank.mesh); |
---|
309 | this.world.scene.remove(genotypePlank.textMesh); |
---|
310 | this.world.scene.remove(fitnessPlank.mesh); |
---|
311 | this.world.scene.remove(fitnessPlank.textMesh); |
---|
312 | |
---|
313 | this.evolvedGenotypePlank.position = genotypePlank.position; |
---|
314 | this.evolvedGenotypePlank.move(this.evolvedGenotypePlank.position); |
---|
315 | this.evolvedGenotypePlank.rotate(Config.Simulation.Element.Angle.NORMAL); |
---|
316 | |
---|
317 | this.evolvedFitnessPlank.position = fitnessPlank.position; |
---|
318 | this.evolvedFitnessPlank.move(this.evolvedFitnessPlank.position); |
---|
319 | this.evolvedFitnessPlank.rotate(Config.Simulation.Element.Angle.NORMAL); |
---|
320 | |
---|
321 | this.world.table.genotypes[replaceIdx] = this.evolvedGenotype; |
---|
322 | this.world.table.genotypePlanks[replaceIdx] = this.evolvedGenotypePlank; |
---|
323 | this.world.table.fitnessPlanks[replaceIdx] = this.evolvedFitnessPlank; |
---|
324 | this.hideText(this.fitnessText); |
---|
325 | } |
---|
326 | |
---|
327 | getText(text, position) { |
---|
328 | let textMesh = Text.getInfoMesh(text); |
---|
329 | textMesh.position.set(position.x, position.y, position.z); |
---|
330 | textMesh.scale.set(0.01, 0.01, 0.01); |
---|
331 | return textMesh; |
---|
332 | } |
---|
333 | |
---|
334 | showText(text) { |
---|
335 | let tween = new TWEEN.Tween(text.scale).to({x: 1, y: 1, z: 1}, 500); |
---|
336 | tween.easing(TWEEN.Easing.Quadratic.InOut); |
---|
337 | tween.start(); |
---|
338 | } |
---|
339 | |
---|
340 | hideText(text) { |
---|
341 | let tween = new TWEEN.Tween(text.scale).to({x: 0.01, y: 0.01, z: 0.01}, 500); |
---|
342 | tween.easing(TWEEN.Easing.Quadratic.InOut); |
---|
343 | tween.start(); |
---|
344 | } |
---|
345 | |
---|
346 | getFramstick(genotype) { |
---|
347 | this.moveCamera(Config.Simulation.Camera.Position.FRAMSTICK); |
---|
348 | return new Framstick(genotype).mesh; |
---|
349 | } |
---|
350 | |
---|
351 | getArrow(idx) { |
---|
352 | return new Arrow(50 + (Config.Table.FitnessPlank.HEIGHT + Config.Table.Board.SPACING) * idx); |
---|
353 | } |
---|
354 | |
---|
355 | moveCamera(position) { |
---|
356 | let tween = new TWEEN.Tween(this.world.camera.position).to(position, Config.Simulation.Camera.SPEED); |
---|
357 | tween.easing(TWEEN.Easing.Quadratic.Out); |
---|
358 | tween.start(); |
---|
359 | } |
---|
360 | |
---|
361 | moveText(textMesh, position) { |
---|
362 | let tween = new TWEEN.Tween(textMesh.position).to(position, Config.Simulation.Camera.SPEED); |
---|
363 | tween.easing(TWEEN.Easing.Quadratic.Out); |
---|
364 | tween.start(); |
---|
365 | } |
---|
366 | |
---|
367 | rotateText(textMesh, angle) { |
---|
368 | let tween = new TWEEN.Tween(textMesh.rotation).to(angle, Config.Simulation.Camera.SPEED); |
---|
369 | tween.easing(TWEEN.Easing.Quadratic.Out); |
---|
370 | tween.start(); |
---|
371 | } |
---|
372 | } |
---|
373 | |
---|
374 | var State = { |
---|
375 | "INITIAL": 0, "EVOLUTION_METHOD_SELECTION": 1, "MUTATION_GENOTYPE_SELECTION": 2, |
---|
376 | "MUTATION_SIDE_VIEW": 3, "MUTATION_RESULT": 4, "SHOW_FRAMSTICK": 5, |
---|
377 | "SHOW_RESULT": 6, "MUTATION_RETURN": 7, "CROSSOVER_GENOTYPE_SELECTION": 8, |
---|
378 | "CROSSOVER_SIDE_VIEW": 9, "CROSSOVER_RESULT": 10, "CROSSOVER_RETURN": 11, |
---|
379 | "DESTROY_FRAMSTICK": 12, "CLONING_GENOTYPE_SELECTION": 13, |
---|
380 | "CLONING_SIDE_VIEW": 14, "CLONING_RESULT": 15, "CLONING_RETURN": 16 |
---|
381 | }; |
---|
382 | |
---|
383 | var EVOLUTION_METHOD_TEXT_POSITION = { |
---|
384 | x: -210, |
---|
385 | y: 80 + (Config.Table.FitnessPlank.HEIGHT + Config.Table.Board.SPACING) * (Config.Simulation.GENOTYPES.length + 1), |
---|
386 | z: -500 |
---|
387 | }; |
---|