In Dark Souls, players level up trading souls for stats. 8 stats are upgradable this way: vitality, attunement, endurance, strength, dexterity, resistance, intelligence, and faith. Each level corresponds to adding one point to a stat of the player's choice. Also, there are 10 possible classes each having their own starting level and stats:

```
Warrior     (Level 4):  11, 8, 12, 13, 13, 11, 9, 9
Knight      (Level 5):  14, 10, 10, 11, 11, 10, 9, 11
Wanderer    (Level 3):  10, 11, 10, 10, 14, 12, 11, 8
Thief       (Level 5):  9, 11, 9, 9, 15, 10, 12, 11
Bandit      (Level 4):  12, 8, 14, 14, 9, 11, 8, 10
Hunter      (Level 4):  11, 9, 11, 12, 14, 11, 9, 9
Sorcerer    (Level 3):  8, 15, 8, 9, 11, 8, 15, 8
Pyromancer  (Level 1):  10, 12, 11, 12, 9, 12, 10, 8
Cleric      (Level 2):  11, 11, 9, 12, 8, 11, 8, 14
Deprived    (Level 6):  11, 11, 11, 11, 11, 11, 11, 11
```

From level 1, the necessary souls to level up each time up to 11 are `673`, `690`, `707`, `724`, `741`, `758`, `775`, `793`, `811`, and `829`. Then from 11 to 12 and onwards the amount is defined by the expression `round(pow(x, 3) * 0.02 + pow(x, 2) * 3.06 + 105.6 * x - 895)` where `x` is the number corresponding to the next level.

Your function will receive a string with the character class and a list of stats. It should calculate which level is required to get the desired character build and the amount of souls needed to do so. The result should be a string in the format: `'Starting as a [CLASS], level [N] will require [M] souls.'` where `[CLASS]` is your starting class, `[N]` is the required level, and `[M]` is the amount of souls needed respectively.

Source: codewars | Difficulty: EASY
Tags: Ad-hoc

Starter code: def souls(character, build):
	

Solutions:
from itertools import accumulate
CHARACTERS = {'warrior': (4, [11, 8, 12, 13, 13, 11, 9, 9]), 'knight': (5, [14, 10, 10, 11, 11, 10, 9, 11]), 'wanderer': (3, [10, 11, 10, 10, 14, 12, 11, 8]), 'thief': (5, [9, 11, 9, 9, 15, 10, 12, 11]), 'bandit': (4, [12, 8, 14, 14, 9, 11, 8, 10]), 'hunter': (4, [11, 9, 11, 12, 14, 11, 9, 9]), 'sorcerer': (3, [8, 15, 8, 9, 11, 8, 15, 8]), 'pyromancer': (1, [10, 12, 11, 12, 9, 12, 10, 8]), 'cleric': (2, [11, 11, 9, 12, 8, 11, 8, 14]), 'deprived': (6, [11, 11, 11, 11, 11, 11, 11, 11])}
REQUIRED_SOULS = list(accumulate([0, 0, 673, 690, 707, 724, 741, 758, 775, 793, 811, 829] + [round(pow(x, 3) * 0.02 + pow(x, 2) * 3.06 + 105.6 * x - 895) for x in range(12, 1000)]))

def souls(character, build):
	(starting_level, stats) = CHARACTERS[character]
	delta = sum((b - s for (b, s) in zip(build, stats)))
	level = starting_level + delta
	souls = REQUIRED_SOULS[level] - REQUIRED_SOULS[starting_level]
	return f'Starting as a {character}, level {level} will require {souls} souls.'


D = {'warrior': (4, 86), 'knight': (5, 86), 'wanderer': (3, 86), 'thief': (5, 86), 'bandit': (4, 86), 'hunter': (4, 86), 'sorcerer': (3, 82), 'pyromancer': (1, 84), 'cleric': (2, 84), 'deprived': (6, 88)}
count = lambda x: round(pow(x, 3) * 0.02 + pow(x, 2) * 3.06 + 105.6 * x - 895)
memo = [0, 0, 673, 1363, 2070, 2794, 3535, 4293, 5068, 5861, 6672, 7501]

def need(level):
	while len(memo) <= level:
		memo.append(memo[-1] + count(len(memo)))
	return memo[level]

def souls(character, build):
	(level, stats) = D[character]
	goal = level + sum(build) - stats
	return f'Starting as a {character}, level {goal} will require {need(goal) - need(level)} souls.'


CLASSES = {'warrior': {'stats': [11, 8, 12, 13, 13, 11, 9, 9], 'lvl': 4}, 'knight': {'stats': [14, 10, 10, 11, 11, 10, 9, 11], 'lvl': 5}, 'wanderer': {'stats': [10, 11, 10, 10, 14, 12, 11, 8], 'lvl': 3}, 'thief': {'stats': [9, 11, 9, 9, 15, 10, 12, 11], 'lvl': 5}, 'bandit': {'stats': [12, 8, 14, 14, 9, 11, 8, 10], 'lvl': 4}, 'hunter': {'stats': [11, 9, 11, 12, 14, 11, 9, 9], 'lvl': 4}, 'sorcerer': {'stats': [8, 15, 8, 9, 11, 8, 15, 8], 'lvl': 3}, 'pyromancer': {'stats': [10, 12, 11, 12, 9, 12, 10, 8], 'lvl': 1}, 'cleric': {'stats': [11, 11, 9, 12, 8, 11, 8, 14], 'lvl': 2}, 'deprived': {'stats': [11, 11, 11, 11, 11, 11, 11, 11], 'lvl': 6}}

def souls(character, build):
	required_lvl = sum(build) - sum(CLASSES[character]['stats']) + CLASSES[character]['lvl']
	souls_needed = sum([calc_exp(lvl) for lvl in range(CLASSES[character]['lvl'] + 1, required_lvl + 1)])
	return f'Starting as a {character}, level {required_lvl} will require {souls_needed} souls.'

def calc_exp(lvl):
	return 673 + 17 * (lvl - 2) if lvl <= 8 else 775 + 18 * (lvl - 8) if lvl <= 11 else round(pow(lvl, 3) * 0.02 + pow(lvl, 2) * 3.06 + 105.6 * lvl - 895)


BASE = {'warrior': (4, 86), 'knight': (5, 86), 'wanderer': (3, 86), 'thief': (5, 86), 'bandit': (4, 86), 'hunter': (4, 86), 'sorcerer': (3, 82), 'pyromancer': (1, 84), 'cleric': (2, 84), 'deprived': (6, 88)}

def souls(s, a):
	(n, b) = BASE[s]
	m = sum(a) - b
	r = sum([673, 690, 707, 724, 741, 758, 775, 793, 811, 829][n - 1:n + m - 1]) + sum((round(i ** 3 * 0.02 + i ** 2 * 3.06 + 105.6 * i - 895) for i in range(12, n + m + 1)))
	return f'Starting as a {s}, level {n + m} will require {r} souls.'


def f(x):
	return round(0.02 * x ** 3 + 3.06 * x ** 2 + 105.6 * x - 895)

def souls(character, build):
	final_level = sum(build) - 83 + sum([1 if n in character else 0 for n in 'hissss'])
	start_level = 6 - sum([1 if n in character else 0 for n in 'awful cccn'])
	souls_needed = sum([17 * l + 16 * 41 if l < 8 else 18 * l + 11 * 59 if l < 12 else f(l + 1) for l in range(start_level, final_level)])
	return 'Starting as a ' + character + ', level ' + str(final_level) + ' will require ' + str(souls_needed) + ' souls.'


classes = {'warrior': (4, [11, 8, 12, 13, 13, 11, 9, 9]), 'knight': (5, [14, 10, 10, 11, 11, 10, 9, 11]), 'wanderer': (3, [10, 11, 10, 10, 14, 12, 11, 8]), 'thief': (5, [9, 11, 9, 9, 15, 10, 12, 11]), 'bandit': (4, [12, 8, 14, 14, 9, 11, 8, 10]), 'hunter': (4, [11, 9, 11, 12, 14, 11, 9, 9]), 'sorcerer': (3, [8, 15, 8, 9, 11, 8, 15, 8]), 'pyromancer': (1, [10, 12, 11, 12, 9, 12, 10, 8]), 'cleric': (2, [11, 11, 9, 12, 8, 11, 8, 14]), 'deprived': (6, [11, 11, 11, 11, 11, 11, 11, 11])}
levelup_souls = [0, 0, 673, 690, 707, 724, 741, 758, 775, 793, 811, 829]

def souls(character, build):
	(level, stats) = classes[character]
	up_level = sum(build) - sum(stats) + level
	s = 0
	for l in range(level + 1, up_level + 1):
		if l < 12:
			s += levelup_souls[l]
		else:
			s += round(pow(l, 3) * 0.02 + pow(l, 2) * 3.06 + 105.6 * l - 895)
	return 'Starting as a {}, level {} will require {} souls.'.format(character, up_level, s)


d = {'warrior': [4, [11, 8, 12, 13, 13, 11, 9, 9]], 'knight': [5, [14, 10, 10, 11, 11, 10, 9, 11]], 'wanderer': [3, [10, 11, 10, 10, 14, 12, 11, 8]], 'thief': [5, [9, 11, 9, 9, 15, 10, 12, 11]], 'bandit': [4, [12, 8, 14, 14, 9, 11, 8, 10]], 'hunter': [4, [11, 9, 11, 12, 14, 11, 9, 9]], 'sorcerer': [3, [8, 15, 8, 9, 11, 8, 15, 8]], 'pyromancer': [1, [10, 12, 11, 12, 9, 12, 10, 8]], 'cleric': [2, [11, 11, 9, 12, 8, 11, 8, 14]], 'deprived': [6, [11, 11, 11, 11, 11, 11, 11, 11]]}

def souls(character, build):
	l = d[character][0]
	p = sum(d[character][1])
	ap = sum(build)
	al = l + ap - p
	s = 0
	for i in range(l + 1, al + 1):
		if i <= 8:
			s = s + 673 + 17 * (i - 2)
		elif i <= 11:
			s = s + 775 + 18 * (i - 8)
		else:
			s = s + round(pow(i, 3) * 0.02 + pow(i, 2) * 3.06 + 105.6 * i - 895)
	return 'Starting as a {}, level {} will require {} souls.'.format(character, al, s)


def last_level():
	x = 12
	formul_level = lambda x: round(pow(x, 3) * 0.02 + pow(x, 2) * 3.06 + 105.6 * x - 895)
	list_level = [0, 673, 690, 707, 724, 741, 758, 775, 793, 811, 829]
	for i in list_level:
		yield i
	while True:
		yield formul_level(x)
		x += 1

def souls(character, build):
	base_class = {'warrior': (4, [11, 8, 12, 13, 13, 11, 9, 9]), 'knight': (5, [14, 10, 10, 11, 11, 10, 9, 11]), 'wanderer': (3, [10, 11, 10, 10, 14, 12, 11, 8]), 'thief': (5, [9, 11, 9, 9, 15, 10, 12, 11]), 'bandit': (4, [12, 8, 14, 14, 9, 11, 8, 10]), 'hunter': (4, [11, 9, 11, 12, 14, 11, 9, 9]), 'sorcerer': (3, [8, 15, 8, 9, 11, 8, 15, 8]), 'pyromancer': (1, [10, 12, 11, 12, 9, 12, 10, 8]), 'cleric': (2, [11, 11, 9, 12, 8, 11, 8, 14]), 'deprived': (6, [11, 11, 11, 11, 11, 11, 11, 11])}
	souls_level = last_level()
	paramets = base_class[character]
	souls = filter(lambda i: i > 0, (b - s for (s, b) in zip(paramets[1], build)))
	level = sum(souls) + paramets[0]
	souls = sum([next(souls_level) for i in range(level)][paramets[0]:])
	return 'Starting as a {0}, level {1} will require {2} souls.'.format(character, level, souls)


def souls(character, build):
	DICT = {'pyromancer': (1, [10, 12, 11, 12, 9, 12, 10, 8]), 'warrior': (4, [11, 8, 12, 13, 13, 11, 9, 9]), 'knight': (5, [14, 10, 10, 11, 11, 10, 9, 11]), 'wanderer': (3, [10, 11, 10, 10, 14, 12, 11, 8]), 'thief': (5, [9, 11, 9, 9, 15, 10, 12, 11]), 'bandit': (4, [12, 8, 14, 14, 9, 11, 8, 10]), 'hunter': (4, [11, 9, 11, 12, 14, 11, 9, 9]), 'sorcerer': (3, [8, 15, 8, 9, 11, 8, 15, 8]), 'cleric': (2, [11, 11, 9, 12, 8, 11, 8, 14]), 'deprived': (6, [11, 11, 11, 11, 11, 11, 11, 11])}
	list_upgrade_souls = [673, 690, 707, 724, 741, 758, 775, 793, 811, 829]
	levels_after_11 = [round(pow(x + 12, 3) * 0.02 + pow(x + 12, 2) * 3.06 + 105.6 * (x + 12) - 895) for x in range(5000)]
	list_upgrade_souls.extend(levels_after_11)
	cnt_souls = 0
	actual_powers = DICT[character][1]
	actual_level = DICT[character][0]
	desired_level = sum([x1 - x2 for (x1, x2) in zip(build, actual_powers)]) + actual_level
	for i in range(actual_level - 1, desired_level - 1):
		cnt_souls += list_upgrade_souls[i]
	return 'Starting as a {}, level {} will require {} souls.'.format(character, desired_level, cnt_souls)


classes = {'warrior': {'start': 4, 'stats': [11, 8, 12, 13, 13, 11, 9, 9]}, 'knight': {'start': 5, 'stats': [14, 10, 10, 11, 11, 10, 9, 11]}, 'wanderer': {'start': 3, 'stats': [10, 11, 10, 10, 14, 12, 11, 8]}, 'thief': {'start': 5, 'stats': [9, 11, 9, 9, 15, 10, 12, 11]}, 'bandit': {'start': 4, 'stats': [12, 8, 14, 14, 9, 11, 8, 10]}, 'hunter': {'start': 4, 'stats': [11, 9, 11, 12, 14, 11, 9, 9]}, 'sorcerer': {'start': 3, 'stats': [8, 15, 8, 9, 11, 8, 15, 8]}, 'pyromancer': {'start': 1, 'stats': [10, 12, 11, 12, 9, 12, 10, 8]}, 'cleric': {'start': 2, 'stats': [11, 11, 9, 12, 8, 11, 8, 14]}, 'deprived': {'start': 6, 'stats': [11, 11, 11, 11, 11, 11, 11, 11]}}
requirements = [0, 673, 690, 707, 724, 741, 758, 775, 793, 811, 829]

def souls(character, build):
	diff = sum((b - s for (b, s) in zip(build, classes[character]['stats'])))
	level = classes[character]['start'] + diff
	required = sum((x for x in requirements[classes[character]['start']:level]))
	required += sum((round(0.02 * x ** 3 + 3.06 * x ** 2 + 105.6 * x - 895) for x in range(12, level + 1)))
	return 'Starting as a {}, level {} will require {} souls.'.format(character, level, required)