Quick actions

cmd+k|ctrl+k

Navigation

Languages

IK dice

Snippet info

Language

Python

Visibility

public

Author

linus.aspelin

Created

2020-04-17T15:44:10Z

Updated

2020-05-04T14:44:40Z

#####################################
#    settings                       #
#####################################
decimals = 0


#####################################
#  description                      #
#####################################   

# Functions

# occurs(number, keep)
#   - how often a die occurs in the given roll

# success(number, keep, add = 0, threshold = 0, compared = False)
#   - how big the chance of succeeding a roll with a given threshold (difficulty)
#   - with threshold, displays current roll as 0 and between -3 and +3 as results for a modified add value
#   - without threshold, displays all possible outcomes

# crit(number, keep, add, threshold)
#   - the rate of critical success for a given roll
#   - without threshold, only critical failures are excluded

# avg(number, keep, add)
#   - the average outcome for a given roll

# values used in functions:
# number    - how many dice is rolled
# keep      - how many dice to keep
# add       - what modifier is added to the dice
#   - default: 0 
# threshold - what the target value is for the roll.
#   - default: 0
#   - disabled if 0 
# compared  - if compared is selected, lets you view with other compared rolls to form columns by threshold
#   - default: False
#   - compared must be used as a keyword, for example: success(4, 3, compared=True)
#   - compared is designed to be used together with additional calls with compared=True
#   - compared is not designed to work together with threshold. leave threshold at 0 if using compared mode



# Calculations
def run():
    ############################################
    #  write your prefered calculations below  #
    ############################################
    
    print('Example of a boosted virtuoso roll against DEF 13 with MAT 5:')
    print('success rate')
    success(4, 3, 5, 13)
    print('crit rate')
    crit(4, 3, 5, 13)
    print('avg value')
    avg(4, 3, 5)
    print()
    print('...and the same roll without the boost')
    print('success rate')
    success(3, 2, 5, 13)
    print('crit rate')
    crit(3, 2, 5, 13)
    print('avg value')
    avg(3, 2, 5)
    print()
    
    print('dice occurance by roll variant:')
    occurs(2, 2)
    occurs(3, 2)
    print()
    
    print('success by target value:')
    print('without specified target, shows success rate for all possible targets')
    print('with compare:')
    success(2, 2, 3, compared=True)
    success(2, 2, 2, compared=True)
    success(2, 2, 1, compared=True)
    success(3, 2, compared=True)
    success(3, 3, compared=True)
    success(5, 3, compared=True)
    print('without compare:')
    success(5, 3)
    print()
    print('with specified target, shows success rate for target value +/- 3')
    print('manfred\'s alchemy:')
    print('assassin\'s venom')
    success(3, 3, 6, 17)
    print('blackbond')
    success(3, 3, 6, 12)
    print()
    print('blackbond removal vs STR 4')
    success(2, 2, 4, 10)
    print()
    
    print('average value')
    avg(2, 2)
    avg(3, 2)
    avg(3, 3)
    avg(4, 3)
    avg(2, 2, 4)
    print()
    
    print('chance to crit')
    crit(2, 2)
    crit(3, 2)
    crit(3, 3)
    crit(2, 2, 7, 10)
    crit(3, 2, 5, 10)
    print()


#######################################################################
# you probably don't need to go passed this line to use the functions #
#######################################################################
# derived values
decimal_just = decimals + 1 if decimals else 0
comb_cache = {}

# code behind

def comb(number):
    def inner(num, acc):
        if not num:
            yield acc
        else:
            for cur in range(1, 7):
                yield from inner(num-1, [*acc, cur])
    
    if number not in comb_cache:
        comb_cache[number] = [*inner(number, [])]
    return comb_cache[number]
    

def comb_keep(number, keep):
    if (number, keep) not in comb_cache:
        comb_cache[number, keep] = [sorted(x)[number-keep:] for x in comb(number)]
    return comb_cache[number, keep]

def percent(part, whole):
    value = part/ whole
    return str.rjust(format(value, f'.{decimals}%'), 4 + decimal_just)

def display_roll_variant(number, keep, add=0, threshold=0):
    keep_text = f'k{keep}' if keep != number else ''
    add_text = f'+{add}' if add else ''
    threshold_text = f' ≥ {threshold}' if threshold else ''
    print(end=str.ljust(f'{number}d6{keep_text}{add_text}{threshold_text}: ', 14))

def is_crit(dice, add=0, threshold=0):
    if (sum(dice) + add) < threshold:
        return False;
    for (i, die) in enumerate(dice[1:]):
        if dice[i] == die:
            return True
    return False

def crit(number, keep, add=0, threshold=0):
    display_roll_variant(number, keep, add, threshold)
    crits = [is_crit(x, add, threshold) for x in comb_keep(number, keep)[1:]]
    print(percent(sum(crits), len(crits)))
        
    
def avg(number, keep, add=0):
    display_roll_variant(number, keep, add)
    dice = [sum(x) for x in comb_keep(number, keep)]
    result = sum(dice) * 100 // len(dice) / 100
    result += add
    print(f'{result:.2f}')
    
def occurs(number, keep):
    display_roll_variant(number, keep)
    levels = {}
    dice = [die for dice in comb_keep(number, keep) for die in dice]
    for value in range(1,7):
        levels[value] = sum(x == value for x in dice)
    total = len(dice)
    print(', '.join(f'[{key}]: {percent(level, total)}' for (key, level) in levels.items()))
    
def success(number, keep, add=0, threshold=0,*, compared=False):
    value_range = [*range(keep, keep*6 + 1)]
    display_roll_variant(number, keep, add, threshold)
    comb_sums = [sum(x) for x in comb_keep(number, keep)]
    levels = [comb_sums.count(v) for v in value_range]
    total = sum(levels)
    values = []
    
    for i in range(len(levels)):
        if not i:
            continue
        value = value_range[i] + add
        if threshold:
            diff = threshold-value
            if abs(diff) > 3:
                continue
            plus = '+' if diff > 0 else ''
            line = f'[{plus}{diff}]:'
        else:
            line = f'[{value}]:'
        line += percent(sum(levels[i:]), total)
        values.append(line)
    if threshold:
        values = values[::-1]
    if compared:
        print(end=' '*(add+keep-2)*(10 + decimal_just))
    print(', '.join(values))

run()
INFO