Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Code Snippet around buildings including calculating costs.
# @Author: Tom-Oliver Heidel <theidel/blackjack4494>
# @Email: github@tom-oliver.eu
# @Date: 2021-07-21
# @Last modified by: theidel
# @Last modified time: 2021-07-22
__author__ = 'github@tom-oliver.eu (Tom-Oliver Heidel <theidel>)'
import json
from typing import Union, Tuple
# DEBUG
dbg = True
# can be put into dedicated class
# CONSTANTS
_DESCRIPTION_KEY = 'description'
_NAME_KEY = 'building_name' # 'name'
_LEVELS_KEY = 'levels'
_BONUSES_KEY = 'bonuses'
_COSTS_KEY = 'build_costs'
_TIME_KEY = 'build_time'
### https://github.com/STFCcommunity/data/blob/32a13b580d8e36221a9e8fe99aaeb1fb6c283049/buildings/operations.json
# f = open('buildings.json')
# f = open('academy.json')
f = open('operations.json')
a = json.load(f)
f.close()
# workaround to make sure data is sorted by level
a['levels'].sort(key=lambda x: (x.get('level')))
_data = a
# name = input('Enter name: ')
# level = int(input('Enter level: '))
name = 'Operations'
# name = 'Academy'
lvl = 18
lvl_upper = 21
def get_building(name: str = name, lvl: Union[int, Tuple[int, int]] = 1,
data: dict = _data, max_distance: int = 10) -> list:
'''
Wrapper for _get_building
Get a building by name and level if provided.
Params:
name (str): Name of the building
lvl (int)(Tuple[int, int]): Level of the building
data (dict): Data to search in
max_distance (int): Maximum level distance to search
Returns:
list: List of level objects of the building
Raises:
Exception: If building not found
Note:
name checking is loose and will match any substring
'''
return _get_building(name, lvl, data, max_distance)
def _get_building(name: str = name, lvl: Union[int, Tuple[int, int]] = None,
data: dict = _data, max_distance: int = 99) -> list:
'''
Get a building by name and level if provided.
Params:
name (str): Name of the building
lvl (int)(Tuple[int, int]): Level of the building
data (dict): Data to search in
max_distance (int): Maximum level distance to search
Returns:
list: List of level objects of the building
Raises:
Exception: If building not found
Note:
name checking is loose and will match any substring
'''
if name is None:
raise Exception('Name is required')
if lvl is None:
if name in data[_NAME_KEY]:
return data
if isinstance(lvl, int):
if lvl < 0:
raise ValueError('Level cannot be lower than 0')
if lvl > 50:
raise ValueError('Level cannot be greater than 50')
# arrays starts at 0 so take offset into account
if lvl > 0:
lvl -= 1
if name in data[_NAME_KEY]:
# check if it's really the right level
if data[_LEVELS_KEY][lvl]['level'] == lvl+1:
return data[_LEVELS_KEY][lvl]
else:
if dbg:
print('[DEBUG] MISMATCH ERROR - Returned (%s, Level %s) but requested level %s' % (name, data[_LEVELS_KEY][lvl]['level'], lvl+1))
raise ValueError('Level not found')
if isinstance(lvl, tuple):
if len(lvl) != 2:
raise ValueError('Level tuple must be of length 2')
_lower, _upper = lvl
if not isinstance(_lower, int):
raise ValueError('Level lower bound must be an integer')
if not isinstance(_upper, int):
raise ValueError('Level upper bound must be an integer')
if not isinstance(max_distance, int):
raise ValueError('Max distance must be an integer')
if abs(_lower - _upper) > max_distance:
raise ValueError('Level range too large')
if _lower < 0:
raise ValueError('Level lower bound cannot be lower than 0')
if _lower > 50:
raise ValueError('Level lower bound cannot be greater than 50')
if _upper < 0:
raise ValueError('Level upper bound cannot be lower than 0')
if _upper > 50:
raise ValueError('Level upper bound cannot be greater than 50')
if _lower > _upper:
raise ValueError('Level lower bound cannot be greater than upper bound')
if _lower == lvl_upper:
raise ValueError('Level lower bound cannot be equal to upper bound')
if _lower > 0:
_lower -= 1
if _upper > 0:
_upper -= 1
if name in data[_NAME_KEY]:
for _, _lvl in enumerate(data[_LEVELS_KEY][_lower:_upper+1], _lower):
if not (_lower <= _lvl['level'] <= _upper+1):
if dbg:
print('[DEBUG] MISMATCH ERROR - Returned (%s, Level %s) but requested level %s' % (name, _lvl['level'], _+1))
raise ValueError('Level range validation failed')
return data[_LEVELS_KEY][_lower:_upper+1]
raise Exception('Building not found')
def get_building_ALL(name: str = name, lvl: Union[int, Tuple[int, int]] = 1,
data: dict = _data, max_distance: int = 10) -> list:
'''
Wrapper for _get_building
Get a building by name and level if provided.
Params:
name (str): Name of the building
lvl (int)(Tuple[int, int]): Level of the building
data (dict): Data to search in
max_distance (int): Maximum level distance to search
Returns:
list: List of level objects of the building
Raises:
Exception: If building not found
Note:
name checking is loose and will match any substring
'''
return _get_building_ALL(name, lvl, data, max_distance)
def _get_building_ALL(name: str = name, lvl: Union[int, Tuple[int, int]] = None,
data: dict = _data, max_distance: int = 99) -> list:
'''
Get a building by name and level if provided.
Params:
name (str): Name of the building
lvl (int)(Tuple[int, int]): Level of the building
data (dict): Data to search in
max_distance (int): Maximum level distance to search
Returns:
list: List of level objects of the building
Raises:
Exception: If building not found
Note:
name checking is loose and will match any substring
'''
if name is None:
raise Exception('Name is required')
if lvl is None:
for _ in data:
if name in _[_NAME_KEY]:
return _
if isinstance(lvl, int):
if lvl < 0:
raise ValueError('Level cannot be lower than 0')
if lvl > 50:
raise ValueError('Level cannot be greater than 50')
# arrays starts at 0 so take offset into account
if lvl > 0:
lvl -= 1
for _ in data:
if name in _[_NAME_KEY]:
# check if it's really the right level
if _[_LEVELS_KEY][lvl]['level'] == lvl+1:
return _[_LEVELS_KEY][lvl]
else:
raise ValueError('Level not found')
if isinstance(lvl, tuple):
if len(lvl) != 2:
raise ValueError('Level tuple must be of length 2')
_lower, _upper = lvl
if not isinstance(_lower, int):
raise ValueError('Level lower bound must be an integer')
if not isinstance(_upper, int):
raise ValueError('Level upper bound must be an integer')
if not isinstance(max_distance, int):
raise ValueError('Max distance must be an integer')
if abs(_lower - _upper) > max_distance:
raise ValueError('Level range too large')
if _lower < 0:
raise ValueError('Level lower bound cannot be lower than 0')
if _lower > 50:
raise ValueError('Level lower bound cannot be greater than 50')
if _upper < 0:
raise ValueError('Level upper bound cannot be lower than 0')
if _upper > 50:
raise ValueError('Level upper bound cannot be greater than 50')
if _lower > _upper:
raise ValueError('Level lower bound cannot be greater than upper bound')
if _lower == lvl_upper:
raise ValueError('Level lower bound cannot be equal to upper bound')
if _lower > 0:
_lower -= 1
if _upper > 0:
_upper -= 1
for _ in data:
if name in _[_NAME_KEY]:
for _lvl in _[_LEVELS_KEY][_lower:_upper+1]:
if not (_lower <= _lvl['level'] <= _upper+1):
raise ValueError('Level range validation failed')
return _[_LEVELS_KEY][_lower:_upper+1]
raise Exception('Building not found')
def calculate_costs(data: list) -> list:
'''
Calculate the cost of a building
Params:
data (list): List of level objects of the building
Returns:
list: List of costs of the building
Note: Also generates a dictionary of the costs but won't be returned
'''
range_costs = [{'mats': [], 'rss': []}]
range_costs_dict = {'mats': [], 'rss': []}
mat_sums = dict()
rss_sums = dict()
mats = []
rss = []
for level in data:
for mat in level[_COSTS_KEY]['materials']:
mats.append(mat)
for rs in level[_COSTS_KEY]['resources']:
rss.append(rs)
for item in mats:
key = item['type'], item['grade'], item['rarity']
value = item['value']
try:
mat_sums[key]['value'] += value
except KeyError:
mat_sums[key] = item.copy()
for item in rss:
key = item['type']
value = item['value']
try:
rss_sums[key]['value'] += value
except KeyError:
rss_sums[key] = item.copy()
# mat_result = list(mat_sums.values())
range_costs[0]['mats'] = list(mat_sums.values())
range_costs_dict['mats'] = list(mat_sums.values())
range_costs[0]['rss'] = list(rss_sums.values())
range_costs_dict['rss'] = list(rss_sums.values())
return range_costs
print(calculate_costs(_get_building(name, (30, 42))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment