Idle Wizard Wiki:Charts

From Idle Wizard Wiki
Jump to navigation Jump to search

Code to render the charts for various equations

#!/usr/bin/env python3
# coding: utf-8
#
# by Darktyle

import matplotlib.pyplot as plt
import numpy as np
from math import pow, sqrt, log10

'''
    All values from default can be overridden
    values that are 'none' in 'default' have to be set in each plot definintion
'''
plots = {
    'default' : {
        'title' : None,                                                         # default title
        'func' : None,                                                          # function to plot. For example: lambda x: x * 2
        'xlim' : (0, 20000),                                                    # touple of the form (start, end, stepsize) or (start, end) for x-values
        'xlabel' : 'Number of casts',                                           # x-label
        'ylabel' : 'Production bonus at 100% spell efficiency (%)',             # y-label
        'xlog' : False,                                                         # should the x-scale be logarithmic?
        'ylog' : False,                                                         # should the y-scale be logarithmic?
        },
    'TS' : {
        'title' : 'True Sorcery bonus based on casts',
        'xlim' : (0, 160000),
        'func' : lambda x: pow(x + 1, 0.58) * 100,
        },
    'ROP' : {
        'title' : 'Ritual of Power bonus based on casts',
        'func' : lambda x: pow(x + 1, 0.39) * 100,
        },
    'UK' : {
        'title' : 'Unclean Knowledge bonus based on casts',
        'func' : lambda x: pow(x + 1, 0.41) * 55,
        },
    'Ebon Truncheon' : {
        'title' : 'Ebon Truncheon bonus based on autoclicks',
        'func' : lambda x: pow(x + 1, 0.35) * 20,
        'xlabel' : 'Number of autoclicks',
        },
    'RoN' : {
        'title' : 'Rules of Nature bonus based on casts',
        'func' : lambda x: (x * 2) + 500,
        },
    'FoN' : {
        'title' : 'Force of Nature bonus based on autoclicks',
        'func' : lambda x: pow(x + 1, 0.35) * 100,
        'xlabel' : 'Number of autoclicks',
        },
    'RWYS' : {
        'title' : 'Reap What You Sow bonus based on total pet XP',
        'xlim' : (1e10, 1e13, 1e7),
        'func' : lambda x: pow(x * 0.4, 0.72) * 0.01,
        'xlabel' : 'Pet XP (total)',
        'xlog' : True,
        },
    'RP' : {
        'title' : 'Radiant Pools bonus based on casts',
        'func' : lambda x: pow(x + 1, 0.61) * 255,
        },
    'NF' : {
        'title' : 'Nightfall bonus based on casts',
        'func' : lambda x: pow(log10(x + 1), 0.98) * 2100,
        },
    'Golem XP' : {
        'title' : 'Golem base XP based on upgrades purchased',
        'xlim' : (0, 420),
        'func' : lambda x: pow(x, 1/3) * 0.5,
        'xlabel' : 'Upgrades',
        'ylabel' : 'XP gain',
        },
    'Mysteries' : {
        'title' : 'Mysteries gained based on total mana (logarithmic)',
        'xlim' : (0, 200),
        'func' : lambda x: sqrt((pow(10, x)/125000000000 + 1) - 1) / 2,
        'xlabel' : 'Mana gained (log)',
        'ylabel' : 'Mysteries',
        'ylog' : True,
        },
}

'''
    Some configuration for colors and output format
'''
font = {'color' : '#D5D4D4',
        'size' : 14,
         }
rcopts = {
    'lines.dotted_pattern' : '1, 5',
    'axes.edgecolor' : '#B0B0B0',
    'xtick.color' : '#D5D4D4',
    'ytick.color' : '#D5D4D4',
    'grid.color': 'black',
    'grid.linestyle': ':',
}
lwidth = 2.5
lcolor = 'y'
outformat = 'svg'

def main(plots):
    '''
    Plot each element from 'plots' but the default.
    'default' is passed as the 3rd parameter
    '''
    for pltname, pltdef  in plots.items():
        if pltname != 'default':
            makeplot(pltname, pltdef, plots['default'])

def makeplot(name, pltdef, default):
    ''' Create the plot according to 'pltdef', fallback to 'default' if an option is missing '''
    if pltdef.get('title', None) == None:
        print("No title provided for '{}'. Cannot plot anything".format(name))
        return
    outfile = pltdef.get('title', None) + '.' + outformat
    print("Creating '{}'...".format(outfile))

    xlim = pltdef.get('xlim', default.get('xlim', None))
    # if there is a stepsize defined, use that too
    if len(xlim) > 2:
        xvals = range(int(xlim[0]), int(xlim[1]), int(xlim[2]))
    else:
        xvals = range(int(xlim[0]), int(xlim[1]))
    yvals = [pltdef.get('func', lambda x: x)(x) for x in xvals]

    fig = plt.figure()

    plt.rc_context(rcopts)
    # plot the calculated values
    plt.plot(xvals, yvals, lcolor, lw=lwidth)

    plt.xlabel(pltdef.get('xlabel', default.get('xlabel', '')), fontdict=font)
    plt.ylabel(pltdef.get('ylabel', default.get('ylabel', '')), fontdict=font)
    plt.title(pltdef.get('title'), fontdict=font)

    # if x or y scale should be logarithmic, set it here
    if pltdef.get('xlog', default.get('xlog', False)):
        plt.xscale('log')
    if pltdef.get('ylog', default.get('ylog', False)):
        plt.yscale('log')

    plt.grid(True, which='both')

    ax = fig.add_subplot(111)
    for axis in ['top','bottom','left','right']:
        ax.spines[axis].set_linestyle(':')

    # set limits
    ymax = max(yvals)
    yticks = ax.get_yticks()
    ax.set_xlim(int(xlim[0]), int(xlim[1]))
    if ymax < yticks[-2]:
        ax.set_ylim(0, yticks[-2])
    else:
        ax.set_ylim(0, yticks[-1])

    # save figure to file
    plt.tight_layout()
    plt.savefig(outfile, format=outformat, transparent=True)
    plt.close(fig)

if __name__ == '__main__':
    main(plots)