''' Created on Apr 25, 2018 @author: Brett Paufler Copyright Brett Paufler To Parse BUG Logfiles We'll see how it goes Gets Graphs: 1) Research, Gold, Espionage, Culture 2) Total Population TODO: take draft into account Takes a CIV 4 The Bat Mod Log file Outputs Graphs ''' from os import listdir from os.path import join as path_join import re from datetime import datetime from collections import defaultdict import matplotlib.pyplot as plt dir_in = './input/' dir_out = './output/' #test_file = '14-montezume_bat_autolog.txt' log_list = [path_join(dir_in, log_file) for log_file in listdir(dir_in)] class Turn(object): '''Holds and extracts each turn object.''' def __init__(self, text): self.text = text self.lines = self.text.split('\n') self.num = int(re.match( '\d*', self.text).group(0)) #Clearly Crap, Munged to Death, but Works self.date_str = self.lines[0][-21:-1] self.date = datetime.strptime( self.date_str, '%d-%b-%Y %H:%M:%S') self.research_percent = 0 self.research_amount = 0 self.gold_percent = 0 self.gold_amount = 0 self.culture_percent = 0 self.culture_amount = 0 self.espionage_percent = 0 self.espionage_amount = 0 research_line = [line for line in self.lines if '% Research: ' in line] gold_line = [line for line in self.lines if '% Gold: ' in line] culture_line = [line for line in self.lines if '% Culture: ' in line] espionage_line = [line for line in self.lines if '% Espionage: ' in line] if research_line: parts = research_line[0].split() self.research_percent = int(parts[0][:-1]) self.research_amount = int(parts[2]) if gold_line: parts = gold_line[0].split() self.gold_percent = int(parts[0][:-1]) self.gold_amount = int(parts[2]) if culture_line: parts = culture_line[0].split() self.culture_percent = int(parts[0][:-1]) self.culture_amount = int(parts[2]) if espionage_line: parts = espionage_line[0].split() self.espionage_percent = int(parts[0][:-1]) self.espionage_amount = int(parts[2]) #Tech Not Used Yet self.tech_finished = '' tech_finished_line = [line for line in self.lines if 'Tech research finished: ' in line] if tech_finished_line: tech = tech_finished_line[0][24:] self.tech_finished = str(tech) #Growth is a Dictionary of Cities #CityName: Population growth_pairs = [line.split(' grows to size ') for line in self.lines if ' grows to size ' in line] self.growth = dict([(city, int(size)) for city, size in growth_pairs ]) #print growth #List of Cities Where Whip Was Used self.whips = [line[24:] for line in self.lines if 'The whip was applie' in line] #print whips #Cities captured This turn self.captured = [line.split(' ')[1] for line in self.lines if line.startswith('Captured')] #print self.captured #Wonders are very Specific #Will need list of Wonders #Tenochtitlan finishes: The Great Wall #Cities founded is counfounded with religions #Maybe will not do, not this go round def __repr__(self): text = 'Turn %d\n' % self.num text += '\tR %3d%%: %d\n' % ( self.research_percent, self.research_amount) text += '\tG %3d%%: %d\n' % ( self.gold_percent, self.gold_amount) text += '\tE %3d%%: %d\n' % ( self.espionage_percent, self.espionage_amount) text += '\tC %3d%%: %d\n' % ( self.culture_percent, self.culture_amount) return text class Game(object): '''Extracts Game info from each logfile, utilizing turn object above ''' def __init__(self, file_path): #Toggled by self.cleanse() if turns removed #Dirty: True means turns were replayed self.dirty = False with open(file_path, 'r') as f: self.name = file_path[8:-4] self.text_raw = f.read() #Removes Blank Lines, Logging Line, --- lines self.text = '\n'.join( line.strip() for line in self.text_raw.split('\n') if line.strip() #blank lines if not line.startswith('Logging by BUG') if not line.startswith('-----') ) reg = re.compile( '^Turn ', flags=re.MULTILINE | re.DOTALL ) #(?!^Turn)') raw_turns = reg.split(self.text) self.turns = [Turn(turn) for turn in raw_turns if turn] print self.name def graph_commerce(self): '''Creates and Outputs Single Turn Commerce Graph ''' nums = [turn.num for turn in self.turns] research = [turn.research_amount for turn in self.turns ] gold = [turn.gold_amount for turn in self.turns] espionage = [turn.espionage_amount for turn in self.turns] culture = [turn.culture_amount for turn in self.turns] #max = 0 game_length = max(nums) print min(nums), max(nums), len(nums) for n in [50, 100, 250, 500, game_length]: plt.figure(figsize=(10,5)) plt.plot( nums[:n+1], research[:n+1], color='blue', label='Research') plt.plot( nums[:n+1], gold[:n+1], color='gold', label='Gold') plt.plot(nums[:n+1], espionage[:n+1], color='black', label='Espionage') plt.plot(nums[:n+1], culture[:n+1], color='pink', label='Culture') plt.legend() plt.title(self.name) plt.ylabel('Commerce') plt.xlabel('Turn Number') plt.xlim(xmin=0, xmax=n) #plt.show() save_name = './output/%s_commerce_%03d.png' % ( self.name, n) print save_name plt.savefig(save_name) plt.close() def cleanse(self): '''Removes multiple turns.''' len_start = len(self.turns) #Running List of turns_to_keep = [] turn_nums_in_keepers = [] while self.turns: turn = self.turns.pop() #print turn.num, turn_nums_in_keepers if not turn.num in turn_nums_in_keepers: turn_nums_in_keepers.append(turn.num) turns_to_keep.append(turn) #print turn.num, turn_nums_in_keepers self.turns = turns_to_keep[::-1] if len_start != len(turns_to_keep): self.dirty = True def graph_population(self): '''An Approximate Graph Growth: Yields a firm number Whip: is -1 Draft: Not taken into account Found: Not taken into account Capture: Estimated at Average City Size TODO: take draft into account ''' pop_running_list = [] pop_dict = defaultdict(int) for turn in self.turns: for whip in turn.whips: pop_dict[whip] -= 1 for k, v in turn.growth.items(): pop_dict[k] = v for city in turn.captured: pop_dict[city] = ( sum(pop_dict.values()) / len(pop_dict)) pop_running_list.append( sum(pop_dict.values())) nums = [turn.num for turn in self.turns] game_length = max(nums) for n in [50, 100, 250, 500, game_length]: plt.figure(figsize=(10,5)) plt.plot( nums[:n+1], pop_running_list[:n+1], color='green')#, label='Population') #plt.legend() plt.title(self.name) plt.ylabel('Population') plt.xlabel('Turn Number') plt.xlim(xmin=0, xmax=n) save_name = './output/%s_pop_%03d.png' % ( self.name, n) print save_name plt.savefig(save_name) plt.close() print pop_running_list print len(pop_running_list) print 'Num', len(pop_dict) print pop_dict print sum(pop_dict.values()) / len(pop_dict) if __name__ == "__main__": '''Graphs all, for now.''' for log in log_list: log = Game(log) log.cleanse() log.graph_commerce() log.graph_population() print 'FINISHED'