Freddie R
Freddie R

Reputation: 387

How to round to specific values in Python

I am working on an algorithm to automatically create character sheets for a roleplaying game. In the game, you have attributes which you put points into to increase them. However, at a certain value it takes 2 points to increase the value of the actual attribute by 1. You start of with a certain number of points, and each attribute has a value of 1 by default

I have a program that randomly assigns the points, however I am stuck as to how I then change these values (that are in a dictionary) to round down when necessary.

For example, if I put 3 points in "strength", thats fine, I get a "strength" value of 3 (including tha base 1). However, if I put 4 points in, I still should only have a value of 4. It should take 5 points (plus the base 1) in order to get a value of 5. It then takes another 2 points to get a value of 6, 3 points to get a value of 7 and 3 points to get a value of 8.

The code I am currently using to assign the attibutes looks like this:

attributes = {}
row1 = ['strength', 'intelligence', 'charisma']
row2 = ['stamina', 'willpower']
row3 = ['dexterity', 'wits', 'luck']

def assignRow(row, p): # p is the number of points you have to assign to each row
    rowValues = {}
    for i in range(0, len(row)-1):
        val = randint(0, p)
        rowValues[row[i]] = val + 1
        p -= val
    rowValues[row[-1]] = p + 1
    return attributes.update(rowValues)

assignRow(row1, 7)
assignRow(row2, 5)
assignRow(row3, 3)

What I want is just a simple function that takes the dictionary "attributes" as a parameter, and converts the number of points each attribute has to the proper value it should be.

i.e. "strength": 4 stays as "strength": 4, but "wits": 6" goes down to "wits": 5", and "intelligence: 9 goes down to "intelligence: 7".

I'm somewhat new to using dictionaries and so the ways I would normally approach this:

def convert(list):
    for i in range(len(list)):
        if list[i] <= 4:
            list[i] = list[i]
        if list[i] in (5, 6):
            list[i] -= 1
        if list[i] in (7, 8):
            list[i] -= 2
        if list[i] in (9, 10):
            list[i] = 6
        if list[i] in (11, 12, 13):
            list[i] = 7
        else:
            list[i] = 8

Not efficient or pretty but still a solutuion. However, you can't just loop over indexes in a dictionary so I am not entirely sure how to go about something like this.

General explanation or function would be appreciated.

Upvotes: 10

Views: 2014

Answers (4)

Slam
Slam

Reputation: 8572

Seems that bisection algo suits your needs pretty well - points to "invest" is always sorted and defined. Create array with reference points and you're good to without bunch of ifs:

>>> from bisect import bisect
>>> points_to_invest = [1, 2, 3, 4, 6, 8, 10, 13]
>>> bisect(points_to_invest, 1)
1
>>> bisect(points_to_invest, 4)
4
>>> bisect(points_to_invest, 5)
4
>>> bisect(points_to_invest, 6)
5

This approach will give you way easier maintainability for future

Upvotes: 6

Dmitrii Sutiagin
Dmitrii Sutiagin

Reputation: 571

A bit less space then your "convert" function, though still manual labor:

p_to_v = {1:1, 2:2, 3:3, 4:4, 5:4, 6:5, 7:5, 8:6} # 'translator' dict, fill up further
input = {'strength':6, 'wits':8} # dict with stats and their points
output = dict((stat, p_to_v[point]) for stat, point in input.items()) # {'strength':5, 'wits':6}

If you want your 'translator' to take less manual work and scale better then you can pre-generate it via some code, depending on your logic of points to values.

Upvotes: 1

Eric Duminil
Eric Duminil

Reputation: 54223

After many trial and error, here's a one-liner function:

def convert(x):
    return x - (x > 4) - (x > 6) - (x > 8) * (x - 8) + (x > 10) + (x > 13)

Here's a test:

print([(points, convert(points)) for points in range(20)])
# [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 4), (6, 5), (7, 5), (8, 6), (9, 6), (10, 6), (11, 7), (12, 7), (13, 7), (14, 8), (15, 8), (16, 8), (17, 8), (18, 8), (19, 8)]

if and elif statements might be clearer, though.

This function simply converts an amount of input points to a value. You can apply the convert function to each element of a list.

Upvotes: 0

BlaXXuN
BlaXXuN

Reputation: 121

You can loop over the elements by using dictionary.items() You can then modify your convert function:

def convert(attributes):
    for key, value in attributes.items():
        # do your conversion on value here
        if value <= 4:
            continue # do nothing for this element
        elif value in (5, 6):
            value -= 1
        elif value in (7, 8):
            value -= 2
        elif value in (9, 10):
            value = 6
        elif value in (11, 12, 13):
            value = 7
        else:
            value = 8

        # to replace the value in the dictionary you can use
        attributes[key] = new_value

Upvotes: 2

Related Questions