def trial(self, cutoff): """Play automatically, without display. Return the number of rounds played (before GameOver raised). If there is no GameOver before cutoff is reached, stop anyway.""" log("trial with cutoff %s" % cutoff) self.finalise_rules() log("finalised") self.reset() log("reset") self.play_state = config.STATE_TRIAL self.player_score = 0 PLAYING = config.PLAYING DYING = config.DYING log("trial with types %s" % self.types) self.game_over = False for r in range(cutoff): for s in self.sprites: if s.state == PLAYING: s.decide() for s in self.sprites: if s.state == PLAYING: s.update() elif s.state == DYING: s.die2() self.track_play(r) if self.game_over: break return (r, self.player_score)
def apply_rules(self, rules): self.rules = rules self.rule_suitability = self.suitabilities[rules.name] self.steps = self.rules.steps self.original_steps = self.steps self.directions = self.rules.directions self.type_name = rules.name #XXX more, surely? self.inputs_per_type, self.zones = self._mind_lut[self.rules.mind] self.block_bits = 0xff self.die_bits = 0 log("applied rules to %s" % self)
def finalise_rules(self): self.food_sprites = [] self.enemy_sprites = [] self.ally_sprites = [] for t in self.teams: log('about to finalise %s' %t) t.finalise() if t.rules.sprite_group == 'monster': self.enemy_sprites.extend(t.sprites) if t.rules.sprite_group == 'food': self.food_sprites.extend(t.sprites) if t.rules.sprite_group == 'ally': self.ally_sprites.extend(t.sprites) s = self.player_sprite self.player_extremes = (s.y, s.x, s.y, s.x)
def _grow_rules(self): """get some rules, and tune them until they work""" stop = time.time() + config.RULE_GROWING_TIMEOUT results = [] #start off with an arbitrary set #XXXXXX # add_rules can be moved here (only used here) #can be made invincible to suitability imbalance? rules.add_rules(self.player_team, self.other_teams) log("added rules") for i in range(config.RULE_GROWING_ATTEMPTS): log("attempt %s" % i) self.types = ['wall'] + [x.rules.name for x in self.teams] r, score = self.trial(config.CUTOFF_LENGTH + 2) score = score // 8 log("trial with %s lasted %s" % (self.types, r)) # diff from ideal matters, but overshooting is penalised if r > config.CUTOFF_LENGTH: error = config.OVERSHOOT_PENALTY else: error = abs(config.IDEAL_LENGTH - r) results.append((max(error - score, 0), self.types)) if r > config.TARGET_LENGTH[1]: rules.mutate_rules(self.other_teams, -1) elif r < config.TARGET_LENGTH[0]: rules.mutate_rules(self.other_teams, 1) else: self.mutate_rules_meta() if time.time() > stop: log("stopping due to timeout") break log(*reversed(sorted(results))) return min(results)
def grow(): try: log("hello") best = self._grow_rules() log("got rules") fn = os.path.join(config.DATA_ROOT, "rules-%s.pickle" % os.getpid()) utils.pickle(best, fn) log("pickled rules") except Exception, e: traceback.print_exc() os._exit(os.EX_SOFTWARE)
def add_rules(player, others): """each passed in team gets appended with a set of rules""" #cycle until no conflicts found (conflicts are quite rare) types = random.sample(NON_PLAYER_TYPES, len(others)) while find_conflicts(types): types = random.sample(NON_PLAYER_TYPES, len(others)) #initial optimisation is simple and greedy unassigned = others[:] for x in types: s, team = max((t.suitabilities[x.name], t) for t in unassigned) log("picked %s for %s (suitability %s) out of %s" % (team, x, s, unassigned)) team.apply_rules(x) unassigned.remove(team) log("now the player") player.apply_rules(random.choice(PLAYER_TYPES)) log("returning")
class Game: """Represents a game. The game forks into two processes, and evolves in one (the child). After a while, the evolved game saves key details to disk, and the game in the parent process picks them up. Saved attributes are: * sprite minds (This list should be growing, so is likely out of date). """ game_over = True play_state = False nothing_happening = 0 def __init__(self, background, blobs, window=None, entropy=None): if entropy: #seed the neural networks' RNG. self.random_holder = weights.seed_nets(entropy) self.background = background self.blobs = blobs self.map = Gamemap(config.WORKING_SIZE[0], config.WORKING_SIZE[1], config.PIXEL_SIZE) self.entropy = entropy if entropy: self.identity = "game-%x-%x" % tuple(entropy[:2]) else: self.identity = "game-%x" % id(self) self.score = 0 self.player_score = 0 self.set_window(window) self.sort_sprites(blobs) self.effects = {} self.invisible_player = False self.high_score = 0 self.score_redraw = False self.food_repopulation_constant = config.FOOD_REPOPULATION_CONSTANT self.enemy_repopulation_constant = config.ENEMY_REPOPULATION_CONSTANT self.ally_repopulation_constant = config.ALLY_REPOPULATION_CONSTANT self.resuscitate_in_packs = False def set_window(self, window): """separate from init for (legacy?) evolution reasons -- game can set window after it is formed""" self.window = window if window is not None: window.display(self.background) def sort_sprites(self, blobs): """Organise sprites into teams. There will be at least one team of one, which becomes the player. """ sorter = assort.TeamFinder(blobs, entropy=self.entropy) arrangement = sorter.find_teams() sorter.print_stats() self.teams = [actor.Team(v, i, self) for i, v in zip(config.TEAM_BITS, arrangement.vectors)] #print self.teams self.player_team = self.teams[0] self.other_teams = self.teams[1:] self.n_teams = len(self.teams) #needed for bootstrapping actors self.sprites = [] #XXX possibly indeterminacy sneaking in, if sprites have ever been a set for t in self.teams: self.sprites.extend(t.sprites) self.nonplayer_sprites = self.sprites[1:] self.player_sprite = self.sprites[0] #so, now, there are teams of sprites, but no minds or rules. def mutate_rules_meta(self): #alter constant aspects of the game. for x in ('food_repopulation_constant', 'enemy_repopulation_constant', 'ally_repopulation_constant'): c = getattr(self, x) a, b = random.sample(range(30,40), 2) c = c * a // b setattr(self, x, c) if random.random() > 0.8: self.resuscitate_in_packs = not self.resuscitate_in_packs def _grow_rules(self): """get some rules, and tune them until they work""" stop = time.time() + config.RULE_GROWING_TIMEOUT results = [] #start off with an arbitrary set #XXXXXX # add_rules can be moved here (only used here) #can be made invincible to suitability imbalance? rules.add_rules(self.player_team, self.other_teams) log("added rules") for i in range(config.RULE_GROWING_ATTEMPTS): log("attempt %s" % i) self.types = ['wall'] + [x.rules.name for x in self.teams] r, score = self.trial(config.CUTOFF_LENGTH + 2) score = score // 8 log("trial with %s lasted %s" % (self.types, r)) # diff from ideal matters, but overshooting is penalised if r > config.CUTOFF_LENGTH: error = config.OVERSHOOT_PENALTY else: error = abs(config.IDEAL_LENGTH - r) results.append((max(error - score, 0), self.types)) if r > config.TARGET_LENGTH[1]: rules.mutate_rules(self.other_teams, -1) elif r < config.TARGET_LENGTH[0]: rules.mutate_rules(self.other_teams, 1) else: self.mutate_rules_meta() if time.time() > stop: log("stopping due to timeout") break log(*reversed(sorted(results))) return min(results) def grow_rules_in_fork(self): """make rules, in background processes""" #utils.make_dir(config.DATA_ROOT) def grow(): try: log("hello") best = self._grow_rules() log("got rules") fn = os.path.join(config.DATA_ROOT, "rules-%s.pickle" % os.getpid()) utils.pickle(best, fn) log("pickled rules") except Exception, e: traceback.print_exc() os._exit(os.EX_SOFTWARE) os._exit(0) def display(): self.window.twiddle(0.5, self) finishers = utils.process_in_fork(grow, display, config.PROCESSES, config.PROCESS_TIMEOUT) results = [] for pid in finishers: try: fn = os.path.join(config.DATA_ROOT, "rules-%s.pickle" % pid) results.append(utils.unpickle(fn)) if config.TIDY_FILES: os.remove(fn) except IOError, e: log("missing out on pid %s", e)
def play(self, hz=config.PLAY_HERTZ, replay=False): self.reset() w = self.window w.blank_screen(self) self.dead_rects = [] dying_rects = [] if replay: # replay is flag and duration combined self.play_state = config.STATE_REPLAY self.player_sprite.auto_mode() replay *= hz for s in w.sounds.values(): s.set_volume(0.3) else: self.play_state = config.STATE_PLAYING self.player_sprite.manual_mode() for s in w.sounds.values(): s.set_volume(1) start = time.time() self.player_score = 0 self.game_over = False r = 0 frame = -1 DYING = config.DYING PLAYING = config.PLAYING try: while True: w.clock.tick(hz) if not frame: r += 1 w.interpret_events(self.do_extra_keys) for s in self.sprites: if s.state == PLAYING: s.decide() if config.DRAW_FINDS: self.sprites[3].draw_finds() #XXX reduce sounds a bit more. sounds = set() for s in self.sprites: if s.state == PLAYING: s.update() sounds.add(s.chatter()) elif s.state == DYING: s.die2() w.speak(sounds) self.track_play(r) #XXX with replay, also want to stop for button press if replay: if r > replay or w.fire_button: raise GameStop("replay is over") for s in self.sprites: if s.state == PLAYING: s.slither(frame) elif s.state == DYING: s.dying_animation() dying_rects.append(s.rect) w.redraw(self) self.dead_rects = dying_rects dying_rects = [] frame = (frame + 1) % config.FRAMES_PER_UPDATE if self.game_over and config.GAME_ENDS: raise GameOver except Exception, e: self.playing = False t = time.time() - start log("%s ticks in %s seconds: ~ %s per sec" % (r, t, (r + 0.5)/t)) self.save_map(r) if not isinstance(e, (GameEscape, GameOver, GameStop)): raise if isinstance(e, GameOver): w.write("G A M E O V E R") if self.player_score > self.high_score: self.high_score = self.player_score if not replay: self.pause(3)
def end(self): self.game_over = True log("player is down!")
os.remove(fn) except IOError, e: log("missing out on pid %s", e) if not results: # the game is crashing raise ParseError("Sorry. The artist was lazy and stupid, and the machine can't handle your picture.") error, self.types = min(results) if error > config.REALLY_BAD_GAME_ERROR: # the game is hardly lasting very long at all. # send out an apology, which has no actual effect self.window.text_cycle(config.GROVEL_CYCLE) rules.apply_rules(self.teams, self.types[1:]) log("%s teams:" % len(self.teams)) for t in self.teams: log(t.size, t, t.rules) def reset(self): """Start the game again""" self.map.clear() for x in self.sprites: x.reset() def trial(self, cutoff): """Play automatically, without display. Return the number of rounds played (before GameOver raised). If there is no GameOver before cutoff is reached, stop anyway."""