001 package sysModel.env;
002
003 import controller.IScrollAdapter;
004 import controller.IDisplayAdapter;
005 import junit.framework.TestCase;
006 import lrs.IAlgo;
007 import lrs.LRStruct;
008 import lrs.visitor.GetLength;
009 import lrs.visitor.Remove;
010 import model.ILambda;
011 import model.RandNumGenerator;
012 import model.fish.GenericFish;
013 import sysModel.ICmdFactory;
014 import sysModel.ISecurityAdapter;
015 import sysModel.NoOpLambda;
016 import sysModel.fish.IFishFactory;
017 import sysModel.fish.AFish;
018 import sysModel.parser.DefaultTokenVisitor;
019 import sysModel.parser.Lexer;
020 import sysModel.parser.ParserException;
021
022 import javax.swing.*;
023 import java.awt.*;
024 import java.awt.event.MouseEvent;
025 import java.awt.geom.AffineTransform;
026 import java.lang.reflect.Constructor;
027 import java.util.LinkedList;
028 import java.util.Random;
029 import java.io.PrintWriter;
030
031 /**
032 * An environment that does not use grids to place fish.
033 *
034 * @author Mathias Ricken
035 */
036 public class NoGridEnv extends AGlobalEnv {
037 /**
038 * Concrete direction class.
039 */
040 public static class Direction {
041 /// floating point epsilon for comparisons
042 private static double EPSILON = 1e-10;
043
044 /**
045 * Direction delta x.
046 */
047 private double _dx;
048
049 /**
050 * Direction delta y.
051 */
052 private double _dy;
053
054 /**
055 * Return the direction delta x.
056 *
057 * @return double direction delta x
058 */
059 public double getDeltaX() {
060 return _dx;
061 }
062
063 /**
064 * Return the direction delta y.
065 *
066 * @return double direction delta y
067 */
068 public double getDeltaY() {
069 return _dy;
070 }
071
072 /**
073 * Return a new object which has the same direction.
074 *
075 * @return new object with same direction
076 */
077 public Direction duplicate() {
078 return new Direction(_dx, _dy);
079 }
080
081 /**
082 * Reverse this direction.
083 */
084 public void reverseDirection() {
085 _dx = -_dx;
086 _dy = -_dy;
087 }
088
089 /**
090 * Return true of this direction is the same as the other.
091 *
092 * @param other other direction
093 * @return true if the directions are the same
094 */
095 public boolean same(Direction other) {
096 double diffx = _dx - other.getDeltaX();
097 double diffy = _dy - other.getDeltaY();
098 return (Math.abs(diffx) < EPSILON && Math.abs(diffy) < EPSILON);
099 }
100
101 /**
102 * Turn this direction to the left.
103 *
104 * @param radians radians to turn
105 */
106 public void turnLeft(double radians) {
107 turnRight(-radians);
108 }
109
110 /**
111 * Turn this direction PI/2 radians to the left.
112 */
113 public void turnLeft() {
114 turnLeft(Math.PI / 2);
115 }
116
117 /**
118 * Turn this direction to the right.
119 *
120 * @param radians radians to turn
121 */
122 public void turnRight(double radians) {
123 double dx = _dx * Math.cos(radians) - _dy * Math.sin(radians);
124 double dy = _dx * Math.sin(radians) + _dy * Math.cos(radians);
125 _dx = dx;
126 _dy = dy;
127 }
128
129 /**
130 * Turn this direction PI/2 radians to the right.
131 */
132 public void turnRight() {
133 turnRight(Math.PI / 2);
134 }
135
136 /**
137 * Constructor.
138 *
139 * @param dx delta x
140 * @param dy delta y
141 */
142 public Direction(double dx, double dy) {
143 _dx = dx;
144 _dy = dy;
145 }
146
147 /**
148 * Copy constructor.
149 *
150 * @param other other direction
151 */
152 public Direction(Direction other) {
153 _dx = other.getDeltaX();
154 _dy = other.getDeltaY();
155 }
156
157 /**
158 * Creates a new direction facing north.
159 */
160 public Direction() {
161 _dx = 0;
162 _dy = -1;
163 }
164
165 /**
166 * Overridden toString method.
167 *
168 * @return string representation
169 */
170 public String toString() {
171 return "(" + _dx + ", " + _dy + ')';
172 }
173
174 /**
175 * Parses a direction.
176 *
177 * @param l parser to read from
178 * @return parsed direction
179 */
180 public static Direction parse(final Lexer l) {
181 // read (
182 return (Direction) l.nextToken().execute(new DefaultTokenVisitor() {
183 public Object defaultCase() {
184 throw new ParserException("Invalid token");
185 }
186
187 public Object openCase() {
188 // read x
189 return l.nextToken().execute(new DefaultTokenVisitor() {
190 public Object defaultCase() {
191 throw new ParserException("Invalid token");
192 }
193
194 public Object numCase(final double x) {
195 // read ,
196 return l.nextToken().execute(new DefaultTokenVisitor() {
197 public Object defaultCase() {
198 throw new ParserException("Invalid token");
199 }
200
201 public Object commaCase() {
202 // read y
203 return l.nextToken().execute(new DefaultTokenVisitor() {
204 public Object defaultCase() {
205 throw new ParserException("Invalid token");
206 }
207
208 public Object numCase(final double y) {
209 // read )
210 return l.nextToken().execute(new DefaultTokenVisitor() {
211 public Object defaultCase() {
212 throw new ParserException("Invalid token");
213 }
214
215 public Object closeCase() {
216 return new Direction(x, y);
217 }
218 });
219 }
220 });
221 }
222 });
223 }
224 });
225 }
226 });
227 }
228
229 /**
230 * Rotate the supplied Graphics2D object to match this direction.
231 *
232 * @param g graphics object to rotate
233 */
234 public void rotateGraphics(Graphics2D g) {
235 /*
236 Find the angle between the current direction dir and (1,0) in a clockwise direction.
237 dir . (0, -1) = |dir|*|(0,-1)|*cos(theta). But |dir| = |(0, -1)| = 1, so
238 cos(theta) = dir . (0, -1) = -dir.y
239 This is always the smaller angle, though. Therefore
240 theta = arccos(-dir.y) if dir.x >= 0
241 = 2 PI - arccos(-dir.y) if dir.x < 0
242 */
243 double theta = getAngle();
244
245 if (null != g) {
246 g.rotate(theta);
247 }
248 }
249
250 /**
251 * Return the angle between (0,-1) and this direction.
252 *
253 * @return angle in radians
254 */
255 public double getAngle() {
256 // keeping argument to acos in range [-1,1] because acos produced a NaN on one system
257 double theta = Math.acos(Math.max(-1,Math.min(1,-_dy)));
258 if (0 > _dx) {
259 theta = 2 * Math.PI - theta;
260 }
261 return theta;
262 }
263 }
264
265 /**
266 * Concrete location class.
267 *
268 * @author Mathias G. Ricken
269 */
270 public static class Location {
271 /**
272 * Column.
273 */
274 private double _x;
275
276 /**
277 * Row.
278 */
279 private double _y;
280
281 /**
282 * Return column.
283 *
284 * @return double column
285 */
286 public double getX() {
287 return _x;
288 }
289
290 /**
291 * Return row.
292 *
293 * @return double row
294 */
295 public double getY() {
296 return _y;
297 }
298
299 /**
300 * Set column.
301 *
302 * @param _x New column
303 */
304 public void setX(double _x) {
305 this._x = _x;
306 }
307
308 /**
309 * Set row.
310 *
311 * @param _y New row
312 */
313 public void setY(double _y) {
314 this._y = _y;
315 }
316
317 /**
318 * Constructor.
319 *
320 * @param x column
321 * @param y row
322 */
323 public Location(double x, double y) {
324 _x = x;
325 _y = y;
326 }
327
328 /**
329 * Return true of this location is the same as the other.
330 *
331 * @param other other location
332 * @return true if the locations are the same
333 */
334 public boolean same(Location other) {
335 return (_x == other.getX()) &&
336 (_y == other.getY());
337 }
338
339 /**
340 * Return true of the other location is in a square with this location at the center, facing in the given
341 * direction, with sides of the specified length.
342 *
343 * @param other location to compare to
344 * @param forward direction the square is facing (this direction is perpendicular to two of the side walls)
345 * @param side side length
346 * @return true if the other point is inside
347 */
348 public boolean inSquare(Location other, Direction forward, double side) {
349 // distante center-to-other
350 double deltaX = other.getX() - _x;
351 double deltaY = other.getY() - _y;
352
353 // point at center front of square
354 double frontX = forward.getDeltaX();
355 double frontY = forward.getDeltaY();
356
357 // project center-to-other onto center-to-front
358 double toFront = deltaX * frontX + deltaY * frontY;
359 if (Math.abs(toFront) > side / 2) {
360 // beyond front or back side
361 return false;
362 }
363
364 // point at center right of square
365 double rightX = -forward.getDeltaY();
366 double rightY = forward.getDeltaX();
367
368 // project center-to-other onto center-to-right
369 double toRight = deltaX * rightX + deltaY * rightY;
370 return Math.abs(toRight) <= side / 2;
371
372 }
373
374 /**
375 * Return the location of a neighbor in the given direction.
376 *
377 * @param dir the direction of the neighbor to be returned
378 * @return neighbor in that direction
379 */
380 public Location getNeighbor(Direction dir) {
381 return new Location(_x + dir.getDeltaX(),
382 _y + dir.getDeltaY());
383 }
384
385 /**
386 * Overridden toString method.
387 *
388 * @return string representation
389 */
390 public String toString() {
391 return "(" + _x + ", " + _y + ')';
392 }
393
394 /**
395 * Parses a location.
396 *
397 * @param l parser to read from
398 * @return parsed location
399 */
400 public static Location parse(final Lexer l) {
401 // read (
402 return (Location) l.nextToken().execute(new DefaultTokenVisitor() {
403 public Object defaultCase() {
404 throw new ParserException("Invalid token");
405 }
406
407 public Object openCase() {
408 // read x
409 return l.nextToken().execute(new DefaultTokenVisitor() {
410 public Object defaultCase() {
411 throw new ParserException("Invalid token");
412 }
413
414 public Object numCase(final double x) {
415 // read ,
416 return l.nextToken().execute(new DefaultTokenVisitor() {
417 public Object defaultCase() {
418 throw new ParserException("Invalid token");
419 }
420
421 public Object commaCase() {
422 // read y
423 return l.nextToken().execute(new DefaultTokenVisitor() {
424 public Object defaultCase() {
425 throw new ParserException("Invalid token");
426 }
427
428 public Object numCase(final double y) {
429 // read )
430 return l.nextToken().execute(new DefaultTokenVisitor() {
431 public Object defaultCase() {
432 throw new ParserException("Invalid token");
433 }
434
435 public Object closeCase() {
436 return new Location(x, y);
437 }
438 });
439 }
440 });
441 }
442 });
443 }
444 });
445 }
446 });
447 }
448 }
449
450 /**
451 * Concrete local environment for the square unbounded environment.
452 */
453 protected class LocalEnvironment extends ALocalEnv {
454 /**
455 * Location.
456 */
457 Location _loc;
458
459 /**
460 * Direction.
461 */
462 Direction _dir;
463
464 /**
465 * State.
466 */
467 ILocalEnvState _state = EmptyLocalEnvState.Singleton;
468
469 /**
470 * Lambda to execute a move.
471 */
472 private class MoveLambda implements ILambda {
473 /// target direction
474 private Direction _newDir;
475 /// target location
476 private Location _newLoc;
477
478 /**
479 * Constructor.
480 *
481 * @param le target local environment
482 */
483 public MoveLambda(LocalEnvironment le) {
484 _newLoc = new Location(le._loc.getX(), le._loc.getY());
485 _newDir = new Direction(le._dir);
486 }
487
488 /**
489 * Execute the move.
490 *
491 * @param param not used
492 * @return null
493 */
494 public Object apply(Object param) {
495 // execute the movement
496 _loc = _newLoc;
497 _dir = _newDir;
498
499 // deactivate all lambdas
500 deactivateMoveLambdas();
501 return null;
502 }
503 }
504
505 /**
506 * Construct a new local environment.
507 *
508 * @param loc location
509 * @param dir direction
510 */
511 public LocalEnvironment(Location loc, Direction dir) {
512 _loc = loc;
513 _dir = dir;
514 }
515
516 /**
517 * Accessor for the location.
518 *
519 * @return location
520 */
521 public Location location() {
522 return _loc;
523 }
524
525 /**
526 * Accessor for the direction.
527 *
528 * @return direction
529 */
530 public Direction direction() {
531 return _dir;
532 }
533
534 /**
535 * Make local environment in forward direction. Do not block yourself.
536 *
537 * @return new local environment in forward direction
538 */
539 protected ALocalEnv makeMoveFwdLocalEnv() {
540 // remove this local environment to prevent collision with itself
541 _localEnvList.execute(Remove.Singleton, this);
542 ALocalEnv le = makeLocalEnv(_loc.getNeighbor(_dir), _dir);
543 // add this local environment back in
544 _localEnvList.insertFront(this);
545 return le;
546 }
547
548 /**
549 * Factory method for a move lambda.
550 *
551 * @param le local environment for the target
552 * @return move lambda to execute the move to the target
553 */
554 protected ILambda makeMoveLambda(ALocalEnv le) {
555 return new MoveLambda((LocalEnvironment) le);
556 }
557
558 /**
559 * Draw the fish on the graphics object. The graphics object still has to be translated and rotated properly,
560 *
561 * @param fish AFish to drawFish
562 * @param g graphics object to drawFish on
563 * @param comp component to drawFish on
564 */
565 public void drawFish(AFish fish, Graphics2D g, Component comp) {
566 double centerX = _loc.getX();
567 double centerY = _loc.getY();
568
569 // save transformation
570 AffineTransform oldTransform = g.getTransform();
571 // translate to center of field
572 g.translate(centerX, centerY);
573
574 // set up the correct rotation
575 _dir.rotateGraphics(g);
576
577 // makeDrawCmd the fish
578 fish.paint(g, comp);
579
580 // restore transformation
581 g.setTransform(oldTransform);
582 }
583
584 /**
585 * Turn the fish radians to the right.
586 *
587 * @param fish AFish to turn
588 * @param radians radians to turn
589 */
590 public void turnRight(AFish fish, double radians) {
591 _dir.turnRight(radians);
592 }
593
594 /**
595 * String representation of the local environment. Should be "(x, y) (dx, dy)".
596 *
597 * @return string representation
598 */
599 public String toString() {
600 return _loc.toString() + ' ' + _dir.toString();
601 }
602 }
603
604 private static int PAN_SIZE = 2000;
605 private static Point.Double PAN_CENTER = new Point.Double(PAN_SIZE / 2, PAN_SIZE / 2);
606
607 /**
608 * List of local environments in this global environment.
609 */
610 private LRStruct _localEnvList;
611
612 /**
613 * Number of steps for complete rotation.
614 */
615 private int _rotSteps;
616
617 /**
618 * Create a new environment without grids. Note: This constructor needs to exist and be public for the "environment
619 * selection" dialog to work.
620 *
621 * @param cmdFactory command factory to use
622 * @param sm security manager to control fish actions
623 */
624 public NoGridEnv(ICmdFactory cmdFactory, ISecurityAdapter sm) {
625 super(cmdFactory, sm);
626 _localEnvList = new LRStruct();
627 }
628
629 /**
630 * Create a new environment without grids.
631 *
632 * @param cmdFactory command factory to use
633 * @param sm security manager to control fish actions
634 * @param rotSteps rotation steps
635 * @param waterColor color of the water
636 */
637 public NoGridEnv(ICmdFactory cmdFactory, ISecurityAdapter sm, int rotSteps, Color waterColor) {
638 super(cmdFactory, sm);
639 _localEnvList = new LRStruct();
640 _rotSteps = rotSteps;
641 _waterColor = waterColor;
642 }
643
644 /**
645 * Add the fish to the global environment.
646 *
647 * @param localEnv local environment
648 * @param fish fish to add
649 */
650 protected void addFishToInternalData(ALocalEnv localEnv, AFish fish) {
651 _localEnvList.insertFront(localEnv);
652 }
653
654 /**
655 * Remove the fish from the global environment.
656 *
657 * @param localEnv local environment
658 */
659 protected void removeFishFromInternalData(ALocalEnv localEnv) {
660 _localEnvList.execute(Remove.Singleton, localEnv);
661 }
662
663 /**
664 * Create a local environment for the position.
665 *
666 * @param p position
667 * @return local environment
668 */
669 public ALocalEnv makeLocalEnv(Point.Double p) {
670 return makeLocalEnv(new Location(p.getX(), p.getY()), new Direction());
671 }
672
673 /**
674 * Create a local environment for the position.
675 *
676 * @param loc location
677 * @param dir direction
678 * @return local environment
679 */
680 private ALocalEnv makeLocalEnv(final Location loc, final Direction dir) {
681 return (ALocalEnv) _localEnvList.execute(new IAlgo() {
682 /**
683 * Operates on a non-empty LRStruct host, given an input object.
684 *
685 * @param host a non-empty LRStruct.
686 * @param inp input object needed by this IAlgo.
687 * @return an appropriate output object.
688 */
689 public Object nonEmptyCase(LRStruct host, Object inp) {
690 LocalEnvironment localEnv = (LocalEnvironment) host.getFirst();
691 if (localEnv.location().inSquare(loc, localEnv.direction(), 1)) {
692 return localEnv;
693 }
694 else {
695 return host.getRest().execute(this, inp);
696 }
697 }
698
699 /**
700 * Operates on an empty LRStruct host, given an input object.
701 *
702 * @param host an empty LRStruct.
703 * @param inp input object needed by this IAlgo.
704 * @return an appropriate output object.
705 */
706 public Object emptyCase(LRStruct host, Object inp) {
707 return new LocalEnvironment(loc, dir);
708 }
709 }, null);
710 }
711
712 /**
713 * Create a local environment with the given data.
714 *
715 * @param loc location
716 * @param dir direction
717 * @return new local environment
718 */
719 protected LocalEnvironment createLocalEnvironment(Location loc, Direction dir) {
720 return new LocalEnvironment(loc, dir);
721 }
722
723 /**
724 * Edit the fish.
725 *
726 * @param le local environment
727 * @param fishFactory
728 * @param button
729 * @return lambda to edit the fish
730 */
731 public ILambda editFish(ALocalEnv le, final IFishFactory fishFactory, int button) {
732 // by default, the control does not need to do anything
733 ILambda lambdaForControl = NoOpLambda.instance();
734
735 // rotate the fish
736 final LocalEnvironment localEnv = (LocalEnvironment) le;
737
738 double initialAngle = localEnv.direction().getAngle();
739 localEnv.direction().turnRight(2 * Math.PI / _rotSteps);
740
741 // check if the fish should be removed
742 if (initialAngle > localEnv.direction().getAngle()) {
743 // rotated back to initial position, remove
744
745 // set up lambda for simulation control
746 lambdaForControl = _cmdFactory.makeDeleteCmd(localEnv);
747
748 // and remove it from the environment's data
749 ILambda deleteLambda = removeFish(le);
750
751 // delete the fish from the simulation by executing the deleteLambda
752 deleteLambda.apply(null);
753 }
754
755 return lambdaForControl;
756 }
757
758 /**
759 * Factory method for parsing a stream of tokens and creating a global environment from it.
760 *
761 * @param l lexer to use
762 * @return new global environment
763 */
764 protected AGlobalEnv parseEnvironment(final Lexer l) {
765 // have to read number of steps
766 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() {
767 public Object defaultCase() {
768 throw new ParserException("Invalid token");
769 }
770
771 public Object numCase(final double rotSteps) {
772 // rotation steps was read
773 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() {
774 public Object defaultCase() {
775 throw new ParserException("Invalid token");
776 }
777
778 public Object numCase(final double red) {
779 // red was read
780 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() {
781 public Object defaultCase() {
782 throw new ParserException("Invalid token");
783 }
784
785 public Object numCase(final double green) {
786 // green was read
787 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() {
788 public Object defaultCase() {
789 throw new ParserException("Invalid token");
790 }
791
792 public Object numCase(double blue) {
793 // blue was read
794 NoGridEnv env = new NoGridEnv(_cmdFactory, _securityAdapter, (int) rotSteps,
795 new Color((int) red, (int) green, (int) blue));
796
797 // parse fish
798 env.parseFish(l);
799
800 return env;
801 }
802 });
803
804 }
805 });
806 }
807 });
808 }
809 });
810
811 }
812
813 /**
814 * Parse fish and add them to the environment.
815 *
816 * @param l parser to read from
817 */
818 protected void parseFish(final Lexer l) {
819 while (l.nextToken().execute(new DefaultTokenVisitor() {
820 public Object defaultCase() {
821 throw new ParserException("Invalid token");
822 }
823
824 public Object endCase() {
825 // end of stream
826 // return false
827 return Boolean.FALSE;
828 }
829
830 public Object wordCase(String className) {
831 // read class name
832 try {
833 // NOTE: Introduced class loader here
834 // Class fishClass = Class.forName(className);
835 Class fishClass = _securityAdapter.getClassLoader().loadClass(className);
836 Constructor fishCtor = fishClass.getConstructor(new Class[]{Color.class});
837 Random rng = RandNumGenerator.instance();
838 AFish fish = (AFish) fishCtor.newInstance(new Object[]{new Color(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256))});
839
840 // read location
841 Location loc = Location.parse(l);
842 Direction dir = Direction.parse(l);
843 LocalEnvironment localEnv = createLocalEnvironment(loc, dir);
844 ILambda addLambda = addFish(localEnv, fish);
845 addLambda.apply(null);
846
847 return Boolean.TRUE;
848 }
849 catch (Exception e) {
850 e.printStackTrace();
851 throw new ParserException(e.toString(),e);
852 }
853 }
854 }) == Boolean.TRUE) {
855 }
856 }
857
858 /**
859 * Get the environment settings class.
860 *
861 * @return environment settings class
862 */
863 public AEnvFactory makeEnvFactory() {
864 return new AEnvFactory() {
865 private JTextField _rotStepsField;
866 private JColorChooser _colorChooser;
867
868 {
869 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
870 add(new JLabel("rotation steps: "));
871 add(_rotStepsField = new JTextField("24"));
872 add(new JLabel("water color: "));
873 _colorChooser = new JColorChooser(OCEAN_BLUE);
874 add(_colorChooser);
875 }
876
877 public AGlobalEnv create() {
878 return new NoGridEnv(_cmdFactory,
879 _securityAdapter,
880 Integer.parseInt(_rotStepsField.getText()),
881 _colorChooser.getColor());
882 }
883
884 public String toString() {
885 return NoGridEnv.class.getName();
886 }
887 };
888 }
889
890 /**
891 * Print file header.
892 *
893 * @param pw PrintWriter to use
894 */
895 protected void printHeader(PrintWriter pw) {
896 pw.println(getClass().getName() + ' ' + _rotSteps + ' ' +
897 _waterColor.getRed() + ' ' + _waterColor.getGreen() + ' ' + _waterColor.getBlue());
898 }
899
900 /**
901 * Get size of the display.
902 *
903 * @return size of the display in model coordinate units.
904 */
905 public Dimension getDisplaySize() {
906 return new Dimension(PAN_SIZE, PAN_SIZE);
907 }
908
909 /**
910 * The action to be executed if the display should return home.
911 *
912 * @param sa scroll adapter
913 */
914 public void returnHome(IScrollAdapter sa) {
915 sa.setCorner((int) PAN_CENTER.x, (int) PAN_CENTER.y);
916 sa.resetScrolling();
917 }
918
919 /**
920 * Ask the model where to scroll, given where the user has scrolled. If the environment just acts like a normal
921 * panal, it should return pos without modification. If the environment recenters, it should return a position in
922 * the middle of the pan area. All coordinates are in model coordinate units.
923 *
924 * @param pos position where the user scrolled to
925 * @return position where the environment wants the view to be
926 * @see IDisplayAdapter#getPanDelta
927 */
928 public Point.Double getViewPosition(Point.Double pos) {
929 // the panel always gets recentered after moving it, so return the center position
930 return PAN_CENTER;
931 }
932
933 /**
934 * Ask the model how much to pan, given where the user scrolled. If the environment just acts like a normal panal,
935 * it should return (0,0). If the environment recenters, it should return delta without modification. All
936 * coordinates are in model coordinate units.
937 *
938 * @param delta how far the user scrolled
939 * @return how far the panel should scroll
940 * @see IDisplayAdapter#getViewPosition
941 */
942 public Point.Double getPanDelta(Point.Double delta) {
943 // we want the panel to keep track of the position, so return the delta as pan value
944 return delta;
945 }
946
947 /**
948 * Get a tool tip description for a specific place in the environment.
949 *
950 * @param p mouse coordinates
951 * @return lambda for the simulation controller to execute. Must return the tooltip string.
952 */
953 public ILambda getToolTipText(final Point.Double p) {
954 final LocalEnvironment l = createLocalEnvironment(new Location(p.x, p.y), new Direction());
955 return ((ILambda) l.execute(new ILocalEnvVisitor() {
956 public Object emptyCase(ALocalEnv host, Object param) {
957 // this field is empty
958
959 // return an ILambda that returns the string for an empty field
960 return new ILambda() {
961 public Object apply(Object param) {
962 return "(" + p.x + ',' + p.y + ") is empty";
963 }
964 };
965 }
966
967 public Object nonEmptyCase(ALocalEnv host, Object param) {
968 // this field is occupied
969
970 // return an ILambda that returns the string for an occupied
971 return new ILambda() {
972 String _fishName;
973 ALocalEnv _localEnv;
974
975 public Object apply(Object param) {
976 _fishName = "";
977 _cmdFactory.makeNotifyCmd(new ILambda() {
978 public Object apply(Object param) {
979 FishApplyParams fap = (FishApplyParams) param;
980 LocalEnvironment lenv = (LocalEnvironment) fap.localEnv();
981 if (lenv.location().inSquare(l.location(), lenv.direction(), 1)) {
982 _fishName = fap.fish().toString();
983 _localEnv = fap.localEnv();
984 }
985 return null;
986 }
987 }).apply(null);
988
989 return _fishName + " at " + _localEnv;
990 }
991 };
992 }
993 }, null));
994 }
995
996
997 /*****************************************************************************************************************
998 * Tests follow
999 *****************************************************************************************************************/
1000
1001 /**
1002 * Test cases for NoGridEnv.
1003 *
1004 * @author Mathias Ricken
1005 */
1006 public static class Test_NoGridEnv extends TestCase {
1007 private ICmdFactory _cmdFactory;
1008 private ISecurityAdapter _sm;
1009 private NoGridEnv _env;
1010 private IFishFactory _fishFactory;
1011
1012 private static final ILambda _notify = new ILambda() {
1013 public Object apply(Object param) {
1014 return "notifyCmd";
1015 }
1016 };
1017 private static final ILambda _delete = new ILambda() {
1018 public Object apply(Object param) {
1019 return "deleteCmd";
1020 }
1021 };
1022 private static final ILambda _add = new ILambda() {
1023 public Object apply(Object param) {
1024 return "addCmd";
1025 }
1026 };
1027
1028 public void setUp() throws Exception {
1029 super.setUp();
1030 _cmdFactory = new ICmdFactory() {
1031 public ILambda makeNotifyCmd(ILambda lambda) {
1032 return _notify;
1033 }
1034
1035 public ILambda makeDeleteCmd(ALocalEnv env) {
1036 return _delete;
1037 }
1038
1039 public ILambda makeAddCmd(AFish fish) {
1040 return _add;
1041 }
1042 };
1043 _sm = new ISecurityAdapter() {
1044 public void setProtected(boolean _protected) {
1045 }
1046 public ThreadGroup getFishThreadGroup() {
1047 return null;
1048 }
1049 public ClassLoader getClassLoader() {
1050 return null;
1051 }
1052 public void handleException(Throwable t) {
1053 }
1054 };
1055
1056 _env = new NoGridEnv(_cmdFactory, _sm, 24, OCEAN_BLUE);
1057
1058
1059 _fishFactory = new IFishFactory() {
1060 /**
1061 * Create a new fish.
1062 *
1063 * @return new fish
1064 */
1065 public AFish createFish() {
1066 return new GenericFish(Color.RED);
1067 }
1068 };
1069 }
1070
1071 /**
1072 * Test addFishToInternalData.
1073 */
1074 public void testAddFish() {
1075 LRStruct lrs = _env._localEnvList;
1076
1077 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1078
1079 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1080 GenericFish fTop = new GenericFish(Color.RED);
1081 fTop.setLocalEnvironment(lTop);
1082 _env.addFishToInternalData(lTop, fTop);
1083
1084 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1085
1086 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1087 GenericFish fBottom = new GenericFish(Color.RED);
1088 fBottom.setLocalEnvironment(lBottom);
1089 _env.addFishToInternalData(lBottom, fBottom);
1090
1091 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1092 }
1093
1094 /**
1095 * Test editFish.
1096 */
1097 public void testEditFish() {
1098 LRStruct lrs = _env._localEnvList;
1099
1100 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1101
1102 LocalEnvironment l = (LocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1103 GenericFish f = new GenericFish(Color.RED);
1104 f.setLocalEnvironment(l);
1105 _env.addFishToInternalData(l, f);
1106 NoGridEnv.Direction d = l.direction();
1107
1108 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1109 assertEquals(0.0, d.getAngle(), 0.01);
1110
1111 ILambda lambda;
1112 for (int i = 1; 24 >= i; ++i) {
1113 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
1114 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1115 assertEquals(i * Math.PI / 12.0, d.getAngle(), 0.01);
1116 assertEquals(NoOpLambda.instance(), lambda);
1117 }
1118
1119 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
1120 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1121 assertEquals(Math.PI / 12.0, d.getAngle(), 0.01);
1122 assertEquals(_delete, lambda);
1123 }
1124
1125 /**
1126 * Test getViewPosition.
1127 */
1128 public void testGetViewPosition() {
1129 Point.Double panCenter = PAN_CENTER;
1130
1131 assertTrue(_env.getViewPosition(new Point.Double(0, 0)).equals(panCenter));
1132 assertTrue(_env.getViewPosition(new Point.Double(1.0, 0)).equals(panCenter));
1133 assertTrue(_env.getViewPosition(new Point.Double(1.2, 0)).equals(panCenter));
1134 assertTrue(_env.getViewPosition(new Point.Double(0, 1.0)).equals(panCenter));
1135 assertTrue(_env.getViewPosition(new Point.Double(0, 1.3)).equals(panCenter));
1136 assertTrue(_env.getViewPosition(new Point.Double(-2.5, 0)).equals(panCenter));
1137 assertTrue(_env.getViewPosition(new Point.Double(-3.0, 0)).equals(panCenter));
1138 assertTrue(_env.getViewPosition(new Point.Double(0, -2.5)).equals(panCenter));
1139 assertTrue(_env.getViewPosition(new Point.Double(0, -3.0)).equals(panCenter));
1140 assertTrue(_env.getViewPosition(new Point.Double(2.0, 1.0)).equals(panCenter));
1141 assertTrue(_env.getViewPosition(new Point.Double(-4.0, -2.3)).equals(panCenter));
1142 }
1143
1144 /**
1145 * Test getPanDelta.
1146 */
1147 public void testGetPanDelta() {
1148 assertTrue(_env.getPanDelta(new Point.Double(0, 0)).equals(new Point.Double(0, 0)));
1149 assertTrue(_env.getPanDelta(new Point.Double(1.0, 0)).equals(new Point.Double(1.0, 0)));
1150 assertTrue(_env.getPanDelta(new Point.Double(1.2, 0)).equals(new Point.Double(1.2, 0)));
1151 assertTrue(_env.getPanDelta(new Point.Double(0, 1.0)).equals(new Point.Double(0, 1.0)));
1152 assertTrue(_env.getPanDelta(new Point.Double(0, 1.3)).equals(new Point.Double(0, 1.3)));
1153 assertTrue(_env.getPanDelta(new Point.Double(-2.5, 0)).equals(new Point.Double(-2.5, 0)));
1154 assertTrue(_env.getPanDelta(new Point.Double(-3.0, 0)).equals(new Point.Double(-3.0, 0)));
1155 assertTrue(_env.getPanDelta(new Point.Double(0, -2.5)).equals(new Point.Double(0, -2.5)));
1156 assertTrue(_env.getPanDelta(new Point.Double(0, -3.0)).equals(new Point.Double(0, -3.0)));
1157 assertTrue(_env.getPanDelta(new Point.Double(2.0, 1.0)).equals(new Point.Double(2.0, 1.0)));
1158 assertTrue(_env.getPanDelta(new Point.Double(-4.0, -2.3)).equals(new Point.Double(-4.0, -2.3)));
1159 }
1160 }
1161
1162 /**
1163 * Test cases for NoGridEnv.LocalEnv.
1164 *
1165 * @author Mathias Ricken
1166 */
1167 public static class Test_NoGridEnv_LocalEnv extends TestCase {
1168 private ICmdFactory _cmdFactory;
1169 private ISecurityAdapter _sm;
1170 private NoGridEnv _env;
1171
1172 private static class SuccessException extends RuntimeException {
1173 public SuccessException() {
1174 super();
1175 }
1176 }
1177
1178 private static final ILambda _notify = new ILambda() {
1179 public Object apply(Object param) {
1180 return "notifyCmd";
1181 }
1182 };
1183 private static final ILambda _delete = new ILambda() {
1184 public Object apply(Object param) {
1185 return "deleteCmd";
1186 }
1187 };
1188 private static final ILambda _add = new ILambda() {
1189 public Object apply(Object param) {
1190 return "addCmd";
1191 }
1192 };
1193
1194 public void setUp() throws Exception {
1195 super.setUp();
1196 _cmdFactory = new ICmdFactory() {
1197 public ILambda makeNotifyCmd(ILambda lambda) {
1198 return _notify;
1199 }
1200
1201 public ILambda makeDeleteCmd(ALocalEnv env) {
1202 return _delete;
1203 }
1204
1205 public ILambda makeAddCmd(AFish fish) {
1206 return _add;
1207 }
1208 };
1209 _sm = new ISecurityAdapter() {
1210 public void setProtected(boolean _protected) {
1211 }
1212 public ThreadGroup getFishThreadGroup() {
1213 return null;
1214 }
1215 public ClassLoader getClassLoader() {
1216 return null;
1217 }
1218 public void handleException(Throwable t) {
1219 }
1220 };
1221
1222 _env = new NoGridEnv(_cmdFactory, _sm);
1223 }
1224
1225 /**
1226 * Test local environment's execute.
1227 */
1228 public void testExecute() {
1229 ALocalEnv l = _env.makeLocalEnv(new Point.Double(1, 1));
1230
1231 try {
1232 l.execute(new AGlobalEnv.ILocalEnvVisitor() {
1233 public Object emptyCase(ALocalEnv host, Object param) {
1234 // ok
1235 throw new SuccessException();
1236 }
1237
1238 public Object nonEmptyCase(ALocalEnv host, Object param) {
1239 throw new RuntimeException("Should be empty");
1240 }
1241 }, null);
1242 fail("emptyCase should have been called --");
1243 }
1244 catch (SuccessException e) {
1245 }
1246
1247
1248 GenericFish f = new GenericFish(Color.RED);
1249 f.setLocalEnvironment(l);
1250 _env.addFish(l, f);
1251 try {
1252 l.execute(new AGlobalEnv.ILocalEnvVisitor() {
1253 public Object emptyCase(ALocalEnv host, Object param) {
1254 throw new RuntimeException("Should be non-empty");
1255 }
1256
1257 public Object nonEmptyCase(ALocalEnv host, Object param) {
1258 // ok
1259 throw new SuccessException();
1260 }
1261 }, null);
1262 fail("nonEmptyCase should have been called --");
1263 }
1264 catch (SuccessException e) {
1265 }
1266
1267 ALocalEnv l2 = _env.makeLocalEnv(new Point.Double(1, 1));
1268 try {
1269 l2.execute(new AGlobalEnv.ILocalEnvVisitor() {
1270 public Object emptyCase(ALocalEnv host, Object param) {
1271 throw new RuntimeException("Should be non-empty");
1272 }
1273
1274 public Object nonEmptyCase(ALocalEnv host, Object param) {
1275 // ok
1276 throw new SuccessException();
1277 }
1278 }, null);
1279 fail("nonEmptyCase should have been called --");
1280 }
1281 catch (SuccessException e) {
1282 }
1283
1284 ALocalEnv l3 = _env.makeLocalEnv(new Point.Double(1.4, 1.4));
1285 try {
1286 l3.execute(new AGlobalEnv.ILocalEnvVisitor() {
1287 public Object emptyCase(ALocalEnv host, Object param) {
1288 throw new RuntimeException("Should be non-empty");
1289 }
1290
1291 public Object nonEmptyCase(ALocalEnv host, Object param) {
1292 // ok
1293 throw new SuccessException();
1294 }
1295 }, null);
1296 fail("nonEmptyCase should have been called --");
1297 }
1298 catch (SuccessException e) {
1299 }
1300
1301
1302 ALocalEnv l3b = _env.makeLocalEnv(new Point.Double(1.4, 1.6));
1303 try {
1304 l3b.execute(new AGlobalEnv.ILocalEnvVisitor() {
1305 public Object emptyCase(ALocalEnv host, Object param) {
1306 // ok
1307 throw new SuccessException();
1308 }
1309
1310 public Object nonEmptyCase(ALocalEnv host, Object param) {
1311 throw new RuntimeException("Should be empty");
1312 }
1313 }, null);
1314 fail("emptyCase should have been called --");
1315 }
1316 catch (SuccessException e) {
1317 }
1318
1319
1320 ALocalEnv l4 = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1321 try {
1322 l4.execute(new AGlobalEnv.ILocalEnvVisitor() {
1323 public Object emptyCase(ALocalEnv host, Object param) {
1324 // ok
1325 throw new SuccessException();
1326 }
1327
1328 public Object nonEmptyCase(ALocalEnv host, Object param) {
1329 throw new RuntimeException("Should be empty");
1330 }
1331 }, null);
1332 fail("emptyCase should have been called --");
1333 }
1334 catch (SuccessException e) {
1335 }
1336
1337
1338 GenericFish f4 = new GenericFish(Color.RED);
1339 f4.setLocalEnvironment(l4);
1340 _env.addFish(l4, f4);
1341 try {
1342 l4.execute(new AGlobalEnv.ILocalEnvVisitor() {
1343 public Object emptyCase(ALocalEnv host, Object param) {
1344 throw new RuntimeException("Should be non-empty");
1345 }
1346
1347 public Object nonEmptyCase(ALocalEnv host, Object param) {
1348 // ok
1349 throw new SuccessException();
1350 }
1351 }, null);
1352 fail("nonEmptyCase should have been called --");
1353 }
1354 catch (SuccessException e) {
1355 }
1356 }
1357
1358 /**
1359 * Test local environment's tryMoveFwd.
1360 */
1361 public void testTryMoveFwd() {
1362 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1363 GenericFish fTop = new GenericFish(Color.RED);
1364 fTop.setLocalEnvironment(lTop);
1365 _env.addFish(lTop, fTop);
1366
1367 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1368 GenericFish fBottom = new GenericFish(Color.RED);
1369 fBottom.setLocalEnvironment(lBottom);
1370 _env.addFish(lBottom, fBottom);
1371
1372 // move lBottom into lTop --> blocked
1373 Integer i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
1374 public Object apply(Object param) {
1375 // ok
1376 return new Integer(456);
1377 }
1378 }, new IOpenCommand() {
1379 public Object apply(Object param) {
1380 throw new RuntimeException("Should be blocked");
1381 }
1382 });
1383 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1384 new Integer(456),
1385 i);
1386
1387 // move lTop --> open, don't move
1388 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
1389 public Object apply(Object param) {
1390 throw new RuntimeException("Should be open");
1391 }
1392 }, new IOpenCommand() {
1393 public Object apply(Object param) {
1394 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1395 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1396 DeactivatableLambda.class,
1397 param.getClass());
1398 // ok
1399 return new Integer(123);
1400 }
1401 });
1402 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
1403
1404 // move lBottom into lTop --> blocked
1405 i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
1406 public Object apply(Object param) {
1407 // ok
1408 return new Integer(789);
1409 }
1410 }, new IOpenCommand() {
1411 public Object apply(Object param) {
1412 throw new RuntimeException("Should be blocked");
1413 }
1414 });
1415 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1416 new Integer(789),
1417 i);
1418
1419 // move lTop --> open, move
1420 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
1421 public Object apply(Object param) {
1422 throw new RuntimeException("Should be open");
1423 }
1424 }, new IOpenCommand() {
1425 public Object apply(Object param) {
1426 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1427 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1428 DeactivatableLambda.class,
1429 param.getClass());
1430 // ok, make move
1431 ((ILambda) param).apply(null);
1432 return new Integer(111);
1433 }
1434 });
1435 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(111), i);
1436
1437 // move lBottom --> open
1438 i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
1439 public Object apply(Object param) {
1440 throw new RuntimeException("Should be open");
1441 }
1442 }, new IOpenCommand() {
1443 public Object apply(Object param) {
1444 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1445 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1446 DeactivatableLambda.class,
1447 param.getClass());
1448 // ok
1449 return new Integer(222);
1450 }
1451 });
1452 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(222), i);
1453
1454 // move lTop --> open, don't move
1455 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
1456 public Object apply(Object param) {
1457 throw new RuntimeException("Should be open");
1458 }
1459 }, new IOpenCommand() {
1460 public Object apply(Object param) {
1461 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1462 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1463 DeactivatableLambda.class,
1464 param.getClass());
1465 // ok
1466 return new Integer(333);
1467 }
1468 });
1469 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(333), i);
1470
1471 // turn and move lTop --> open, don't move
1472 lTop.turnRight(fTop, Math.PI / 2.0);
1473 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
1474 public Object apply(Object param) {
1475 throw new RuntimeException("Should be open");
1476 }
1477 }, new IOpenCommand() {
1478 public Object apply(Object param) {
1479 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1480 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1481 DeactivatableLambda.class,
1482 param.getClass());
1483 // ok
1484 return new Integer(444);
1485 }
1486 });
1487 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(444), i);
1488 }
1489
1490 /**
1491 * Test local environment's tryBreedFwd.
1492 */
1493 public void testTryBreedFwd() {
1494 LRStruct lrs = _env._localEnvList;
1495
1496 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1497
1498 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1499 GenericFish fTop = new GenericFish(Color.RED);
1500 fTop.setLocalEnvironment(lTop);
1501 _env.addFish(lTop, fTop);
1502
1503 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1504
1505 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1506 GenericFish fBottom = new GenericFish(Color.RED);
1507 fBottom.setLocalEnvironment(lBottom);
1508 _env.addFish(lBottom, fBottom);
1509
1510 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1511
1512 // breed lBottom into lTop --> blocked
1513 Integer i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
1514 public Object apply(Object param) {
1515 // ok
1516 return new Integer(456);
1517 }
1518 }, new IOpenCommand() {
1519 public Object apply(Object param) {
1520 throw new RuntimeException("Should be blocked");
1521 }
1522 });
1523 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1524 new Integer(456),
1525 i);
1526
1527 // breed lTop --> open, don't breed
1528 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1529 public Object apply(Object param) {
1530 throw new RuntimeException("Should be open");
1531 }
1532 }, new IOpenCommand() {
1533 public Object apply(Object param) {
1534 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1535 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1536 DeactivatableLambda.class,
1537 param.getClass());
1538 // ok
1539 return new Integer(123);
1540 }
1541 });
1542 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
1543
1544 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1545
1546 // breed lBottom into lTop --> blocked
1547 i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
1548 public Object apply(Object param) {
1549 // ok
1550 return new Integer(456);
1551 }
1552 }, new IOpenCommand() {
1553 public Object apply(Object param) {
1554 throw new RuntimeException("Should be blocked");
1555 }
1556 });
1557 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1558 new Integer(456),
1559 i);
1560
1561 // breed lTop --> open, breed
1562 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1563 public Object apply(Object param) {
1564 throw new RuntimeException("Should be open");
1565 }
1566 }, new IOpenCommand() {
1567 public Object apply(Object param) {
1568 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1569 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1570 DeactivatableLambda.class,
1571 param.getClass());
1572 // ok, breed
1573 ((ILambda) param).apply(null);
1574 return new Integer(123);
1575 }
1576 });
1577 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
1578
1579 assertEquals(3, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1580
1581 // breed lBottom into lTop --> blocked
1582 i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
1583 public Object apply(Object param) {
1584 // ok
1585 return new Integer(456);
1586 }
1587 }, new IOpenCommand() {
1588 public Object apply(Object param) {
1589 throw new RuntimeException("Should be blocked");
1590 }
1591 });
1592 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1593 new Integer(456),
1594 i);
1595
1596 // breed lTop --> blocked
1597 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1598 public Object apply(Object param) {
1599 // ok
1600 return new Integer(456);
1601 }
1602 }, new IOpenCommand() {
1603 public Object apply(Object param) {
1604 throw new RuntimeException("Should be blocked");
1605 }
1606 });
1607 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1608 new Integer(456),
1609 i);
1610
1611 // turn and breed lTop --> open, don't breed
1612 lTop.turnRight(fTop, Math.PI / 2.0);
1613 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1614 public Object apply(Object param) {
1615 throw new RuntimeException("Should be open");
1616 }
1617 }, new IOpenCommand() {
1618 public Object apply(Object param) {
1619 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1620 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1621 DeactivatableLambda.class,
1622 param.getClass());
1623 // ok
1624 return new Integer(789);
1625 }
1626 });
1627 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i);
1628
1629 assertEquals(3, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1630
1631 // turn and breed lTop --> open, breed
1632 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1633 public Object apply(Object param) {
1634 throw new RuntimeException("Should be open");
1635 }
1636 }, new IOpenCommand() {
1637 public Object apply(Object param) {
1638 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1639 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1640 DeactivatableLambda.class,
1641 param.getClass());
1642 // ok, breed
1643 ((ILambda) param).apply(null);
1644 return new Integer(789);
1645 }
1646 });
1647 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i);
1648
1649 assertEquals(4, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1650
1651 // turn and breed lTop --> blocked
1652 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1653 public Object apply(Object param) {
1654 // ok
1655 return new Integer(789);
1656 }
1657 }, new IOpenCommand() {
1658 public Object apply(Object param) {
1659 throw new RuntimeException("Should be blocked");
1660 }
1661 });
1662 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1663 new Integer(789),
1664 i);
1665
1666 assertEquals(4, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1667 }
1668
1669 /**
1670 * Test local environment's turnRight.
1671 */
1672 public void testTurnRight() {
1673 LocalEnvironment l = (LocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1674 GenericFish f = new GenericFish(Color.RED);
1675 f.setLocalEnvironment(l);
1676 _env.addFish(l, f);
1677 NoGridEnv.Direction d = l.direction();
1678
1679 assertEquals(0.0, d.getAngle(), 0.01);
1680 l.turnRight(f, Math.PI / 2);
1681 assertEquals(Math.PI / 2.0, d.getAngle(), 0.01);
1682 l.turnRight(f, Math.PI / 2);
1683 assertEquals(Math.PI, d.getAngle(), 0.01);
1684 l.turnRight(f, Math.PI / 2);
1685 assertEquals(3 * Math.PI / 2.0, d.getAngle(), 0.01);
1686 l.turnRight(f, Math.PI / 2);
1687 assertEquals(2 * Math.PI, d.getAngle(), 0.01);
1688 }
1689
1690 /**
1691 * Test local environment's removeFish.
1692 */
1693 public void testRemoveFish() {
1694 LRStruct lrs = _env._localEnvList;
1695
1696 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1697
1698 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1699 GenericFish fTop = new GenericFish(Color.RED);
1700 fTop.setLocalEnvironment(lTop);
1701 _env.addFish(lTop, fTop);
1702
1703 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1704
1705 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1706 GenericFish fBottom = new GenericFish(Color.RED);
1707 fBottom.setLocalEnvironment(lBottom);
1708 _env.addFish(lBottom, fBottom);
1709
1710 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1711
1712 lTop.removeFish(fTop);
1713
1714 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1715
1716 lBottom.removeFish(fBottom);
1717
1718 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
1719 }
1720
1721 /**
1722 * Test to make sure only one move lambda can be executed.
1723 */
1724 public void testOnlyOneMove() {
1725 LocalEnvironment l = (LocalEnvironment) _env.makeLocalEnv(new Point.Double(5.0, 5.0));
1726 GenericFish f = new GenericFish(Color.RED);
1727 f.setLocalEnvironment(l);
1728 _env.addFish(l, f);
1729
1730 final LinkedList<ILambda> lambdas = new LinkedList<ILambda>();
1731 l.tryMoveFwd(f, new IBlockedCommand() {
1732 public Object apply(Object param) {
1733 throw new RuntimeException("Should be open");
1734 }
1735 }, new IOpenCommand() {
1736 public Object apply(Object param) {
1737 lambdas.add((ILambda)param);
1738 return null;
1739 }
1740 });
1741 f.turnRight();
1742 l.tryMoveFwd(f, new IBlockedCommand() {
1743 public Object apply(Object param) {
1744 throw new RuntimeException("Should be open");
1745 }
1746 }, new IOpenCommand() {
1747 public Object apply(Object param) {
1748 lambdas.add((ILambda)param);
1749 return null;
1750 }
1751 });
1752
1753 assertEquals(2, lambdas.size());
1754
1755 lambdas.get(0).apply(null);
1756 NoGridEnv.Location loc = l.location();
1757 assertTrue(loc.same(new NoGridEnv.Location(5.0, 4.0)));
1758
1759 lambdas.get(1).apply(null);
1760 loc = l.location();
1761 assertTrue(loc.same(new NoGridEnv.Location(5.0, 4.0)));
1762
1763 lambdas.clear();
1764 l.tryMoveFwd(f, new IBlockedCommand() {
1765 public Object apply(Object param) {
1766 throw new RuntimeException("Should be open");
1767 }
1768 }, new IOpenCommand() {
1769 public Object apply(Object param) {
1770 lambdas.add((ILambda)param);
1771 return null;
1772 }
1773 });
1774 f.turnRight();
1775 l.tryMoveFwd(f, new IBlockedCommand() {
1776 public Object apply(Object param) {
1777 throw new RuntimeException("Should be open");
1778 }
1779 }, new IOpenCommand() {
1780 public Object apply(Object param) {
1781 lambdas.add((ILambda)param);
1782 return null;
1783 }
1784 });
1785
1786 assertEquals(2, lambdas.size());
1787
1788 lambdas.get(1).apply(null);
1789 loc = l.location();
1790 assertTrue(loc.same(new NoGridEnv.Location(6.0, 4.0)));
1791
1792 lambdas.get(0).apply(null);
1793 loc = l.location();
1794 assertTrue(loc.same(new NoGridEnv.Location(6.0, 4.0)));
1795 }
1796 }
1797
1798 }