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 }