001 /*
002 * SimuCS - Simulator to use with Classifier Systems
003 * MSc project - Oxford University
004 * by Benoit Isaac - Summer 2005
005 */
006
007 package simuLCS;
008 import java.awt.Color;
009 import java.awt.Graphics2D;
010 import java.awt.Image;
011 import java.io.BufferedWriter;
012 import java.io.File;
013 import java.io.FileWriter;
014 import java.io.PrintWriter;
015 import java.util.Collections;
016 import java.util.Comparator;
017 import java.util.Iterator;
018 import java.util.SortedSet;
019 import java.util.Timer;
020 import java.util.TimerTask;
021 import java.util.TreeSet;
022
023 import javax.swing.JOptionPane;
024
025 import simuLCS.graphics.G_MainWindow;
026 import simuLCS.graphics.G_Panel;
027
028 /**
029 * Important class: handles the entities, the Thread for running the Simulation, and the Thread to update the Behaviour shown to the user when there is a GUI.
030 * @author Benoit
031 *
032 */
033 public class Simulation {
034
035 /**
036 * The entities used in the Simulation
037 */
038 private SortedSet entities;
039 // private int numberOfEntities;
040 private G_MainWindow window;
041 private Graphics2D graphics;
042 private Utils utils;
043
044 private boolean isSimuRunning;
045
046 /**
047 * Used to run the simulation only for one step
048 */
049 private boolean simuStep;
050
051 /**
052 * Used to know when to ask the name of the file
053 */
054 private boolean isNewRun = true;
055
056 protected int nbOfRuns = 1;
057 protected int nbOfRunsDone = 0;
058
059 private boolean isPlotting = true;
060 private int plottingInterval = 100;
061 private String fileBaseForPlotting = "noname";
062 private String fileBaseForSeveralPlotting = "noname";
063
064 private FileWriter fW = null;
065 private BufferedWriter bW = null;
066 private PrintWriter pW = null;
067
068 private int timeStep = 0;
069 private int maxTimeSteps = -1;
070 private final static int UPDATE_BEHAVIOUR_TO_SHOW = 100;
071
072 protected Thread animation = null;
073 // private Animation animation = null ;
074
075 private int nbAgentsToCreate = 0;
076 private boolean hasAlreadyOneAgentInteractive = false;
077
078 protected java.util.Random generator;
079 private Image offscreen;
080 private Graphics2D graphicsOffscreen;
081
082 protected Arena arena;
083
084 public Simulation(Utils u) {
085
086 window = null;
087 utils = u;
088
089 arena = new Arena(Config.SIZE_ARENA, Config.SHIFT_ARENA);
090
091 init();
092 // window = win;
093 // graphics = win.getPanelDraw().getGraphics2D();
094 // graphics.translate(sizeArena/2+shiftArena,sizeArena/2+shiftArena);
095
096 }
097
098 public void linkToGraphics(G_MainWindow win) {
099 window = win;
100 graphics = win.getPanelDraw().getGraphics2D();
101 graphics.translate(
102 Config.SIZE_ARENA / 2 + Config.SHIFT_ARENA,
103 Config.SIZE_ARENA / 2 + Config.SHIFT_ARENA);
104 window.getPanelDraw().linkArena(arena);
105 }
106
107 /**
108 * Add one default entity
109 *
110 */
111 public void add() {
112 add(getNumberOfEntities() + 1);
113 }
114
115 /**
116 * Add the default entity : the AgentClassifierLearning with
117 * the TemplateRSPVerySimple (by convention).
118 * @param name
119 */
120 public void add(int name) {
121 // AgentClassifierDuck a = new AgentClassifierDuck(generator,arena,""+i);
122 AgentClassifierLearning a =
123 new AgentClassifierLearning(generator, arena, "" + name);
124 // a.setGhostPainted(false);
125 // AgentDuck a = new AgentDuck(generator,arena,""+name);
126
127 System.out.println(a);
128 System.out.println(a.getBehaviour());
129 add(a);
130 }
131
132 /**
133 * The user can choose which Agent should be added, if there is a GUI
134 * otherwise, the default Agent is added.
135 *
136 */
137 public void addWithChoice() {
138 if (window != null) {
139 Object[] possibilities =
140 {
141 "a: AgentClassifierLearning (with TemplateRSPVerySimple)",
142 "b: AgentClassifierLearning (with TemplateRSP)",
143 "c: AgentDuck (no classifier)",
144 "d: Agent Interactive (drag and drop with the mouse) - Only one" };
145
146 String s =
147 (String) JOptionPane.showInputDialog(
148 window,
149 "Which kind of Agent do you want to add ?",
150 "Adding an Agent",
151 JOptionPane.PLAIN_MESSAGE,
152 null,
153 possibilities,
154 "a: AgentClassifierLearning (with TemplateRSPVerySimple)");
155
156 // If a string was returned, add the corresponding agent
157 if ((s != null) && (s.length() > 0)) {
158 char c = s.charAt(0);
159 switch (c) {
160
161 default : /* ACL with TemplateRSPVerySimple */
162 AgentClassifierLearning ag2 =
163 new AgentClassifierLearning(
164 generator,
165 arena,
166 "" + (getNumberOfEntities() + 1));
167 add(ag2);
168 break;
169 case 'b' : /* ACL with TemplateRSP */
170
171 TemplateRSP t = new TemplateRSP();
172 ClassifierSet b = new ClassifierSet(t);
173
174 /* creating the classifiers */
175 // c1 : ducks are attracted to each other
176 Classifier c1 =
177 new Classifier(
178 "10",
179 "0001011",
180 "attracted by ducks ");
181 // c2 : ducks are repelled from each other (preventing collisions)
182 Classifier c2 =
183 new Classifier(
184 "10",
185 "1000010",
186 "repelled by ducks ");
187 // c3 : ducks are repelled from obstacles
188 Classifier c3 =
189 new Classifier(
190 "01",
191 "1000000",
192 "repelled by the wall");
193 // c4 : ducks are repelled from the robot
194 Classifier c4 =
195 new Classifier(
196 "11",
197 "1011110",
198 "repelled by the robot");
199
200 b.addClassifier(c1);
201 b.addClassifier(c2);
202 b.addClassifier(c3);
203 b.addClassifier(c4);
204 AgentClassifierLearning ag =
205 new AgentClassifierLearning(
206 generator,
207 arena,
208 "" + (getNumberOfEntities() + 1),
209 t,
210 b);
211 add(ag);
212 break;
213
214 case 'c' : /* AgentDuck */
215 AgentDuck ag3 =
216 new AgentDuck(
217 generator,
218 arena,
219 "" + (getNumberOfEntities() + 1));
220 add(ag3);
221 break;
222 case 'd' :
223 if (!hasAlreadyOneAgentInteractive) {
224 AgentInteractive ag4 =
225 new AgentInteractive(utils, generator, arena);
226 ag4.setName("R");
227 add(ag4);
228 hasAlreadyOneAgentInteractive = true;
229 } else {
230 JOptionPane.showMessageDialog(
231 window,
232 "Sorry, only one Agent Interactive can be used.");
233 }
234 break;
235 }
236 return;
237 }
238
239 // If you're here, the return value was null/empty.
240
241 } else {
242 add();
243 }
244 }
245
246 /**
247 * Adding the given entity to the Simulation.
248 * @param e
249 */
250 public synchronized void add(Entity e) {
251 if (entities.size() < Config.MAX_NB_ENTITIES) {
252 synchronized (entities) {
253 entities.add(e);
254 }
255
256 System.out.println("ADDED: " + e);
257 if (window != null) // we can print a message
258 {
259 window.getStatusBar().printMessage(
260 "Nb of Entities:" + entities.size());
261 window.getPanelCustom().updateTabAgents();
262 }
263 } else {
264 if (window != null) // we can print a message
265 window.getStatusBar().printMessage("Too many entities.");
266 }
267 }
268
269 /**
270 * Removing the given entity to the simulation
271 * @param e
272 */
273 public synchronized void remove(Entity e) {
274 if (entities.contains(e)) {
275 synchronized (entities) {
276 entities.remove(e);
277 }
278 System.out.println("REMOVED: " + e);
279 if (window != null) // we can print a message
280 {
281 window.getStatusBar().printMessage(
282 "Nb of Entities:" + entities.size());
283 window.getPanelCustom().updateTabAgents();
284 }
285 } else {
286 if (window != null) // we can print a message
287 window.getStatusBar().printMessage(
288 "This entity is not in the Simulation.");
289 }
290 }
291
292 /**
293 * Painting all the simulation
294 * @param g
295 */
296 public void paintSimu(Graphics2D g) {
297
298 g.setColor(Color.WHITE);
299 g.fillRect(
300 -Config.SIZE_ARENA / 2 - 100,
301 -Config.SIZE_ARENA / 2 - 100,
302 Config.SIZE_ARENA + 100,
303 Config.SIZE_ARENA + 100);
304
305 arena.paint(g);
306 Iterator iterator = getEntities();
307 while (iterator.hasNext())
308 ((Entity) iterator.next()).paint(g);
309 }
310
311 /**
312 * Erasing everything in the Drawing Panel
313 * @param g
314 */
315 public void clearSimu(Graphics2D g) {
316 g.setColor(Color.WHITE);
317 g.fillRect(
318 -Config.SIZE_ARENA / 2 - 200,
319 -Config.SIZE_ARENA / 2 - 200,
320 Config.SIZE_ARENA + 300,
321 Config.SIZE_ARENA + 300);
322
323 }
324
325 public void create() {
326 create(nbAgentsToCreate);
327 }
328
329 public void create(int numAgents) {
330 int i;
331 nbAgentsToCreate = numAgents;
332 System.out.println("");
333 System.out.println("");
334
335 AgentClassifierAutomatic RobotAutomatic =
336 new AgentClassifierAutomatic(generator, arena, "Ra");
337 // System.out.println(RobotAutomatic);
338 add(RobotAutomatic);
339
340 for (i = 0; i < numAgents; i++) {
341 add();
342 }
343 }
344
345 /**
346 * Open the file to start recording the average reward.
347 *
348 */
349 private void openFile() {
350 if (!fileBaseForSeveralPlotting.equals("noname"))
351 setFileBaseForPlotting(
352 getFileBaseForSeveralPlotting() + (nbOfRunsDone + 1));
353
354 File outFile =
355 new File(Config.FOLDER_DATA + fileBaseForPlotting + ".dat");
356
357 try {
358 fW = new FileWriter(outFile);
359 bW = new BufferedWriter(fW);
360 pW = new PrintWriter(bW);
361 if (Config.PRINT_MODE > 0)
362 System.out.println(
363 "Starting to log in: "
364 + Config.FOLDER_DATA
365 + fileBaseForPlotting
366 + ".dat");
367 } catch (Exception e) {
368 System.out.println("Mistake while creating file Writers:" + e);
369 }
370 }
371
372 /**
373 * Close the data file and creates the gnuplot file and the file
374 * saving the rules at the end, for each agent
375 * @see Utils#writeFileGnuplot(String, Entity[], String)
376 * @see Utils#writeFileWithRules(String, Entity[])
377 */
378 private void closeFile() {
379
380 try {
381 pW.flush();
382 bW.flush();
383 fW.flush();
384 fW.close();
385 // if (Config.PRINT_MODE > -1)
386 System.out.println(
387 "Data recorded in the log file: "
388 + Config.FOLDER_DATA
389 + fileBaseForPlotting
390 + ".dat");
391 } catch (Exception e) {
392 System.out.println("Mistake while closing the file writer:" + e);
393 }
394 }
395
396 /**
397 * Starting the simulation !
398 *
399 */
400 public void start() {
401 isSimuRunning = true;
402
403 if (isNewRun && fileBaseForPlotting != "")
404 openFile();
405 isNewRun = false;
406 }
407
408 /**
409 * Pausing the simulation. It can be resumed.
410 *
411 */
412 public void stop() {
413 isSimuRunning = false;
414 }
415
416 /**
417 * Stopping the simulation. It cannot be resumed.
418 * Files are closed , and a reset should be done before a new simulation.
419 *
420 */
421 public void finish() {
422 stop();
423 if (fileBaseForPlotting != null && !fileBaseForPlotting.equals("")) {
424 closeFile();
425 // creation of the associated gnuplot file
426 Entity[] en = new Entity[0];
427 en = (Entity[]) entities.toArray(en);
428 Utils.writeFileGnuplot(fileBaseForPlotting, en, "ps");
429 Utils.writeFileWithRules(fileBaseForPlotting, en);
430 }
431 }
432
433 /**
434 * Just one time step more.
435 *
436 */
437 public void step() {
438 simuStep = true;
439 isSimuRunning = true;
440 if (isNewRun && fileBaseForPlotting != "")
441 openFile();
442 isNewRun = false;
443 }
444
445 /**
446 * Starting again with the default simulation.
447 *
448 */
449 public void reset() {
450 if (isSimuRunning) {
451 return;
452 } else {
453 init();
454 create();
455 if (window != null) {
456 G_Panel.repaintAll();
457 G_Panel.repaintCustom();
458 }
459 }
460 }
461
462 /**
463 * Initializing the simulation
464 *
465 */
466 private void init() {
467 generator = new java.util.Random(System.currentTimeMillis());
468 entities =
469 Collections.synchronizedSortedSet(new TreeSet(new Comparator() {
470 public int compare(Object o1, Object o2) {
471 Entity e1 = (Entity) o1;
472 Entity e2 = (Entity) o2;
473 return (e1.getId() - e2.getId());
474 }
475 }));
476
477 hasAlreadyOneAgentInteractive = false;
478 timeStep = 0;
479 isSimuRunning = false;
480 simuStep = false;
481 isNewRun = true;
482 if (window != null) {
483 G_Panel.getSetSelectedEntities().clear();
484 G_Panel.getSetCurrentEntityToWatch().clear();
485
486 }
487
488 }
489
490 /**
491 * Creating the Thread for the Animation
492 *
493 */
494 public void createAnimation() {
495 System.out.println("Creating Animation...");
496
497 animation = new Thread(new Animation());
498 // animation = new Animation();
499
500 System.out.println("Creating Animation...DONE");
501 // updateThread = new UpdateThread();
502
503 }
504
505 /**
506 * Launch the Thread that updates every <code>timeInMilliSeconds</code>
507 * the behaviour shown in the display (to avoid real-time, because
508 * it makes the simulation really slow)
509 * @param timeInMilliSeconds
510 */
511 public void launchUpdateThread(long timeInMilliSeconds) {
512 Timer timer = new Timer();
513 TimerUpdate updateTask = new TimerUpdate();
514 timer.schedule(updateTask, 0, timeInMilliSeconds);
515
516 }
517
518 /**
519 * Starts the Thread Animation
520 *
521 */
522 public void run() {
523 if (animation == null) {
524 System.out.println("You must launch the animation first.");
525 } else {
526 System.out.println("Starting Animation...");
527
528 animation.start();
529 // animation.run();
530
531 System.out.println("Starting Animation...DONE");
532 // updateThread.run();
533 }
534 }
535
536 /**
537 * The function to launch in the Thread updateTask
538 * @see #launchUpdateThread(long)
539 * @see TimerUpdate
540 *
541 */
542 public void updateBehaviours() {
543 String type;
544 Iterator iterator = getEntities();
545 Entity current;
546 while (iterator.hasNext()) {
547 current = (Entity) iterator.next();
548 if (isSimuRunning
549 && current instanceof AgentClassifierLearning
550 && ((AgentClassifierLearning) current).isLearning()) {
551 System.out.println("[Automatic Thread Update] " + current);
552 (
553 (
554 AgentClassifierLearning) current)
555 .requestUpdateBehaviourToShow(
556 isSimuRunning,
557 null);
558 if (window != null)
559 G_Panel.repaintCustom();
560 }
561
562 }
563 }
564
565 /**
566 * The task to be executed every X milliSeconds
567 * @author Benoit
568 *
569 */
570 class TimerUpdate extends TimerTask {
571 public void run() {
572 updateBehaviours();
573 }
574 }
575
576 /**
577 * The inner class handling the simulation
578 * (MAIN PROCEDURE)
579 * @author Benoit
580 */
581 class Animation
582 // extends Thread
583 implements Runnable {
584 public void run() {
585 String type;
586 Entity current;
587 try {
588
589 while (nbOfRuns > 0) {
590 System.out.println(
591 "**Experiment " + (nbOfRunsDone + 1) + "**");
592 nbOfRuns--;
593
594 // changing the suffix of the file if several experiments are run
595 if (!fileBaseForSeveralPlotting.equals("noname"))
596 setFileBaseForPlotting(
597 getFileBaseForSeveralPlotting()
598 + (nbOfRunsDone + 1));
599
600 // main loop !
601 while (maxTimeSteps == -1 || timeStep < maxTimeSteps) {
602
603 if (window != null)
604 arena.paintWithTimeSteps(graphics, timeStep);
605
606 if (isSimuRunning()) {
607 timeStep++;
608 if (timeStep % 5000 == 0)
609 System.out.println(
610 "Time step " + timeStep + " reached");
611 }
612
613 if (isSimuRunning()
614 && timeStep % plottingInterval == 0) {
615 if (Config.PRINT_MODE > 3)
616 System.out.println(
617 "Plotting...timeStep " + timeStep);
618 pW.println();
619 pW.print(timeStep + "\t");
620 }
621
622 Iterator iterator = getEntities();
623 // moving all the entities one by one
624 while (iterator.hasNext()) {
625 current = (Entity) iterator.next();
626 if (isSimuRunning
627 || current instanceof AgentInteractive) {
628 if (!(current instanceof AgentInteractive)
629 && Config.PRINT_MODE > 8)
630 System.out.println(
631 "*MOVE Agent" + current.getId() + "*");
632
633 //agents[i].clear(graphics);
634 Entity[] en = new Entity[0];
635 en = (Entity[]) entities.toArray(en);
636 current.move(
637 arena,
638 en,
639 entities.size(),
640 graphics);
641
642 if (timeStep % plottingInterval == 0
643 && current
644 instanceof AgentClassifierLearning) {
645 AgentClassifierLearning ag =
646 (AgentClassifierLearning) current;
647 double avgReward =
648 ag.getAverageRewardFromLastPlot();
649 pW.print(avgReward + "\t");
650 }
651
652 }
653 if (window != null)
654 current.paint(graphics);
655 }
656
657 if (simuStep && isSimuRunning) {
658 isSimuRunning = false;
659 simuStep = false;
660 }
661
662 // TODO delete sleep
663 Thread.sleep(1);
664 }
665 nbOfRunsDone++;
666 finish();
667 // Thread.sleep(10);
668 if (nbOfRuns > 0) {
669
670 reset();
671 start();
672 }
673 }
674
675 } catch (InterruptedException e) {
676 }
677 }
678 }
679
680 /**
681 * @return
682 */
683 public static int getMaxNbEntities() {
684 return Config.MAX_NB_ENTITIES;
685 }
686
687 /**
688 * @return
689 */
690 public int getNumberOfEntities() {
691 return entities.size();
692 }
693
694 public Iterator getEntities() {
695 return entities.iterator();
696 }
697
698 /**
699 * @return
700 */
701 public boolean isSimuRunning() {
702 return isSimuRunning;
703 }
704
705 /**
706 * @return
707 */
708 public boolean isNewRun() {
709 return isNewRun;
710 }
711
712 /**
713 * @param b
714 */
715 public void setNewRun(boolean b) {
716 isNewRun = b;
717 }
718
719 /**
720 * @return
721 */
722 public String getFileBaseForPlotting() {
723 return fileBaseForPlotting;
724 }
725
726 /**
727 * @param string
728 */
729 public void setFileBaseForPlotting(String string) {
730 fileBaseForPlotting = string;
731 }
732
733 public String getFileBaseForSeveralPlotting() {
734 return fileBaseForSeveralPlotting;
735 }
736
737 public void setFileBaseForSeveralPlotting(String string) {
738 fileBaseForSeveralPlotting = string;
739 }
740
741 /**
742 * @param i
743 */
744 public void setMaximumTimeSteps(int max) {
745 maxTimeSteps = max;
746 }
747
748 /**
749 * @return
750 */
751 public int getNbOfRuns() {
752 return nbOfRuns;
753 }
754
755 /**
756 * @param i
757 */
758 public void setNbOfRuns(int i) {
759 nbOfRuns = i;
760 }
761
762 /**
763 * @return
764 */
765 public int getMaxTimeSteps() {
766 return maxTimeSteps;
767 }
768
769 /**
770 * @param i
771 */
772 public void setMaxTimeSteps(int i) {
773 maxTimeSteps = i;
774 }
775
776 }