''' Created on Aug 26, 2015 @author: Brett Paufler Copyright Brett Paufler THis is OK for cold storage: When resume work on refining scoring first Main Control for MutaGenetics Football To improve this class, add new seed, score, or selector functions probably should start with score as the most leverage there only have one scoring criteria at this point (sum distance) ''' import football_roster import football_scrimmage from copy import deepcopy class Tournament(): '''The base MutaGenetic control structure: controls for a Genetic Tournament: seed = initial tournament values, and mid-tournament additions score = how rosters are rated selector = main Genetic Algorithm the control function for the iterative refinement process Sample code: t = Tournament(score='score_sum_nearest', seed='seed_serial_twiddle', selector='selector_basic_twiddle', max_turns=25, keepers=5) t.run() t.gif_best(images=False) ''' def __init__(self, seed, score, selector, max_turns=25, keepers=5): '''seed, score, & tourney must be strings that correspond to names of Tournament class functions. All seed functions start seed_etc score functions start score_this_way tourney functions start tourney_foo ''' self.max_turns = max_turns self.keepers = keepers self.s = football_scrimmage.Scrimmage() self.seed_func = getattr(self, seed) self.score_func = getattr(self, score) self.selector_func = getattr(self, selector) self.scored_rosters = [] self.triage() #populates scored_rosters def triage(self, seed=True): '''Sorts scored_rosters, keeping only the best, where len(self.scored_rosters) == self.keepers. if seed: self.scored_rosters is additionally (or pre) populated.''' if seed: self.seed_func() self.scored_rosters = sorted(self.scored_rosters, reverse=True) self.scored_rosters = self.scored_rosters[:self.keepers] def defensive_selector(self, offensive_roster): #TODO - Implement in a meaningful way #Given offense, select defense, static for now return football_roster.defense(offensive_roster, play='def_play_all_track_closest') '''SCORE FUNCTIONS: All score_functions: apply a score to passed offensive_roster append (score, offensive_roster) to self.scored_rosters. The differen score_functions differ only in their scoring modality.''' #TODO - THIS IS NEXT #score_helpers #score_control #scoring is where the most improvement will come def score_sum_nearest(self, offensive_roster): '''SCORE FUNCTION: additive distance of each player on offense to nearest player on defense.''' defensive_roster = self.defensive_selector(offensive_roster) s = football_scrimmage.Scrimmage(offensive_roster, defensive_roster) s.run_scrimmage(num_turns=self.max_turns, name='muta_test', images=False, gif=False) points = 0 for player in [p for p in s.players if p.number > 0]: points += s.closest_player(player, mates=False, return_distance=True, closest=True)[0] self.scored_rosters.append((points, offensive_roster)) '''SEED FUNCTIONS: ALL seed_functions: Extend scored_roster list with new primitive (unrefined) examples. Either as starting value, or fresh meat. Remember: scored_rosters (score, football_roster(11,6,4)) ''' def seed_serial_twiddle(self): '''Starting with a simplistic center_line, all_run football_roster, randomly applies football_roster.twiddle to improve.''' num_twidles=100 play='off_play_all_run' start='off_start_center_line' offensive_roster = football_roster.offense(start=start, play=play) rL = [football_roster.twiddle(offensive_roster, num=num_twidles, t_play=True, t_x=True, t_y=True, t_i=True, t_start=True) for _ in range(self.keepers)] for r in rL: self.score_func(r) #NOTE: score_func adds seed values to self.scored_rosters '''SELECTOR FUNCTIONS: ALL selector_functions: Start & End with len(scored_rosters) == keepers. Remember: scored_rosters (score, football_roster(11,6,4)) Selecting the Best From some mutation method. The heart and soul of the genetic process. Adds scored_rosters by some method Keeps the best scored_rosters Rinse and repeated by run.''' def selector_basic_twiddle(self): '''Given a seed list of scored_rosters (the seed), expands list by twiddling copies, scores mutated rosters, keeps the best (len same as initial), and repeats num_loops times.''' print 'Tourney', len(self.scored_rosters) print self.scored_rosters print self.scored_rosters #prev_winners = deepcopy(self.scored_rosters) for _, r in self.scored_rosters[:self.keepers]: #print s, r twid_roster = football_roster.twiddle(deepcopy(r), num=10, t_play=True, t_x=True, t_y=True, t_i=True, t_start=True) self.score_func(twid_roster) self.scored_rosters = sorted(self.scored_rosters, reverse=True) self.scored_rosters = self.scored_rosters[:self.keepers] print len(self.scored_rosters) print self.scored_rosters[0] '''TOURNAMENT CONTROL: functions needed for running the tournament''' def run(self): self.seed_func() for n in range(self.max_turns): print n self.selector_func() def best(self, sN='muta_test', images=True, gif=True, save_roster=True): '''Reports as to the best scored_roster. If images: saves intermediate images If gif: saves composite gif If football_roster: saves best football_roster as text saves in ./images for now.''' self.triage(seed=True) score, best_roster = self.scored_rosters[0] sN = '{:>05}_'.format(int(score)) + sN if save_roster: football_roster.save(best_roster, './images/%s.txt' % sN) if images or gif: print 'image/gif run: %s' % sN defensive_roster = self.defensive_selector(best_roster) s = football_scrimmage.Scrimmage(best_roster, defensive_roster) s.run_scrimmage(num_turns=self.max_turns, name=sN, images=images, gif=gif) if __name__ == '__main__': t = Tournament(score='score_sum_nearest', seed='seed_serial_twiddle', selector='selector_basic_twiddle', max_turns=25, keepers=25) print t t.run() t.best(images=False) print '\n\tFINISHED: MutaGenetics Football!!!'