def roam(self): self.hunger += Boid.hungerRate # Flocking (with arbitrary weight) steer = self.align() * 1.1 steer += self.cohesion() * 0.9 steer += self.separation() * 1. steer = vu.capMag(steer, Boid.maxForce * 2.) return steer
def manageHunger(self): hungry = ( self.hunger >= Predator.hungerThld or (self.eating is not None and self.hunger > Predator.hungerRate) ) # Or eating but not full self.maxSpeed = Predator.maxRoamSpeed if hungry: self.maxSpeed = Predator.maxHungrySpeed # Still eating (immobile) if self.eating is not None: if self.eatCounter < Predator.eatDuration: self.eatCounter += 1 if self.eatCounter % 10 == 0: # Blinking if (self.eatCounter // 10) % 2 == 0: self.canvas.itemconfig(self.eating.id, state='normal') else: self.canvas.itemconfig(self.eating.id, state='hidden') else: self.hunger -= Predator.nrgPerBoid if DEBUG_LOG: print('Eaten boid:', self.id) self.eating.killed() self.eating = None self.eatCounter = 0 self.acc *= 0. self.vxt *= 0. # Look for boid else: closestBoid = self.getClosestBoid() if closestBoid is None: # Keep looking self.acc += self.roam() else: self.maxSpeed = Predator.maxHuntSpeed sqrDist = closestBoid.distanceSqr(self) # Close enough to eat if sqrDist <= PRED_HH**2: self.eating = closestBoid self.eatCounter = 1 closestBoid.caught = True print('Caught pred:', self.id) self.acc *= 0. self.vxt *= 0. # Go toward boid else: self.hunger += Predator.hungerRate steer = vu.wrapRelativePos(self.pos, closestBoid.pos) - self.pos steer = vu.setMag(steer, Predator.maxHuntSpeed) steer -= self.vel steer = vu.capMag(steer, Predator.maxForce * 4.) self.acc = steer return hungry
def manageHunger(self): hungry = (self.hunger >= Boid.hungerThld or (self.eating is not None and self.hunger > 0) ) # Or eating but not full if hungry: bite = -1. if self.eating is not None: bite = self.eating.takeBite() # Still eating (immobile) if bite > 0.: self.hunger -= bite * Boid.floraNrgRatio self.acc *= 0. self.vxt *= 0. # Look for flora else: self.eating = None closestFlora = self.getBiggestClosestFlora() if closestFlora is None: # Keep looking self.acc += self.roam() else: sqrDist = self.distanceSqr(closestFlora) nearDist = max(BOID_HH**2, closestFlora.rad**2) # Close enough to eat if sqrDist <= nearDist: bite = closestFlora.takeBite() if bite > 0.: self.hunger -= bite * Boid.floraNrgRatio self.eating = closestFlora self.acc *= 0. self.vxt *= 0. # Go toward flora else: self.hunger += Boid.hungerRate steer = vu.wrapRelativePos(self.pos, closestFlora.pos) - self.pos steer = vu.setMag(steer, Boid.maxRoamSpeed) steer -= self.vel steer = vu.capMag(steer, Boid.maxForce * 2) self.acc = steer return hungry
def align(self): steer = np.array([0., 0.]) sum = np.array([0., 0.]) nbr = 0 cells = self.getNearCells() for cell in cells: for boid in Boid.grid[cell[0]][cell[1]]: if boid is not self and boid.eating is None and self.distanceSqr( boid) <= VISION_RADSQR: sum += boid.vel nbr += 1 # Apply if nbr > 0: steer = sum / nbr steer = vu.setMag(steer, Boid.maxRoamSpeed) steer -= self.vel steer = vu.capMag(steer, Boid.maxForce) return steer
def separation(self): steer = np.array([0., 0.]) sum = np.array([0., 0.]) nbr = 0 cells = self.getNearCells() for cell in cells: for boid in Boid.grid[cell[0]][cell[1]]: sqrDist = self.distanceSqr(boid) if boid is not self and boid.eating is None and sqrDist <= VISION_RADSQR * 0.5: if sqrDist < EPSILON: sqrDist = EPSILON diff = self.pos - vu.wrapRelativePos(self.pos, boid.pos) sum += vu.setMag(diff, 1. / sqrDist) nbr += 1 # Apply if nbr > 0: steer = sum / nbr steer = vu.setMag(steer, Boid.maxRoamSpeed) steer -= self.vel steer = vu.capMag(steer, Boid.maxForce) return steer
def updatePos(self): # Update state self.acc *= self.inertiaRatio # Reproduction self.manageReproduction() # Show stoppers if self.caught or self.starved(): return # Escape predator escaping = self.managePredators() if not escaping: self.maxSpeed = Boid.maxRoamSpeed # Hungry hungry = self.manageHunger() # Roam free if not hungry: self.eating = None self.mating() self.acc += self.roam() else: self.eating = None self.maxSpeed = Boid.maxEscapeSpeed # Special case: immobile -> random push if self.eating is None and vu.norm(self.acc) < EPSILON and vu.norm( self.vxt) < EPSILON: self.acc = vu.randomUnitVector() * Boid.maxForce # if DEBUG_LOG: print('Random push:', self.id) # No deceleration if not at full speed if vu.norm(self.vxt) < self.maxSpeed: self.inertiaRatio = 1. else: self.inertiaRatio = Boid.inertiaRatio # Apply acceleration self.acc = vu.capMag(self.acc, Boid.maxForce * 8.) # Saturate acc self.vxt += self.acc self.vxt = vu.capMag(self.vxt, self.maxSpeed) # Saturate vel self.nxt = np.add(self.nxt, self.vxt) # Update position + rotation if ((self.acc[0] != 0. or self.acc[1] != 0.) and (self.vxt[0] != 0. or self.vxt[1] != 0)): # Warp around vu.wrapEdges(self) self.points = vu.updateTriPoints(self.nxt, self.vel, BOID_HH, BOID_HW) # Update position only else: # Move self.points = [ self.points[0] + self.vxt[0], self.points[1] + self.vxt[1], self.points[2] + self.vxt[0], self.points[3] + self.vxt[1], self.points[4] + self.vxt[0], self.points[5] + self.vxt[1] ] # Warp around vu.wrapEdges(self, True) # Apply if (self.pos[0] != self.nxt[0] or self.pos[1] != self.nxt[1] or self.vel[0] != self.vxt[0] or self.vel[1] != self.vxt[1]): self.canvas.coords(self.id, *self.points) self.canvas.update_idletasks()