This forum is obsolete and read-only. Feel free to contact us at support.keenswh.com
  1. This forum is obsolete and read-only. Feel free to contact us at support.keenswh.com

Simple blueprints calculator

Discussion in 'Programming Guides and Tools' started by batkom, Jul 12, 2020.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. batkom Trainee Engineer

    Messages:
    1
    Hello.
    I met a problem when trying bo build ship from blueprint - i forgot to collect enough nickel and produce enough motors for thrusters.
    So i made simple python program that parses my blueprints, game data xml and then calculate all required components and production time.
    Here is the code:
    Code:
    import xml.etree.ElementTree as ET
    import os
    import json
    
    gamePath = ''
    blueprintsPath =''
    gameDefinitionsPath=''
    componentsBlueprintData=''
    
    assemblySpeed = 3
    assemblyEffiency = 3
    refinerySpeed = 3
    
    def getBlueprintBlocks(blueprintFile):
    	tree = ET.parse(blueprintFile)
    	root = tree.getroot()
    	blocks = root.iter('MyObjectBuilder_CubeBlock')
    	res = {}
    	for block in blocks:
    		typeId = block.get('{https://www.w3.org/2001/XMLSchema-instance}type').replace('MyObjectBuilder_','')
    		subTypeId = block.find('SubtypeName').text
    		if subTypeId is None: subTypeId = 'None'
    		if not typeId+subTypeId in res: res[typeId+subTypeId] = 1
    		else: res[typeId+subTypeId] += 1
    	return res
    
    def getGameDefinitions():
    	res = {}
    	for filename in os.listdir(gameDefinitionsPath):
    		tree = ET.parse(gameDefinitionsPath+'/'+filename)
    		root = tree.getroot()
    		blocks = root.iter('Definition')
    		for block in blocks:
    			typeId = block.find('Id/TypeId').text
    			subTypeId = block.find('Id/SubtypeId').text
    			if subTypeId is None: subTypeId = 'None'
    			compoList = {}
    			for component in block.findall('Components/Component'):
    				compoType = component.get('Subtype')
    				compoAmount = component.get('Count')
    				if not compoType in compoList: compoList[compoType] = int(compoAmount)
    				else: compoList[compoType] += int(compoAmount)
    			res[typeId+subTypeId] = compoList
    			
    	return res
    
    
    def getComponentsToProduce(blocks, recipies):
    	res = {}
    	for blockType in blocks:
    		if not blockType in recipies:
    			print('Warning: block type {} skipped, cant find recipie for this'.format(blockType))
    			continue
    		count = blocks[blockType]
    		components = recipies[blockType]
    		for component in components:
    			if not component in res: res[component] = components[component]*count
    			else: res[component] += components[component]*count
    	return res
    
    def getComponentsRecipies():
    	ignoreList = [
    		'HydrogenBottlesRefill',
    		'OxygenBottlesRefill',
    		'IceToOxygen',
    		'ScrapIngotToIronIngot',
    		'ScrapToIronIngot'
    	]
    	res = {}
    	tree = ET.parse(componentsBlueprintData)
    	root = tree.getroot()
    	items = root.iter('Blueprint')
    	for item in items:
    		result = item.find('Result')
    		if item.find('Id/SubtypeId').text in ignoreList: continue
    		if result is None: continue
    		resultAmount = float(result.get('Amount'))
    		subTypeId = result.get('SubtypeId')
    		if subTypeId == 'Scrap': continue
    		productionTime = float(item.find('BaseProductionTimeInSeconds').text)
    		prerequesites = item.findall('Prerequisites/Item')
    		compoList = {}
    		for component in prerequesites:
    			compoAmount = float(component.get('Amount'))
    			compoType = component.get('SubtypeId')
    			compoList[compoType] = compoAmount
    		res[subTypeId] = {
    			'time': productionTime,
    			'amount': resultAmount,
    			'compoList': compoList
    		}
    	return res
    
    def initialize():
    	global assemblySpeed, assemblyEffiency, refinerySpeed, gamePath, blueprintsPath, gameDefinitionsPath, componentsBlueprintData
    	try:
    		config = json.load(open('blueprintCalc.conf', 'r'))
    		assemblySpeed = config['assemblySpeed']
    		assemblyEffiency = config['assemblyEffiency']
    		refinerySpeed = config['refinerySpeed']
    		gamePath = config['gamePath']
    		blueprintsPath = config['blueprintsPath']
    	except:
    		assemblySpeed = int(input("Enter assembly speed multiplier:"))
    		assemblyEffiency = int(input("Enter assembly effiency multiplier:"))
    		refinerySpeed = int(input("Enter refinery speed multiplier:"))
    		gamePath = input("Enter game path:")
    		blueprintsPath = input("Enter blueprints path:")
    		config = {
    			'assemblySpeed': assemblySpeed,
    			'assemblyEffiency': assemblyEffiency,
    			'refinerySpeed': refinerySpeed,
    			'gamePath': gamePath,
    			'blueprintsPath': blueprintsPath
    		}
    		json.dump(config, open('blueprintCalc.conf', 'w'), indent=4)
    	gameDefinitionsPath = gamePath + '/Content/Data/CubeBlocks'
    	componentsBlueprintData = gamePath + '/Content/Data/Blueprints.sbc'
    
    initialize()
    gamedDefinitions = getGameDefinitions()
    recipies = getComponentsRecipies()
    if not os.path.isdir('blueprintCalcResults'): os.mkdir('blueprintCalcResults')
    
    for folderName in os.listdir(blueprintsPath):
    	try: blueprintBlocks = getBlueprintBlocks(blueprintsPath + '/' + folderName + '/bp.sbc')
    	except:
    		print('Warning: cannot parse blueprint ' + folderName)
    		continue
    	components = getComponentsToProduce(blueprintBlocks, gamedDefinitions)
    	print('Processing blueprint ' + folderName)
    
    	strCompo = 'Components:\n'
    	time=0.0
    	baseComponents = {}
    	for component in components:
    		count = components[component]
    		strCompo += '	{} : {}'.format(component, count) + '\n'
    		time += recipies[component]['time']*count/recipies[component]['amount']/assemblySpeed
    		for ingot in recipies[component]['compoList']:
    			if not ingot in baseComponents: baseComponents[ingot] = recipies[component]['compoList'][ingot]*count/recipies[component]['amount']
    			else: baseComponents[ingot] += recipies[component]['compoList'][ingot]*count/recipies[component]['amount']
    	strCompo += '  Production Time(h): {}\n'.format(time/60/60)
    
    	strIngots = 'Base Components:\n'
    	time=0.0
    	ores = {}
    	for ingot in baseComponents:
    		count = baseComponents[ingot]/assemblyEffiency
    		strIngots += '	{} : {}'.format(ingot, count) + '\n'
    		if not ingot in recipies: continue
    		time += recipies[ingot]['time']*count/recipies[ingot]['amount']/refinerySpeed
    		for ore in recipies[ingot]['compoList']:
    			if not ore in ores: ores[ore] = recipies[ingot]['compoList'][ore]*count/recipies[ingot]['amount']
    			else: ores[ore] += recipies[ingot]['compoList'][ore]*count/recipies[ingot]['amount']
    	strIngots += '  Refinery Time(h): {}\n'.format(time/60/60)
    
    	strOres = 'Ores:\n'
    	for ore in ores:
    		count = ores[ore]
    		strOres += '	{} : {}'.format(ore, count) + '\n'
    	f = open('blueprintCalcResults/' + folderName + '.txt', 'w')
    	f.write(strCompo)
    	f.write(strIngots)
    	f.write(strOres)
    At first run it does config where writes some parameters
    Here is config sample:
    Code:
    {
    	"assemblySpeed": 3,
    	"assemblyEffiency": 3,
    	"refinerySpeed": 3,
    	"gamePath": "D:\\games\\steam\\steamapps\\common\\SpaceEngineers",
    	"blueprintsPath": "C:\\Users\\username\\AppData\\Roaming\\SpaceEngineers\\Blueprints\\local"
    }
    As result you obtain folder named blueprintCalcResults containing some statistic for all your blieprints.
    Sample result:
    Code:
    Components:
    	SteelPlate : 623
    	MetalGrid : 183
    	Construction : 270
    	Motor : 249
    	InteriorPlate : 15
    	Display : 4
    	Computer : 41
    	BulletproofGlass : 40
    	PowerCell : 40
    	SmallTube : 18
    	LargeTube : 14
      Production Time(h): 0.21407407407407408
    Base Components:
    	Iron : 7799.5
    	Nickel : 746.6666666666666
    	Cobalt : 183.0
    	Silicon : 222.73333333333335
      Refinery Time(h): 0.35277976190476196
    Ores:
    	Iron : 11142.142857142859
    	Nickel : 1866.6666666666665
    	Cobalt : 610.0
    	Silicon : 318.1904761904762
    You can put it in table processor like excell and apply some other multiplyers like refinery count, modules etc.
    Hope this can be useful for someone.
     
Thread Status:
This last post in this thread was made more than 31 days old.