def _breed_new_ai(self, cursor, old_player_ids): """Breed a new AI, from two weighted-random experienced parents.""" query = """SELECT p.player_id, p.mu FROM player p WHERE p.class = 'CleverBot'""" cursor.execute(query) rows = cursor.fetchall() possible_parents = [] for row in rows: mu = row["mu"] player_id = row["player_id"] if player_id in old_player_ids: possible_parents.append((mu, player_id)) tup1 = Dice.weighted_random_choice(possible_parents) possible_parents.remove(tup1) tup2 = Dice.weighted_random_choice(possible_parents) player_id1 = tup1[1] info1 = self.get_player_info(player_id1) bp1 = BotParams.BotParams.fromstring(info1) player_id2 = tup2[1] info2 = self.get_player_info(player_id2) bp2 = BotParams.BotParams.fromstring(info2) bp3 = bp1.cross(bp2).mutate_random_field() bot = CleverBot.CleverBot("child", config.DEFAULT_AI_TIME_LIMIT, bp3) info = bot.player_info logging.info("player_info %s", info) query = """INSERT INTO player (class, info, mu, sigma) VALUES (?, ?, ?, ?)""" cursor.execute(query, ("CleverBot", info, DEFAULT_MU, DEFAULT_SIGMA)) # And fetch the player_id. query = """SELECT player_id FROM player where class = ? AND info = ?""" cursor.execute(query, ("CleverBot", info)) row = cursor.fetchone() player_id = row["player_id"] # And update the name. name = "ai%d" % player_id query = """UPDATE player SET name = ? WHERE player_id = ?""" cursor.execute(query, (name, player_id)) logging.info("father %s %s", player_id1, bp1) logging.info("mother %s %s", player_id2, bp2) logging.info("baby %s %s %s", player_id, name, bp3) return player_id
def _breed_new_ai(self, cursor, old_player_ids): """Breed a new AI, from two weighted-random experienced parents.""" query = """SELECT p.player_id, p.mu FROM player p WHERE p.class = 'CleverBot'""" cursor.execute(query) rows = cursor.fetchall() possible_parents = [] for row in rows: mu = row["mu"] player_id = row["player_id"] if player_id in old_player_ids: possible_parents.append((mu, player_id)) tup1 = Dice.weighted_random_choice(possible_parents) possible_parents.remove(tup1) tup2 = Dice.weighted_random_choice(possible_parents) player_id1 = tup1[1] info1 = self.get_player_info(player_id1) bp1 = BotParams.BotParams.fromstring(info1) player_id2 = tup2[1] info2 = self.get_player_info(player_id2) bp2 = BotParams.BotParams.fromstring(info2) bp3 = bp1.cross(bp2).mutate_random_field() bot = CleverBot.CleverBot("child", config.DEFAULT_AI_TIME_LIMIT, bp3) info = bot.player_info logging.info("player_info %s", info) query = """INSERT INTO player (class, info, mu, sigma) VALUES (?, ?, ?, ?)""" cursor.execute(query, ("CleverBot", info, DEFAULT_MU, DEFAULT_SIGMA)) # And fetch the player_id. query = """SELECT player_id FROM player where class = ? AND info = ?""" cursor.execute(query, ("CleverBot", info)) row = cursor.fetchone() player_id = row["player_id"] # And update the name. name = "ai%d" % player_id query = """UPDATE player SET name = ? WHERE player_id = ?""" cursor.execute(query, (name, player_id)) logging.info("father %s %s", player_id1, bp1) logging.info("mother %s %s", player_id2, bp2) logging.info("baby %s %s %s", player_id, name, bp3) return player_id
def test_weighted_random_choice(self): lst = [ (0.4, 1), (0.3, 2), (0.2, 3), (0.1, 4), ] # Can use a Counter when we require Python 2.7 counter = defaultdict(int) for trial in xrange(1000): tup = Dice.weighted_random_choice(lst) print tup counter[tup[1]] += 1 print counter assert sum(counter.itervalues()) == 1000 assert counter[1] > counter[2] > counter[3] > counter[4]
def test_weighted_random_choice(self): lst = [ (0.4, 1), (0.3, 2), (0.2, 3), (0.1, 4), ] # Can use a Counter when we require Python 2.7 counter = defaultdict(int) for trial in xrange(1000): tup = Dice.weighted_random_choice(lst) print tup counter[tup[1]] += 1 print counter assert sum(counter.itervalues()) == 1000 assert counter[1] > counter[2] > counter[3] > counter[4]
def get_weighted_random_player_id(self, excludes=(), highest_mu=False): """Return a player_id. Exclude any player_ids in excludes. If there are fewer than GENERATION_SIZE AI player_id in the database, add a new AI (by mutating the default) and return its player_id. If highest_mu is not None, then return the eligible player_id with the highest mu. If there are fewer than GENERATION_SIZE AI player_ids in the database with sigma <= BREEDING_SIGMA_THRESHOLD, breed a new AI (by crossing two parent AIs with sigma <= BREEDING_SIGMA_THRESHOLD, chosen randomly weighted by mu), and return its player_id. Otherwise, choose an existing player_id randomly, weighted by low sigma, and return it. """ with self.connection: cursor = self.connection.cursor() total_ai_count = 0 query = """SELECT player_id, mu, sigma FROM player p WHERE p.class='CleverBot'""" cursor.execute(query) rows = cursor.fetchall() young_player_ids = set() old_player_ids = set() for row in rows: player_id = row["player_id"] sigma = row["sigma"] if sigma <= BREEDING_SIGMA_THRESHOLD: old_player_ids.add(player_id) else: young_player_ids.add(player_id) young_ai_count = len(young_player_ids) old_ai_count = len(old_player_ids) total_ai_count = young_ai_count + old_ai_count if highest_mu: # Pick the eligible AI with the highest mu and return its # player_id. query = """SELECT player_id, mu FROM player WHERE class = 'CleverBot' ORDER BY mu DESC""" cursor.execute(query) rows = cursor.fetchall() for row in rows: player_id = row["player_id"] if player_id not in excludes: logging.info("picked high-mu AI %s", player_id) return player_id if young_ai_count < GENERATION_SIZE and old_ai_count >= 2: # Not enough young AIs, so breed one. return self._breed_new_ai(cursor, old_player_ids) else: candidates = [] # Pick an existing young AI randomly, weighted by low sigma, # and return its player_id. query = """SELECT player_id, sigma FROM player WHERE class = 'CleverBot'""" cursor.execute(query) rows = cursor.fetchall() for row in rows: player_id = row["player_id"] sigma = row["sigma"] if player_id in young_player_ids and player_id not in excludes: candidates.append((1.0 / sigma, player_id)) if candidates: tup = Dice.weighted_random_choice(candidates) player_id = tup[1] logging.info("picked random AI %s", player_id) return player_id else: # No eligible AIs available, so either breed or spawn # a new one. if total_ai_count < GENERATION_SIZE: return self._spawn_new_ai(cursor) elif old_ai_count >= 2: return self._breed_new_ai(cursor, old_player_ids) else: return self._spawn_new_ai(cursor)
def get_weighted_random_player_id(self, excludes=(), highest_mu=False): """Return a player_id. Exclude any player_ids in excludes. If there are fewer than GENERATION_SIZE AI player_id in the database, add a new AI (by mutating the default) and return its player_id. If highest_mu is not None, then return the eligible player_id with the highest mu. If there are fewer than GENERATION_SIZE AI player_ids in the database with sigma <= BREEDING_SIGMA_THRESHOLD, breed a new AI (by crossing two parent AIs with sigma <= BREEDING_SIGMA_THRESHOLD, chosen randomly weighted by mu), and return its player_id. Otherwise, choose an existing player_id randomly, weighted by low sigma, and return it. """ with self.connection: cursor = self.connection.cursor() total_ai_count = 0 query = """SELECT player_id, mu, sigma FROM player p WHERE p.class='CleverBot'""" cursor.execute(query) rows = cursor.fetchall() young_player_ids = set() old_player_ids = set() for row in rows: player_id = row["player_id"] sigma = row["sigma"] if sigma <= BREEDING_SIGMA_THRESHOLD: old_player_ids.add(player_id) else: young_player_ids.add(player_id) young_ai_count = len(young_player_ids) old_ai_count = len(old_player_ids) total_ai_count = young_ai_count + old_ai_count if highest_mu: # Pick the eligible AI with the highest mu and return its # player_id. query = """SELECT player_id, mu FROM player WHERE class = 'CleverBot' ORDER BY mu DESC""" cursor.execute(query) rows = cursor.fetchall() for row in rows: player_id = row["player_id"] if player_id not in excludes: logging.info("picked high-mu AI %s", player_id) return player_id if young_ai_count < GENERATION_SIZE and old_ai_count >= 2: # Not enough young AIs, so breed one. return self._breed_new_ai(cursor, old_player_ids) else: candidates = [] # Pick an existing young AI randomly, weighted by low sigma, # and return its player_id. query = """SELECT player_id, sigma FROM player WHERE class = 'CleverBot'""" cursor.execute(query) rows = cursor.fetchall() for row in rows: player_id = row["player_id"] sigma = row["sigma"] if (player_id in young_player_ids and player_id not in excludes): candidates.append((1.0 / sigma, player_id)) if candidates: tup = Dice.weighted_random_choice(candidates) player_id = tup[1] logging.info("picked random AI %s", player_id) return player_id else: # No eligible AIs available, so either breed or spawn # a new one. if total_ai_count < GENERATION_SIZE: return self._spawn_new_ai(cursor) elif old_ai_count >= 2: return self._breed_new_ai(cursor, old_player_ids) else: return self._spawn_new_ai(cursor)