''' Created on Mar 23, 2019 @author: Brett Paufler (c) Copyright Brett Paufler Taking a CIV 4 BAT Logfile as input Outputs Combat Graphs ''' from os import listdir from os.path import join as path_join from collections import namedtuple import re import matplotlib.pyplot as plt #################################################### # FILE NAME BOOK KEEPING #################################################### #Standard Defaults dir_in = './input/' dir_out = './output/' #File of Interest: Assumes only one logfile_in = path_join(dir_in, listdir(dir_in)[0]) #print logfile_in #Create the Two Save Names base_name = logfile_in[:-4] base_name = base_name.replace(dir_in, dir_out) base_name = base_name.replace(' ', '_') base_name = base_name.lower() save_name_combats = base_name + '_combats.png' save_name_strength = base_name + '_strength.png' save_name_text = base_name + '_combat_tables.html' #print save_name_combats #print save_name_strength #Create a name for the game game_name = logfile_in[8:-4] #print game_name #################################################### # Get Listing of Logfile Items # Load, Extract, Split # Pass on as a raw list of text entries #################################################### #Get the text with open(logfile_in, 'r') as f: log_text = f.read() #print log #Split into a List of Individual Log Items log_items = log_text.split('\n') log_items = [item.strip() for item in log_items if item] #print log_items #################################################### # # The Main Log File Loop # Returns a list of # combat_events # #################################################### #This section returns a list of combat events combat_event = namedtuple( 'combat_event', ['turn', 'attack', 'win', 'str_me', 'Str_opp']) #turn: Turn # #attack: True if I am attacker, False=defender #win: True if I won, False=Lose #str_me: strength at time of combat #str_opp: strength at time of combat #print combat_event #test_event = combat_event(99, True, True, 8, 4) #print test_event #This is to track Capitulation and Elimination conquest_event = namedtuple( 'conquest_event', ['turn', 'civ', 'event']) #print conquest_event #test_event = conquest_event(199, 'Losers', 'Capit') #print test_event #This is updated, sequentially, as loop runs turn = 0 combats = [] conquests = [] #The Main Loop for item in log_items: #Initialize Data to None #If None passes through, we have an error attack = None win = None str_me = None str_opp = None #Updates Turn Info if item.startswith('Turn'): turn = int(item[5:].split('/')[0]) #print turn, type(turn) continue #For each Combat if item.startswith('While '): # attacking') or item.startswith('While defending'): #Set attack if 'attacking' in item: attack = True elif 'defending' in item: attack = False #Set win if 'loses' in item: win = False elif 'defeats' in item: win = True #Set str_me, str_opp #Siege Falls Through and is Not Handled both_strengths = re.search(r'\(\d.+?\)', item) if both_strengths: str_me, str_opp = both_strengths.group(0)[1:-1].split('/') str_me = float(str_me) str_opp = float(str_opp) #print item #print str_me, str_opp else: #print 'Siege Attack Not Handled' #print item continue #We have data assert attack != None assert win != None assert str_me != None assert str_opp != None this_combat = combat_event( turn, attack, win, str_me, str_opp) #print item #print this_combat combats.append(this_combat) #Conquest Events: Capitulation - Elimination if item.endswith('has been eliminated'): this_event = conquest_event( turn, item[:-20], 'Destroyed') conquests.append(this_event) #print this_event elif item.endswith(' becomes a Vassal State of Master Controller'): this_event = conquest_event( turn, item[:-44], 'Capitulates') conquests.append(this_event) #print this_event if 'Great General' in item: this_event = conquest_event( turn, None, 'Great General') conquests.append(this_event) #print combats print 'Total Combats %d' % len(combats) print 'Total Events %d' % len(conquests) '''This Section passes through combats: a lisitng of combat_events conquests: a listing of conquest_events ''' ################################################### # Data Preparation # I could have collected better data types ################################################### turns_in_game = max([c.turn for c in combats]) print 'turns_in_game = %d' % turns_in_game #For tracking count data (number) att_win_num = [0] * (turns_in_game + 1) att_lose_num = [0] * (turns_in_game + 1) def_win_num = [0] * (turns_in_game + 1) def_lose_num = [0] * (turns_in_game + 1) #print len(att_win_num), att_win_num #For tracking Combat Strength data att_win_str = [0] * (turns_in_game + 1) att_lose_str = [0] * (turns_in_game + 1) def_win_str = [0] * (turns_in_game + 1) def_lose_str = [0] * (turns_in_game + 1) #Gather Data for c in combats: if c.attack and c.win: att_win_num[c.turn] += 1 att_win_str[c.turn] += c.str_me elif c.attack and (not c.win): att_lose_num[c.turn] += 1 att_lose_str[c.turn] += c.str_me elif (not c.attack) and c.win: def_win_num[c.turn] += 1 def_win_str[c.turn] += c.str_me elif (not c.attack) and (not c.win): def_lose_num[c.turn] += 1 def_lose_str[c.turn] += c.str_me else: print 'Something Is Wrong' assert False max_combats_in_turn = max([aw+al+dw+dl for aw, al, dw, dl in zip(att_win_num, att_lose_num, def_win_num, def_lose_num) ]) #print zip(att_win_num, att_lose_num, def_win_num, def_lose_num) #print max_combats_in_turn max_strength_in_turn = max([aw+al+dw+dl for aw, al, dw, dl in zip(att_win_str, att_lose_str, def_win_str, def_lose_str) ]) #print max_strength_in_turn ################################################### # GRAPH COMBATS (INCIDENTS) ################################################### fig, ax = plt.subplots( figsize=(15,5)) ax.stackplot( range(turns_in_game + 1), def_lose_num,att_lose_num, def_win_num, att_win_num, labels = ['Lost as Defender', 'Lost as Attacker', 'Won as Defender', 'Won as Attacker'], colors = ['red', 'pink', 'lime', 'green'], #title=game_name ) plt.title(game_name) plt.xlabel('Turn') plt.ylabel('Number of Combats') handles, labels = ax.get_legend_handles_labels() ax.legend( handles[::-1], labels[::-1], loc='upper left') for c in conquests: print c #Creates a Gray Line plt.axvline( c.turn, color='grey', alpha=0.25, label=c.civ ) #Text and Color for Event if c.event == 'Destroyed': event_text = '%s %s' % (c.civ, c.event) color = 'red' elif c.event == 'Capitulates': event_text = '%s %s' % (c.civ, c.event) color = 'green' elif c.event == 'Great General': event_text = 'Great General' color = 'black' #Applies the Event Text plt.text( c.turn, max_combats_in_turn, event_text, verticalalignment='top', rotation=90, fontsize=6, color=color) plt.xlim(xmin=0, xmax=turns_in_game + 1) plt.ylim(ymin=0, ymax=max_combats_in_turn + 1) #plt.show() plt.savefig(save_name_combats) plt.close() ################################################### # GRAPH STRENGTH ################################################### fig, ax = plt.subplots( figsize=(15,5)) ax.stackplot( range(turns_in_game + 1), def_lose_str, att_lose_str, def_win_str, att_win_str, labels = ['Lost as Defender', 'Lost as Attacker', 'Won as Defender', 'Won as Attacker'], colors = ['red', 'pink', 'lime', 'green'], #title=game_name ) plt.title(game_name) plt.xlabel('Turn') plt.ylabel('Strength of Combats') handles, labels = ax.get_legend_handles_labels() ax.legend( handles[::-1], labels[::-1], loc='upper left') #Capitulation & Civilization Destructions for c in conquests: #Creates a Gray Line plt.axvline( c.turn, color='grey', alpha=0.25, label=c.civ ) #Text and Color for Event if c.event == 'Destroyed': event_text = '%s %s' % (c.civ, c.event) color = 'red' elif c.event == 'Capitulates': event_text = '%s %s' % (c.civ, c.event) color = 'green' elif c.event == 'Great General': event_text = 'Great General' color = 'black' #Applies the Event Text plt.text( c.turn, max_strength_in_turn - 5, #bring down from top edge event_text, verticalalignment='top', rotation=90, fontsize=6, color=color) plt.xlim(xmin=0, xmax=turns_in_game + 1) plt.ylim(ymin=0, ymax=max_strength_in_turn + 1) #plt.show() plt.savefig(save_name_strength) plt.close() ################################################### # # Output Text Data # ################################################### #Get Some Totals tot_att_win_num = sum(att_win_num) tot_att_lose_num = sum(att_lose_num) tot_def_win_num = sum(def_win_num) tot_def_lose_num = sum(def_lose_num) tot_att_win_str = sum(att_win_str) tot_att_lose_str = sum(att_lose_str) tot_def_win_str = sum(def_win_str) tot_def_lose_str = sum(def_lose_str) text_tot_num = ''' Combats by Number
WinLose
Attack%d%d
Defence%d%d
''' % (tot_att_win_num, tot_att_lose_num, tot_def_win_num, tot_def_lose_num) #print text_tot_num text_tot_str = ''' Combats by My Stregth
WinLose
Attack%d%d
Defence%d%d
''' % (tot_att_win_str, tot_att_lose_str, tot_def_win_str, tot_def_lose_str) #print text_tot_str num_civs_destroyed = len( [event for event in conquests if event.event == 'Destroyed' or event.event == 'Capitulate']) num_great_generals = len( [event for event in conquests if event.event == 'Great General']) html_out = ''' Turns: %d

Combats: %d

Civilizations Dominated: %d

Great Generals: %d



Max Number of Combats in Turn: %d

%s\n\n\n


Maximum Combat Strength in a a Turn: %d

%s\n\n\n ''' % ( turns_in_game, len(combats), num_civs_destroyed, num_great_generals, max_combats_in_turn, text_tot_num, max_strength_in_turn, text_tot_str) with open(save_name_text, 'w') as f: f.write(html_out)