Last active
April 16, 2019 02:24
-
-
Save szarroug3/7b9b7a3d0b42970864f1465119ac9120 to your computer and use it in GitHub Desktop.
FFXIV Craft Leveling Calculator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import math | |
import os | |
from collections import defaultdict | |
xp_chart = [300, 600, 1100, 1700, 2300, 4200, 6000, 7350, 9930, 11800, | |
15600, 19600, 23700, 26400, 30500, 35400, 40500, 45700, 51000, 56600, | |
63900, 71400, 79100, 87100, 95200, 109800, 124800, 140200, 155900, 162500, | |
175900, 189600, 203500, 217900, 232320, 249900, 267800, 286200, 304900, 324000, | |
340200, 356800, 373700, 390800, 408200, 437600, 467500, 498000, 529000, 864000, | |
1058400, 1267200, 1555200, 1872000, 2217600, 2592000, 2995200, 3427200, 3888000, 4470000, | |
4873000, 5316000, 5809000, 6364000, 6995000, 7722000, 8575000, 9593000, 10826000] | |
config_file = 'ffxiv.json' | |
if os.path.exists(config_file): | |
with open(config_file) as f: | |
config = json.load(f) | |
else: | |
config = {} | |
with open(config_file, 'w') as f: | |
json.dump(config, f, indent=4) | |
def get_xp_needed(): | |
start_level = int(input('Starting Level: ')) | |
end_level = int(input('Ending Level: ')) | |
total_xp_needed = 0 - int(input('Starting XP: ') or 0) | |
for i in range(start_level, end_level): | |
total_xp_needed += xp_chart[i-1] | |
return total_xp_needed | |
def get_quest_data(): | |
name = input('Quest Recipe Name: ') | |
if not name: | |
return | |
if name not in config: | |
add_recipe(name) | |
count = int(input('Recipe Count: ')) | |
xp = int(input('Quest XP: ')) | |
allowances = int(input('Allowances: ') or 1) | |
return name, count, xp, allowances | |
def add_recipe(name): | |
print('Ingredients for {}: '.format(name)) | |
recipe = {} | |
config[name] = {'recipe': recipe} | |
while True: | |
data = input() | |
if not data: | |
break | |
quantity, ing_name = data.split(' ', 1) | |
config[name]['recipe'][ing_name] = int(quantity) | |
yields = input('Yields: ') | |
if not yields: | |
yields = 1 | |
else: | |
yields = int(yields) | |
config[name]['yield'] = yields | |
for sub_name in config[name]['recipe'].keys(): | |
if sub_name not in config: | |
add_recipe(sub_name) | |
with open(config_file, 'w') as f: | |
json.dump(config, f, indent=4) | |
def calculate(xp_needed, quests): | |
if xp_needed <= 0: | |
return | |
base = defaultdict(int) | |
crafted = {} | |
leftovers = defaultdict(int) | |
order = 0 | |
for name, count, xp, allowances in quests: | |
xp_needed, needed = calculate_repeat(xp_needed, xp, allowances) | |
print('\nCompleting {} quest {} times will get you to {} XP needed'.format(name, needed, xp_needed)) | |
base, crafted, leftovers, order = get_recipe_data(name, base, crafted, leftovers, needed * count, order) | |
return base, crafted, order | |
def calculate_repeat(total_xp_needed, xp, allowances): | |
total_needed = math.ceil(total_xp_needed / xp) | |
if total_needed > allowances: | |
total_needed = allowances | |
total_xp_needed -= (total_needed * xp) | |
return total_xp_needed, total_needed | |
def get_recipe_data(name, base, crafted, leftovers, count, order=0): | |
if len(config[name]['recipe']) > 0: | |
if leftovers.get(name, 0) > 0: | |
leftovers[name] -= count | |
if leftovers.get(name) >= count: | |
leftovers[name] -= count | |
count = 0 | |
else: | |
count -= leftovers[name] | |
leftovers[name] = 0 | |
if count <= 0: | |
return base, crafted, leftovers, order | |
if name not in leftovers: | |
have = int(input('How much {} do you have? '.format(name)) or 0) | |
left = have - count | |
if left < 0: | |
left = 0 | |
leftovers[name] += left | |
count -= have | |
if count <= 0: | |
return base, crafted, leftovers, order | |
need = int(math.ceil(count / config[name]['yield'])) | |
leftovers[name] += config[name]['yield'] * count | |
for sub_name, sub_count in config[name]['recipe'].items(): | |
total_sub_count = need * sub_count | |
base, crafted, leftovers, order = get_recipe_data(sub_name, base, crafted, leftovers, total_sub_count, order=order) | |
if name not in crafted: | |
order += 1 | |
crafted[name] = {'count': need, 'order': order} | |
else: | |
crafted[name]['count'] += need | |
else: | |
if name not in leftovers: | |
have = int(input('How much {} do you have? '.format(name)) or 0) | |
leftovers[name] += have - count | |
count -= have | |
if count < 0: | |
count = 0 | |
base[name] += count | |
return base, crafted, leftovers, order | |
if __name__ == '__main__': | |
total_xp_needed = get_xp_needed() | |
print('Total XP Needed:', total_xp_needed) | |
quests = [] | |
while True: | |
data = get_quest_data() | |
if not data: | |
break | |
quests.append(data) | |
base, crafted, order = calculate(total_xp_needed, quests) | |
if base: | |
print('\nBase Ingredients:') | |
for name, count in base.items(): | |
if count > 0: | |
print('{} {}'.format(count, name)) | |
if crafted: | |
print() | |
if crafted: | |
print('Crafted Recipes:') | |
for name, data in sorted(list(crafted.items()), key=lambda x:x[1]['order']): | |
if data['count'] > 0: | |
print('{} {}'.format(data['count'], name)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment