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    }