001    package sysModel.fish;
002    
003    import model.ILambda;
004    import model.RandNumGenerator;
005    import sysModel.env.AGlobalEnv;
006    import sysModel.env.FishApplyParams;
007    import sysModel.env.IBlockedCommand;
008    import sysModel.env.IOpenCommand;
009    
010    import java.awt.*;
011    import java.util.Observable;
012    import java.util.Observer;
013    
014    /**
015     * Behavior-controlled fish. *
016     *
017     * @author Mathias G. Ricken
018     */
019    public abstract class AFish implements Cloneable, Observer {
020        /**
021         * The local environment of the fish.
022         */
023        private AGlobalEnv.ALocalEnv _localEnv;
024    
025        /**
026         * The probability that this fish attempts to breed this timestep. Subclasses can override this value.
027         */
028        protected double _probOfBreeding = 0; // 1.0f/7.0f;
029    
030        /**
031         * The probability that this fish will die in this timestep. Subclasses can override this value.
032         */
033        protected double _probOfDying = 0; // 1.0f/5.0f;
034    
035        /**
036         * Color of the fish.
037         */
038        protected Color _fishColor;
039    
040        /**
041         * Display for the fish.
042         */
043        protected IFishDisplay _fishDisplay;
044    
045        /**
046         * Create a new fish.
047         *
048         * @param fishColor    fish color
049         * @param fishDisplay  fish display
050         */
051        public AFish(Color fishColor, IFishDisplay fishDisplay) {
052            _fishColor = fishColor;
053            _fishDisplay = fishDisplay;
054        }
055    
056        /**
057         * Set the local environment of this fish.
058         *
059         * @param localEnv local environment
060         */
061        public final void setLocalEnvironment(AGlobalEnv.ALocalEnv localEnv) {
062            _localEnv = localEnv;
063        }
064    
065        /**
066         * Attempt to move the fish forward, which may or may not be successful. The behavior in each case is defined by the
067         * visitor: - If the move cannot be executed, the blockedCmd lambda is applied. The parameter is not used and set to
068         * null. - If the move can be executed, the openCmd lambda is applied. The parameter is an ILambda that can to be
069         * executed to actually move the fish to the target location of this move. The ILambda ignores the input parameter
070         * and returns null.
071         *
072         * @param blockedCmd lambda to apply if blocked
073         * @param openCmd    lambda to apply if open
074         * @return return value of lambda executed
075         */
076        public final Object tryMoveFwd(final IBlockedCommand blockedCmd, final IOpenCommand openCmd) {
077            // TODO: PART3
078            return null;
079        }
080    
081        /**
082         * Attempt to breed the fish forward, which may or may not be successful. The behavior in each case is defined by
083         * the visitor: - If the breeding cannot be executed, the blockedCmd lambda is applied. The parameter is not used
084         * and set to null. - If the breeding can be executed, the openCmd lambda is applied. The parameter is an ILambda
085         * that can to be executed to actually put a clone of the fish into the target location of this breeding attempt.
086         * The ILambda ignores the input parameter and returns null.
087         *
088         * @param blockedCmd lambda to apply if blocked
089         * @param openCmd    lambda to apply if open
090         * @return return value of lambda executed
091         */
092        public final Object tryBreedFwd(final IBlockedCommand blockedCmd, final IOpenCommand openCmd) {
093            // TODO: PART3
094            return null;
095        }
096    
097        /**
098         * Draw the fish on the graphics object. The graphics object still has to be translated and rotated properly,
099         *
100         * @param g    graphics object to drawFish on
101         * @param comp component to drawFish on
102         */
103        public final void draw(Graphics2D g, Component comp) {
104            _localEnv.drawFish(this, g, comp);
105        }
106    
107        /**
108         * Turn the fish radians to the right.
109         *
110         * @param radians radians to turn
111         */
112        public final void turnRight(double radians) {
113            _localEnv.turnRight(this, radians);
114        }
115    
116        /**
117         * Turn the fish radians to the left.
118         *
119         * @param radians radians to turn
120         */
121        public final void turnLeft(double radians) {
122            _localEnv.turnRight(this, -radians);
123        }
124    
125        /**
126         * Turn the fish Pi/2 radians to the right.
127         */
128        public final void turnRight() {
129            turnRight(Math.PI / 2);
130        }
131    
132        /**
133         * Turn the fish Pi/2 radians to the left.
134         */
135        public final void turnLeft() {
136            turnLeft(Math.PI / 2);
137        }
138    
139        /**
140         * Let the fish die, remove it from the environment and the simulation.
141         */
142        public final void die() {
143            _localEnv.removeFish(this);
144        }
145    
146        /**
147         * Execute a simulation step. This function acts as a template method. It always calls the breed, move and age
148         * functions, which can be overridden, though.
149         */
150        public final void act() {
151            breed();
152            move();
153            age();
154        }
155    
156        /**
157         * Execute the breeding part of a simulation step.
158         */
159        protected void breed() {
160            if (RandNumGenerator.instance().nextDouble() < _probOfBreeding) {
161                // breed
162                // by default, a fish breeds into all open spaces around it
163                final IBlockedCommand blocked1 = new IBlockedCommand() {
164                    public Object apply(Object param) {
165                        // turn PI/2 radians to the right to face in the original direction
166                        turnRight();
167                        return null;
168                    }
169                };
170    
171                final IBlockedCommand blocked2 = new IBlockedCommand() {
172                    public Object apply(Object param) {
173                        // turn PI/2 radians to the right and attempt to breed forward
174                        turnRight();
175                        return tryBreedFwd(blocked1, new IOpenCommand() {
176                            public Object apply(Object param) {
177                                // the field to the right is open
178                                ((ILambda) param).apply(null);
179                                return blocked1.apply(null);
180                            }
181                        });
182                    }
183                };
184    
185                final IBlockedCommand blocked3 = new IBlockedCommand() {
186                    public Object apply(Object param) {
187                        // turn PI/2 radians to the right and attempt to breed forward
188                        turnRight();
189                        return tryBreedFwd(blocked2, new IOpenCommand() {
190                            public Object apply(Object param) {
191                                // the field to the right is open
192                                ((ILambda) param).apply(null);
193                                return blocked2.apply(null);
194                            }
195                        });
196                    }
197                };
198    
199                final IBlockedCommand blocked4 = new IBlockedCommand() {
200                    public Object apply(Object param) {
201                        // turn PI/2 radians to the right and attempt to breed forward
202                        turnRight();
203                        return tryBreedFwd(blocked3, new IOpenCommand() {
204                            public Object apply(Object param) {
205                                // the field straight ahead is open
206                                ((ILambda) param).apply(null);
207                                return blocked3.apply(null);
208                            }
209                        });
210                    }
211                };
212    
213                // attempt to breed forward
214                tryBreedFwd(blocked4, new IOpenCommand() {
215                    public Object apply(Object param) {
216                        // the field to the left is open
217                        ((ILambda) param).apply(null);
218                        return blocked4.apply(null);
219                    }
220                });
221            }
222        }
223    
224        /**
225         * Execute the aging part of a simulation step.
226         */
227        protected void age() {
228            if (RandNumGenerator.instance().nextDouble() < _probOfDying) {
229                die();
230            }
231        }
232    
233        /**
234         * Return a clone this instance.
235         *
236         * @return clone
237         */
238        public Object clone() {
239            Object clone;
240            try {
241                clone = super.clone();
242            }
243            catch (CloneNotSupportedException e) {
244                throw new FishException(e);
245            }
246            return clone;
247        }
248    
249        /**
250         * Return a string representation of this fish.
251         *
252         * @return string representation
253         */
254        public String toString() {
255            String className = this.getClass().getName();
256            return className.substring(className.lastIndexOf('.') + 1);
257        }
258    
259        /**
260         * Execute the movement part of a simulation step. PART4
261         */
262        protected abstract void move();
263    
264        /**
265         * Draw the fish. The Graphics2D object has been translated, rotated and scaled so that the origin is in the center
266         * of the fish. The fish should just be drawn from (-0.5, -0.5) to (0.5, 0.5).
267         *
268         * @param g    graphics object to drawFish on.
269         * @param comp the component to drawFish on
270         */
271        public void paint(Graphics2D g, Component comp) {
272            _fishDisplay.draw(g, comp, getColor());
273        }
274    
275        /**
276         * Get the fish's color.
277         *
278         * @return color of the fish
279         */
280        public Color getColor() {
281            return _fishColor;
282        }
283    
284        /**
285         * This method is called whenever the observed object is changed. The fish executes the lambda that is passed in
286         * with the fish itself as parameter.
287         *
288         * @param o   the observable object
289         * @param arg lambda to execute
290         */
291        public final void update(final Observable o, Object arg) {
292            try {
293                ((ILambda) arg).apply(new FishApplyParams(this, _localEnv));
294            }
295            catch (Throwable t) {
296                // let fish die
297                die();
298                System.gc();
299            }
300        }
301    
302        /**
303         * Set probability of dying.
304         *
305         * @param p the probability
306         */
307        public void setProbOfDying(double p) {
308            _probOfDying = p;
309        }
310    
311        /**
312         * Set probability of breeding.
313         *
314         * @param p the probability
315         */
316        public void setProbOfBreeding(double p) {
317            _probOfBreeding = p;
318        }
319    }