It sounds like the elegant solution to this is linear programming. I can help with that if you provide more details.
with only 88 items spread out amongst ten slots, brute forcing it wouldn't be terrible either. Some combination of the two might be the easiest.
update:
based on the update you gave, I think linear programming is overkill (and difficult to apply). I wrote you this fairly general solution. Study it and understand it. If anybody has any corrections or improvements, I'd love to hear them.
from itertools import ifilter, product
# Definition of ITEMS cut for brevity. See below.
def find_best(slots, maximize, constraints):
"""example call:
find_best(['helm', 'chest'], ['slashing', 'bludgeon'],
{'encumbrance': 12})
"""
# save the slot names to construct a nice return value
slot_names = slots
# pull the actual lists of items for each slot out of the global dict
slots = [ITEMS[slot] for slot in slots]
# this function calculates the value of a solution
value = lambda solution: sum(float(slot[attr]) for attr in maximize
for slot in solution)
# replace our constraints with functions to check solutions
constraints = [lambda solution:
sum(float(slot[attr]) for slot in solution) < constraint
for attr, limit in constraints.items()]
# start with all possible combinations
solutions = product(*slots)
# chain together ifilters to weed out the ones that fail any of the
# constraints. Note that for optimum performance, you should place the
# constraints in descending order of probability to fail
for constraint in constraints:
solutions = ifilter(constraint, solutions)
# We're going to do decorate, max, undecorate
solutions = ((value(solution), solution) for solution in solutions)
value, solution = max(solutions)
# get the indexes and return
return dict((name, slot.index(item)) for name, slot, item
in zip(slot_names, slots, solution))
Note that you should be storing the values as floats and not as strings! It's easier (because it's often automatic when you need it) to cast to string than a float. Then you can take the ugly casts out of my code. I was just to lazy to do it for you. Note that you can call with as many constraints as you want but only one set of attributes to maximize. This made sense to me. If you study the code and understand it, you should be able to modify it to suit your tastes.
Here's how I modified your data structure.
ITEMS = { 'helm': [{'Acid':' 2.71',
'Bludgeoning': '1.04',
'Cold': '2.71',
'Encumbrance': '8.00',
'Fire': '2.71',
'Holy': '2.71',
'Impact': '1.30',
'Lightning': '2.00',
'Name': 'Plate Helm',
'Piercing': '1.17',
'Slashing': '1.30',
'Unholy': '2.71'},
{'Acid': '2.18',
'Bludgeoning': '0.92',
'Cold': '2.18',
'Encumbrance': '7.00',
'Fire': '2.18',
'Holy': '2.18',
'Impact': '1.15',
'Lightning': '1.65',
'Name': 'Scale Helm',
'Piercing': '1.03',
'Slashing': '1.15',
'Unholy': '2.18'}],
'chest':[{'Acid': '5.47',
'Bludgeoning': '2.05',
'Cold': '5.47',
'Encumbrance': '32.00',
'Fire': '5.47',
'Holy': '5.47',
'Impact': '2.57',
'Lightning': '4.06',
'Name': 'Plate Chest',
'Piercing': '2.31',
'Slashing': '2.57',
'Unholy': '5.47'},
{'Acid': '4.45',
'Bludgeoning': '1.84',
'Cold': '4.45',
'Encumbrance': '28.00',
'Fire': '4.45',
'Holy': '4.45',
'Impact': '2.30',
'Lightning': '3.31',
'Name': 'Scale Cuirass',
'Piercing': '2.07',
'Slashing': '2.30',
'Unholy': '4.45'}]}
note that the values of the outermost dictionary are lists and not tuples as you said. There is a huge distinction!