'''
Created on Mar 10, 2018
@author: Brett Paufler
Copyright Brett Paufler
Parsing of the Tech Tree File
CIV4TechInfos.xml
Need to pull from below
and place in ref folder
C:\civ_4\Beyond the Sword\Assets\XML\Technologies
OrPreReqs: one or more of these techs required
AndPreReqs: all of these techs required
TECH_WRITING (as an example of multiple or's)
TECH_PRIESTHOOD
TECH_ANIMAL_HUSBANDRY
TECH_POTTERY
TECH_PRINTING_PRESS (example of multiple and's)
TECH_MACHINERY
TECH_ALPHABET
No and's or or's (first line, worker techs)
'''
#import xml.etree.ElementTree as ET
import re
#import networkx as nx
#import plotly
#import yellowbrick
from copy import copy
#from lxml import etree
#print text_raw
def left_align(text):
'''Returns left aligned copy of passed text.'''
text = '\n'.join(
line.strip()
for line in text.split('\n'))
return text
def find_all(tag, text):
'''Returns list of strings found in text
between xml tags of passed type
tag (str): xml tag to search for
text (str): string to search
returns list of strings: [str, str...]
or if nothing found
the empty list: []
'''
#Covers for an empty Or/And PreRequisite Listing
if not text:
return []
pattern = re.compile(
'<%s>(.*?)%s>' % (tag, tag),
re.IGNORECASE | re.DOTALL)
listing = re.findall(pattern, text)
return listing
def find_one(tag, text):
'''Returns a single string of text.
as found between tag.
If nothing found,
returns the empty string ''
'''
listing = find_all(tag, text)
#print listing
if listing:
listing = listing[0]
else:
listing = ''
return listing
class Tech(object):
def __init__(self, tech_str):
self.Name = find_one('Type', tech_str)[5:]
and_tech_str = find_one('AndPreReqs', tech_str)
and_techs = find_all('PrereqTech', and_tech_str)
self.AndPreReqs = [tech[5:] for tech in and_techs]
or_tech_str = find_one('OrPreReqs', tech_str)
or_techs = find_all('PrereqTech', or_tech_str)
self.OrPreReqs = [tech[5:] for tech in or_techs]
self.iGridX = int(find_one('iGridX', tech_str))
self.iGridY = int(find_one('iGridY', tech_str))
self.iCost = int(find_one('iCost', tech_str))
self.researched = False
def __repr__(self):
text = '%s (%d) (%d,%d)\n' % (
self.Name, self.iCost,
self.iGridX, self.iGridY)
text += '\tResearched: %s\n' % str(self.researched)
text += '\tAnd: %s\n' % str(self.AndPreReqs)
text += '\tOr: %s' % str(self.OrPreReqs)
if self.Name == 'FUTURE_TECH':
print 'Got a hit'
return text
class TechTree(object):
def __init__(self, file_in):
#print 'Init'
with open(file_in) as f:
text_raw = f.read()
text_left_aligned = left_align(text_raw)
tech_strings = find_all('TechInfo', text_left_aligned)
techs = [Tech(tech_str) for tech_str in tech_strings]
self.techs = techs
self.paths = []
#print self.techs
def get_tech_by_name(self, tech_name):
for tech in self.techs:
if tech.Name == tech_name:
return tech
def report(self):
for tech in self.techs:
print tech
def set_all_research(self, val=True):
'''Sets all research True or False
depending upon val.'''
for tech in self.techs:
tech.researched = val
#def all_research_false(self):
# for tech in self.techs:
# tech.researched = False
def set_single_research(self, technology, val=True):
'''Sets a single tech's researched value.'''
for tech in self.techs:
if tech.Name == technology:
#print 'HIT'
tech.researched = val
def path_clear(self):
self.paths = []
def path_add_goal(self, tech):
if self.paths:
for path in self.paths:
path.append(tech)
else:
self.paths = [{tech}]
def compute_paths(self):
x = True
while x:
x = self._path_incremental_add_prereq()
#print 'Loop'
def _path_incremental_add_prereq(self):
for path in self.paths:
for tech_name in path:
#print tech_name
tech = self.get_tech_by_name(tech_name)
#print tech
#if not all(and_prereq in path
# for and_prereq in tech.AndPreReqs):
# path.update(tech.AndPreReqs)
# return
#if not tech.AndPreReqs:
# all_and = True
if not all([prereq in path
for prereq in tech.AndPreReqs]):
path.update(tech.AndPreReqs)
#print 'All Update: returning', self.paths
return True
#print 'Never Get Here'
#print self.paths
#else:
# print 'No And'
#if not all_and:
# for path in self.paths:
# #for prerer
# path.append(tech_name)
if not tech.OrPreReqs:
any_or = True
else:
any_or = any(
[prereq in path
for prereq in tech.OrPreReqs])
#print 'Any_Or', any_or, tech.OrPreReqs
if not any_or:
self.paths.remove(path)
#new_path = copy(path)
for or_tech in tech.OrPreReqs:
#print 'Or Tech:', or_tech, new_path
#path.update(or_tech)
new_path = copy(path)
new_path.update([or_tech])
#print 'Or_tech', or_tech
#print 'New Path', new_path
self.paths.append(new_path)
#print self.paths
#print 'Or:', self.paths
return True
#path_copy = path.copy()
#Fell off edge, all prereqs in
return False
def icost_from_name(self, tech_name):
iCost = [t.iCost for t in self.techs
if tech_name == t.Name][0]
return iCost
def standard_tech_tree():
file_in = '.\\ref\\CIV4TechInfos.xml'
tech_tree = TechTree(file_in)
return tech_tree
if __name__ == '__main__':
#file_in = '.\\ref\\CIV4TechInfos.xml'
tech_tree = standard_tech_tree()
tech_tree.set_all_research(False)
tech_tree.set_all_research(True)
#tech_tree.report()
tech_tree.set_single_research('FUTURE_TECH', False)
#tech_tree.report()
tech_tree.path_add_goal('FUTURE_TECH')
#tech_tree._path_incremental_add_prereq()
#print tech_tree.paths
#tech_tree._path_incremental_add_prereq()
#print tech_tree.paths
#tech_tree._path_incremental_add_prereq()
#print tech_tree.paths
#print type(tech_tree.paths)
tech_tree.compute_paths()
print tech_tree.paths
print 'RESULT'
for path in tech_tree.paths:
print path
#edges = []
#for tech in techs:
#DG = nx.DiGraph()
#print DG
#for tech in techs:
#print tech
#DG.add
#OrPreReqs
#AndPreReqs
#iGridX
#iGridY
#iCost