def load(self, creatures=None, foods=None): """Sets up various game world objects""" self.entities = [] self.scene_graph = SceneGraph() self.scene_graph.add(self.spinner) if creatures and foods: for creature in creatures: self._insert_creature(creature) for food in foods: self._insert_food(food) else: # Create creatures for x in range(self.num_of_creatures): self._insert_new_creature() # Create foods for x in range(self.num_of_food): self._insert_new_food() # Setup text boxes self.speed_textbox = TextBox("", (10, self.CAM_HEIGHT - 40)) self.creature_stats_textbox = MultilineTextBox([""], (10, 10)) self.population_stats_textbox = MultilineTextBox([""], (self.CAM_WIDTH-300, 10)) num_creatures_text = "{} Creatures, {} Food".format(self.num_of_creatures, self.num_of_food) self.num_creatures_textbox = TextBox(num_creatures_text, (10, self.CAM_HEIGHT - 70)) self.ui.add(self.speed_textbox) self.ui.add(self.creature_stats_textbox) self.ui.add(self.num_creatures_textbox) self.ui.add(self.population_stats_textbox)
class CreatureSim(PyGameBase): """Runs the creature sim game""" CAMERA_MOVE_SPEED = .5 GAME_SPEED_INCREMENT = .1 MAX_GAME_SPEED = 2.0 MIN_GAME_SPEED = .1 WORLD_WIDTH = 100000 WORLD_HEIGHT = 100000 SELECTED_COLOR = GREEN def __init__(self): super().__init__() self.infoObject = pygame.display.Info() self.CAM_HEIGHT = self.infoObject.current_h - 80 self.CAM_WIDTH = int(self.infoObject.current_w-100) self.num_of_creatures = 180 self.num_of_food = 100 self.game_bounds = (-5000, 5000) self.running = True # self.camera = Camera(self.CAM_WIDTH, self.CAM_HEIGHT, x=-(self.CAM_WIDTH / 2), y=-(self.CAM_HEIGHT / 2)) self.camera = Camera(self.CAM_WIDTH, self.CAM_HEIGHT, x=0, y=0, zoom=1.0) self.screen = pygame.display.set_mode((self.CAM_WIDTH, self.CAM_HEIGHT)) self.fullscreen = False # QuadTree for collision detection self.quadtree = QuadTree( bounds=( -self.WORLD_WIDTH/2, -self.WORLD_HEIGHT/2, self.WORLD_WIDTH, self.WORLD_HEIGHT), depth=9) self.ui = UserInterface(self.screen) self.dt = 0 # Will draw the quadtree overlay if true self.draw_quadtree = False self.paused = False # When the user clicks on the screen it's position will be stored here self.mouse_screen_position = None self.mouse_real_position = None # How fast everything moves self.game_speed = 1.0 self.selected_creature = None self.follow_creature = False self.breeder = Breeder() self.population_stats = StatsTracker() self.spinner = Background() def run(self): self.load() self.setup_keys() self.game_loop() def game_loop(self): while self.running: self.dt = self.clock.tick() self.screen.fill(BLACK) self.handle_events() self.handle_key_presses() self.update_creature_positions() self.scene_graph.update() self.center_camera() self.quadtree.update_objects(self.entities) self.handle_collisions() self.do_obj_events() self.render_frame() def render_frame(self): pygame.display.set_caption(str(self.clock.get_fps())) # Find all objects in nodes intersecting the screen objects_to_draw = self.quadtree.get_objects_at_bounds(self.camera.get_bounds()) for scene_object in objects_to_draw: scene_object.draw(self.screen, self.camera) if self.draw_quadtree: self.quadtree.draw_tree(self.screen, self.camera) self.draw_ui() pygame.display.flip() def center_camera(self): if self.selected_creature and self.follow_creature: self.camera.position[0] = self.selected_creature.position[0] self.camera.position[1] = self.selected_creature.position[1] def do_obj_events(self): if not self.paused: self.check_healths() def remove_creature(self, creature): if creature.selected: self.unfollow_creature() self.quadtree.remove(creature) self.entities.remove(creature) self.entities.remove(creature.vision_cone) self.scene_graph.remove(creature) def check_healths(self): for creature in self.get_creatures(): if creature.health <= 0: self.remove_creature(creature) self._breed_creature() for food in self.get_foods(): if food.eaten == True: self.quadtree.remove(food) self.entities.remove(food) self._insert_new_food() def _insert_new_creature(self): new_creature = Creature( x=randrange(*self.game_bounds), y=randrange(*self.game_bounds), color=WHITE ) new_creature.vision_cone = VisionCone(parent=new_creature, x=265, color=RED) self.entities.append(new_creature.vision_cone) self._insert_creature(new_creature) return new_creature def _insert_creature(self, creature): self.entities.append(creature) creature.calc_absolute_position() self.scene_graph.add(creature) self.scene_graph.add_to_parent(creature.vision_cone, creature, transformer=OldStyleTransformer) self.quadtree.insert(creature) def _insert_new_food(self): new_food = Food( x=randrange(*self.game_bounds), y=randrange(*self.game_bounds), color=DARKGREEN ) self._insert_food(new_food) return new_food def _insert_food(self, food): self.entities.append(food) food.calc_absolute_position() self.quadtree.insert(food) def _breed_creature(self): new_weights = self.breeder.breed(list(self.get_creatures())) new_creature = Creature( x=randrange(*self.game_bounds), y=randrange(*self.game_bounds), nn_weights=new_weights ) new_creature.vision_cone = VisionCone(parent=new_creature, x=265, color=RED) self.entities.append(new_creature.vision_cone) self._insert_creature(new_creature) return new_creature def handle_collisions(self): # Handle collisions for each creature's vision cone for creature in self.get_creatures(): # Get rough idea of what things could be colliding first_pass = self.quadtree.get_objects_at_bounds(creature.get_bounds()) if first_pass: for entity in first_pass: creature.vision_cone.check_collision(entity) creature.check_collision(entity) for entity in self.entities: entity.finish_collisions() def handle_key_presses(self): # Camera Zoom if self.key_presses["zoom-in"]: self.camera.zoom_in(self.dt) if self.key_presses["zoom-out"]: self.camera.zoom_out(self.dt) # Camera movement if not self.follow_creature: if self.key_presses["cam-up"]: self.camera.move(y_change=-self.dt * self.CAMERA_MOVE_SPEED) if self.key_presses["cam-down"]: self.camera.move(y_change=self.dt * self.CAMERA_MOVE_SPEED) if self.key_presses["cam-left"]: self.camera.move(x_change=-self.dt * self.CAMERA_MOVE_SPEED) if self.key_presses["cam-right"]: self.camera.move(x_change=self.dt * self.CAMERA_MOVE_SPEED) def handle_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() self.register_key_presses(event) def register_key_presses(self, event): # Key Down ######################## if event.type == KEYDOWN: if event.key == K_ESCAPE: raise SystemExit() try: self.key_presses[self.key_map[event.key]] = True except KeyError: pass if event.key == K_t: self.draw_quadtree = not self.draw_quadtree if event.key == K_b: self.select_best() if event.key == K_F5: self.save_state() if event.key == K_F9: self.load_state() if event.key == K_p: self.paused = not self.paused if event.key == K_F11: self.toggle_fullscreen() if event.key == K_f: self.toggle_follow_creature() if event.key == K_RIGHTBRACKET: self.speedup_game() if event.key == K_LEFTBRACKET: self.slowdown_game() # Key Up ######################## if event.type == KEYUP: try: self.key_presses[self.key_map[event.key]] = False except KeyError: pass # Mouse Down ######################## if event.type == pygame.MOUSEBUTTONDOWN: self.handle_click() def toggle_fullscreen(self): self.fullscreen = not self.fullscreen if self.fullscreen: pygame.display.set_mode((self.CAM_WIDTH, self.CAM_HEIGHT), pygame.FULLSCREEN) else: pygame.display.set_mode((self.CAM_WIDTH, self.CAM_HEIGHT)) def save_state(self): def export_items(items): with open('weight_data.pickle', 'wb+') as f: pickle.dump(items, f) # dump the creatures and food stuffs # let's get all the creatures first x = list(self.get_creatures()) y = list(self.get_foods()) export_items([x, y]) self.ui.toast("Saved Simulation") def load_state(self): def retrieve_items(): with open('weight_data.pickle', 'rb+') as f: return pickle.load(f) try: creatures, foods = retrieve_items() except: return self.reload(creatures, foods) self.ui.toast("Loaded Simulation") def toggle_follow_creature(self): if self.selected_creature: self.follow_creature = not self.follow_creature def unfollow_creature(self): self.follow_creature = False def select_best(self): self.unselect_creature() self.unfollow_creature() best = self._find_best() self.select_creature(best) self.toggle_follow_creature() def _find_best(self): return max(self.get_creatures(), key=lambda c: c.total_food_eaten) def select_creature(self, creature): self.selected_creature = creature creature.selected = True def unselect_creature(self): if self.selected_creature: self.selected_creature.selected = False self.selected_creature = None self.unfollow_creature() def handle_click(self): self.mouse_screen_position = pygame.mouse.get_pos() self.mouse_real_position = self.camera.real_position(self.mouse_screen_position) hit = self.quadtree.ray_cast(self.mouse_real_position) if hit: # If our hit is a Creature if issubclass(type(hit), Creature): self.unselect_creature() self.select_creature(hit) def _reload_init(self): self.quadtree = QuadTree( bounds=( -self.WORLD_WIDTH/2, -self.WORLD_HEIGHT/2, self.WORLD_WIDTH, self.WORLD_HEIGHT), depth=9) self.ui = UserInterface(self.screen) self.dt = 0 # When the user clicks on the screen it's position will be stored here self.mouse_screen_position = None self.mouse_real_position = None # How fast everything moves self.game_speed = 1.0 self.selected_creature = None self.follow_creature = False self.population_stats = StatsTracker() def reload(self, creatures, foods): self._reload_init() self.load(creatures, foods) def load(self, creatures=None, foods=None): """Sets up various game world objects""" self.entities = [] self.scene_graph = SceneGraph() self.scene_graph.add(self.spinner) if creatures and foods: for creature in creatures: self._insert_creature(creature) for food in foods: self._insert_food(food) else: # Create creatures for x in range(self.num_of_creatures): self._insert_new_creature() # Create foods for x in range(self.num_of_food): self._insert_new_food() # Setup text boxes self.speed_textbox = TextBox("", (10, self.CAM_HEIGHT - 40)) self.creature_stats_textbox = MultilineTextBox([""], (10, 10)) self.population_stats_textbox = MultilineTextBox([""], (self.CAM_WIDTH-300, 10)) num_creatures_text = "{} Creatures, {} Food".format(self.num_of_creatures, self.num_of_food) self.num_creatures_textbox = TextBox(num_creatures_text, (10, self.CAM_HEIGHT - 70)) self.ui.add(self.speed_textbox) self.ui.add(self.creature_stats_textbox) self.ui.add(self.num_creatures_textbox) self.ui.add(self.population_stats_textbox) def update_creature_positions(self): if not self.paused: for creature in self.get_creatures(): network = creature.nn network.compute_network() creature.rotate((self.dt * creature.rotation_speed) * self.game_speed) creature.move_forward((self.dt * creature.speed) * self.game_speed) creature.do_everyframe_action(self.dt, self.game_speed) creature.update_position() def speedup_game(self): self.game_speed = min(self.MAX_GAME_SPEED, self.game_speed + self.GAME_SPEED_INCREMENT) def slowdown_game(self): self.game_speed = max(self.MIN_GAME_SPEED, self.game_speed - self.GAME_SPEED_INCREMENT) def setup_keys(self): """Sets up key presses""" key_map = { K_w: "cam-up", K_s: "cam-down", K_a: "cam-left", K_d: "cam-right", K_e: "zoom-out", K_q: "zoom-in", } self.register_keys(key_map) def draw_ui(self): ui = self.ui if self.paused: speed_text = "Speed: Paused" else: speed_text = "Speed: {:.2}x".format(self.game_speed) self.speed_textbox.set(speed_text) if self.selected_creature: self.creature_stats_textbox.set(self.selected_creature.get_stats()) else: self.creature_stats_textbox.clear() stats = self.population_stats.update(list(self.get_creatures())) self.population_stats_textbox.set(stats) ui.draw() def get_creatures(self): return (entity for entity in self.entities if issubclass(type(entity), Creature)) def get_foods(self): return (entity for entity in self.entities if issubclass(type(entity), Food))