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 }