def _search_for_path(self, border_func, end_func, max_steps): paths = [None] * 4 paths[0] = self._check_steps(Position(-1, 0), border_func, end_func, max_steps) paths[1] = self._check_steps(Position(0, -1), border_func, end_func, max_steps) paths[2] = self._check_steps(Position(1, 0), border_func, end_func, max_steps) paths[3] = self._check_steps(Position(0, 1), border_func, end_func, max_steps) cands = [x for x in paths if x is not None] if not cands: return None elif len(cands) == 1: return cands[0][1:] # take the end point closest to our target final_path = cands[0] min_dist = final_path[-1].dist(self.target) for this_path in cands[1:]: dist = this_path[-1].dist(self.target) if dist < min_dist: min_dist = dist final_path = this_path elif dist == min_dist and random.randint(0, 1) == 0: final_path = this_path return final_path[1:] # path's include self.pos
def _find_best_path_step(self): """Find the cheapest path to final_pos, and return the next step along the path.""" if self.path: next_step = self.path.pop(0) if next_step.dist(self.pos) < 2: return next_step else: # Been bounced off the path self.path = [] new_pos = None if self.target.z < self.pos.z: # We need to try heading down. new_pos = Position(self.pos.x, self.pos.y, self.pos.z - 1) if self.target.x == self.pos.x and self.target.y == self.pos.y and \ self.target.z > self.pos.z: # We try heading up new_pos = Position(self.pos.x, self.pos.y, self.pos.z + 1) if new_pos: if new_pos in self._last_steps: # ladder, so we allow backtracking self._last_steps.remove(new_pos) return new_pos cur_dist = self.target.dist(self.pos) if cur_dist < 2: # We're right ontop of our target, so just go there return self.target # Find the cheapest spot close to us that moves us closer to the target best, min_cost = self._find_min_cost_neighbour(self.target) if min_cost < 20 or not self.gameboard.in_bounds(self.pos) \ or not self.gameboard.in_bounds(best): # If we're not on the gameboard yet, there's no point in looking # for an optimal path. return best # Else expensive step, so think further if self._is_fence(best): path = self._find_fence_gap() elif min_cost == 30: # building path = self._find_nearest_corner() else: # We're looping self._last_steps = [] return self.pos if path: self.path = path[1:] # exclude 1st step return path[0] return best
def _calc_next_move(self): """Find the path to the target""" if self.hunting: # Check if we need to update our idea of a target if self.closest and self.closest in self.gameboard.chickens: stealth = self.closest.get_stealth() roll = random.randint(1, 100) is_visible = roll > stealth if not is_visible: self._select_prey() elif not self.target: self.target = self.closest.pos else: # Either no target, or someone ate it self._select_prey() if not self.target: self.target = self.start_pos self._last_steps = [] if self.target == self.pos: # No need to move, but we will need to update the target self.target = None return self.pos if self.target.to_tile_tuple() == self.pos.to_tile_tuple(): # Only differ in z, so next step is in z if self.target.z < self.pos.z: new_z = self.pos.z - 1 else: new_z = self.pos.z + 1 return Position(self.pos.x, self.pos.y, new_z) return self._find_best_path_step()
def __init__(self, tile_pos, gameboard): # load images self._image_left = imagecache.load_image(self.IMAGE_FILE) self._image_right = imagecache.load_image(self.IMAGE_FILE, ("right_facing", )) # Create the animal somewhere far off screen Sprite.__init__(self, self._image_left, (-1000, -1000)) self.image_left = self._image_left.copy() self.image_right = self._image_right.copy() if hasattr(tile_pos, 'to_tile_tuple'): self.pos = tile_pos else: self.pos = Position(tile_pos[0], tile_pos[1], 0) self.equipment = [] self.accoutrements = [] self.abode = None self.facing = 'left' self.gameboard = gameboard
def move(self): """A free chicken will wander around aimlessly""" pos_x, pos_y = self.pos.to_tile_tuple() surrounds = [ Position(pos_x + dx, pos_y + dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] ] pos_options = [ pos for pos in surrounds if self.gameboard.in_bounds(pos) and self. gameboard.tv.get(pos.to_tile_tuple()) == self.gameboard.GRASSLAND and not self.gameboard.get_outside_chicken(pos.to_tile_tuple()) ] + [self.pos] self.pos = pos_options[random.randint(0, len(pos_options) - 1)]
def chop(self): if self.has_axe(): pos_x, pos_y = self.pos.to_tile_tuple() surrounds = [ Position(pos_x + dx, pos_y + dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] ] tree_options = [ pos for pos in surrounds if self.gameboard.in_bounds(pos) and self.gameboard.is_woodland_tile(pos) ] if tree_options: num_trees_to_cut = random.randint(1, len(tree_options)) trees_to_cut = random.sample(tree_options, num_trees_to_cut) for tree_pos in trees_to_cut: self.gameboard.add_wood(5) self.gameboard.tv.set(tree_pos.to_tile_tuple(), self.gameboard.GRASSLAND)
def _update_pos(self, new_pos): """Update the position, making sure we don't step on other foxes""" if not self.hunting and not self.gameboard.in_bounds(self.pos): self.safe = True return self.pos if new_pos == self.pos: # We're not moving, so we can skip all the checks return new_pos blocked = self.gameboard.get_animal_at_pos(new_pos, 'fox') is not None final_pos = new_pos if blocked: if new_pos.z != self.pos.z or self.pos.z != 0: # We can only move up and down a ladder moves = [ Position(self.pos.x, self.pos.y, z) for z in range(self.pos.z - 1, self.pos.z + 2) if z >= 0 ] else: moves = [self.pos + step for step in NEIGHBOUR_8] # find the cheapest point in moves that's not blocked final_pos = None min_cost = 1000 for poss in moves: if self.gameboard.get_animal_at_pos(poss, 'fox'): continue # blocked cost = self._cost_tile(poss) if cost < min_cost: min_cost = cost final_pos = poss if cost == min_cost and random.randint(0, 1) > 0: # Add some randomness in this case final_pos = poss if not final_pos: # No good choice, so stay put return self.pos if self._is_fence(final_pos) and not self.dig_pos: return self._dig(final_pos) self._last_steps.append(final_pos) if len(self._last_steps) > 6: self._last_steps.pop(0) return final_pos
class Animal(Sprite, serializer.Simplifiable): """Base class for animals""" STEALTH = 0 VISION_BONUS = 0 VISION_RANGE_PENALTY = 10 # sub-class must set this to the name of an image # file IMAGE_FILE = None SIMPLIFY = [ 'pos', 'equipment', 'accoutrements', 'abode', 'facing', 'gameboard', ] def __init__(self, tile_pos, gameboard): # load images self._image_left = imagecache.load_image(self.IMAGE_FILE) self._image_right = imagecache.load_image(self.IMAGE_FILE, ("right_facing", )) # Create the animal somewhere far off screen Sprite.__init__(self, self._image_left, (-1000, -1000)) self.image_left = self._image_left.copy() self.image_right = self._image_right.copy() if hasattr(tile_pos, 'to_tile_tuple'): self.pos = tile_pos else: self.pos = Position(tile_pos[0], tile_pos[1], 0) self.equipment = [] self.accoutrements = [] self.abode = None self.facing = 'left' self.gameboard = gameboard @classmethod def make(cls): """Override default Simplifiable object creation.""" return cls((0, 0), None) @classmethod def unsimplify(cls, *args, **kwargs): """Override default Simplifiable unsimplification.""" obj = super(Animal, cls).unsimplify(*args, **kwargs) obj.redraw() return obj def loop(self, tv, _sprite): ppos = tv.tile_to_view(self.pos.to_tile_tuple()) self.rect.x = ppos[0] self.rect.y = ppos[1] def die(self): """Play death animation, noises, whatever.""" if hasattr(self, 'DEATH_SOUND'): sound.play_sound(self.DEATH_SOUND) if hasattr(self, 'DEATH_ANIMATION'): self.DEATH_ANIMATION(self.gameboard.tv, self.pos.to_tile_tuple()) self._game_death() def _game_death(self): # Call appropriate gameboard cleanup here. pass def move(self): """Return a new position for the object""" # Default is not to move pass def attack(self): """Given the game state, attack a suitable target""" # Default is not to attack pass def set_pos(self, tile_pos): """Move an animal to the given tile_pos.""" new_pos = Position(*tile_pos) self._fix_face(new_pos) self.pos = new_pos def _fix_face(self, facing_pos): """Set the face correctly""" if facing_pos.left_of(self.pos): self._set_image_facing('left') elif facing_pos.right_of(self.pos): self._set_image_facing('right') def _set_image_facing(self, facing): self.facing = facing if self.facing == 'left': self.setimage(self.image_left) elif self.facing == 'right': self.setimage(self.image_right) def equip(self, item): if equipment.is_equipment(item): self.equipment.append(item) elif equipment.is_accoutrement(item): self.accoutrements.append(item) self.redraw() def unequip(self, item): if equipment.is_equipment(item): self.equipment = [e for e in self.equipment if e != item] elif equipment.is_accoutrement(item): self.accoutrements = [e for e in self.accoutrements if e != item] self.redraw() def unequip_by_name(self, item_name): # only remove first match matches = [ item for item in self.equipment + self.accoutrements if item.NAME == item_name ] if matches: self.unequip(matches[0]) def get_stealth(self): stealth = self.STEALTH for eq in self.equipment: stealth_bonus = getattr(eq, "STEALTH_BONUS", 0) stealth += stealth_bonus return stealth def redraw(self): layers = [(self._image_left.copy(), self._image_right.copy(), 0)] if hasattr(self, 'EQUIPMENT_IMAGE_ATTRIBUTE'): for item in self.accoutrements + self.equipment: images = item.images(self.EQUIPMENT_IMAGE_ATTRIBUTE) if images: layers.append(images) layers.sort(key=lambda l: l[2]) # these always go on the bottom so that other layers don't get overwritten self.image_left = self._image_left.copy() self.image_right = self._image_right.copy() for l in layers: self.image_left.blit(l[0], (0, 0)) self.image_right.blit(l[1], (0, 0)) self._set_image_facing(self.facing) def weapons(self): return [e for e in self.equipment if equipment.is_weapon(e)] def surveillance_equipment(self): return [ e for e in self.equipment if equipment.is_surveillance_equipment(e) ] def armour(self): return [e for e in self.equipment if equipment.is_armour(e)] def covers(self, tile_pos): return tile_pos[0] == self.pos.x and tile_pos[1] == self.pos.y def outside(self): return self.abode is None def damage(self): for a in self.armour(): if not a.survive_damage(): self.unequip(a) return True self.die() return False
def set_pos(self, tile_pos): """Move an animal to the given tile_pos.""" new_pos = Position(*tile_pos) self._fix_face(new_pos) self.pos = new_pos
import random from pgu.vid import Sprite import imagecache import tiles from misc import Position import sound import equipment import animations import serializer import constants NEIGHBOUR_4 = [ Position(-1, 0), Position(1, 0), Position(0, 1), Position(0, -1) ] NEIGHBOUR_8 = [ Position(-1, 0), Position(1, 0), Position(0, 1), Position(0, -1), Position(1, 1), Position(1, -1), Position(-1, 1), Position(-1, -1) ]