Tired of exchanging which side an item should go on? Wish a computer could brute force a 100% balanced scale? Have I got the solution for you.
Contents
Guide to Balance Libra Scales with Python Code
Preamble
This requires a bit of Python knowledge to use, but only up to writing numbers in a list, commenting code out, and running a file. Also, have python installed or whatever to run the code. I have Python 3.11, but it probably works with some older versions as well.
To use the code for yourself, you’ll only need to change the values in one line and then run the code.
Also as a bit of a disclaimer: it has worked for my own personal use and satisfaction; If the code is bugged and happens to miss some cases, or doesn’t work for some input size, that’s an oopsies on my part, but I’m unlikely to fix the code.
I have tried to clean up the code a bit, but I’m bad at naming things. I normally use Java, so if the naming conventions are off, then that’s why.
And yes, the code can be more efficient (such as not picking the same value as different choices when the list has dupe weights), but I can’t be bothered to make the code longer and more complex for this guide.
Balancing with Known Items
So at some point back in the past, I had a bunch of items I wanted on the Libra scale, but couldn’t do the math to balance them 100%. So I wrote up a brute forcer to do it for me.
Code
def balancedLibra(items, target=None):
global iteration
iteration = 0
if target is None:
target = sum(items)//2
return findBalancedLibra(items, target, len(items)//2, 0)
def findBalancedLibra(items, target, left, min_i):
if left == 0:
global iteration
iteration+=1
if target == 0:
# print("Win")
print(items)
return True
else:
# print("Lose")
return False
for i in range(min_i, len(items)):
picked = items[i]
unpicked = [item for j, item in enumerate(items) if j != i]
newTarget = target - picked
if (findBalancedLibra(unpicked, newTarget, left-1, i)):
return False # give all combinations
# return True # give first combination only
#I want to find a balance of these items.
items = [90, 86, 70, 63, 49, 48, 45, 41, 38, 10]
balancedLibra(items)
print(iteration, "cases checked")
The way to use this is to write the weights of the items inside the items list. After running it, in the console should print the weights of one side of the scales. So just put the remaining items on the other.
If there are multiple valid combinations, it’ll print them all.
Example Output
The above code yields
[86, 70, 63, 41, 10]
[90, 49, 48, 45, 38]
251 cases checked
I’ll pick one, “[90, 49, 48, 45, 38]”, so the other side of the scales should be [86, 70, 63, 41, 10].
They very nicely sum up to 270 on each side.
Balancing with a Filler Item
So this is some more recent code I wrote (specifically, it’s from while playing the Gaiden DLC).
Strategy Explanation
There are probably multiple strategies to balancing the Libra, but my goto is to fill in all the pans with items I want except for one, then put in a filler item with the express purpose of getting 100% balance and completely ignoring its effect (as long as it doesn’t counteract any benefits).
I dub this strategy as the Hanging Weight, since this filler weight is just dangling there doing nothing. However, as an extension of this strategy, it might be possible to pick a hanging weight with more favorable effects by moving the other items around (thereby changing the target hanging weight).
But I don’t have the patience to make an exhaustive list of all possible hanging weights values. So I wrote up a brute forcer to do it for me.
Code
class ScaleBalance:
def __init__(self, emptySide, fullSide):
self.emptySide = emptySide
self.fullSide = fullSide
@property
def target(self):
return sum(self.fullSide) - sum(self.emptySide)
def __eq__(self, other):
return self.fullSide == other.fullSide and self.emptySide == other.emptySide
def __hash__(self):
return 0 #performance doesn't matter here.
def __lt__(self, other):
return self.target < other.target
def __str__(self):
return f"{self.target:>3} + {self.emptySide} | {self.fullSide}"
def hangingWeights(items):
global iteration
iteration = 0
options = set()
findHangingWeights(items, [], len(items)//2, 0, options)
options = sorted(option for option in options if option.target >= 0)
for option in options:
print(str(option))
return {option.target for option in options}
def findHangingWeights(remainingItems, pickedItems, numRemainingPicks, min_i, options):
# cannot pick anymore. No more space
if numRemainingPicks == 0:
global iteration
iteration+=1
option = ScaleBalance(pickedItems, remainingItems)
options.add(option)
return
#Pick a new item
for i in range(min_i, len(remainingItems)):
picked = remainingItems[i]
unpickedItems = [item for j, item in enumerate(remainingItems) if j != i]
newPickedItems = pickedItems + [picked]
#pick from the rest
findHangingWeights(unpickedItems, newPickedItems, numRemainingPicks-1, i, options)
#I want to find the weight to balance these items.
items = [76, 74, 70, 63, 63, 59, 48]
print(hangingWeights(items))
print(iteration, "cases checked")
The way to use this is to write the weights of the items inside the items
list. Remember to have space for an additional pan for the hanging weight. After running it, in the console should print the weights of both sides of the scales (delimited by a ‘|’), with the hanging weight values to the left of the ‘+’ sign. It also prints all the possible hanging weights as a set afterwards, so you don’t have to scroll for all the values.
If there are multiple combinations with the same hanging weight, they’ll be printed next to each other. This can help when the hanging weight shares an effect with one of your other items, so you might be able to have the hanging weight be on the same side to avoid the effects from cancelling out.
Example Output
Running the above code yields
13 + [76, 74, 70] | [63, 63, 59, 48]
27 + [76, 74, 63] | [70, 63, 59, 48]
35 + [76, 70, 63] | [74, 63, 59, 48]
35 + [76, 74, 59] | [70, 63, 63, 48]
39 + [74, 70, 63] | [76, 63, 59, 48]
43 + [76, 70, 59] | [74, 63, 63, 48]
47 + [74, 70, 59] | [76, 63, 63, 48]
49 + [76, 63, 63] | [74, 70, 59, 48]
53 + [74, 63, 63] | [76, 70, 59, 48]
57 + [76, 74, 48] | [70, 63, 63, 59]
57 + [76, 63, 59] | [74, 70, 63, 48]
61 + [74, 63, 59] | [76, 70, 63, 48]
61 + [70, 63, 63] | [76, 74, 59, 48]
65 + [76, 70, 48] | [74, 63, 63, 59]
69 + [74, 70, 48] | [76, 63, 63, 59]
69 + [70, 63, 59] | [76, 74, 63, 48]
79 + [76, 63, 48] | [74, 70, 63, 59]
83 + [74, 63, 48] | [76, 70, 63, 59]
83 + [63, 63, 59] | [76, 74, 70, 48]
87 + [76, 59, 48] | [74, 70, 63, 63]
91 + [74, 59, 48] | [76, 70, 63, 63]
91 + [70, 63, 48] | [76, 74, 63, 59]
99 + [70, 59, 48] | [76, 74, 63, 63]
105 + [63, 63, 48] | [76, 74, 70, 59]
113 + [63, 59, 48] | [76, 74, 70, 63]
{13, 27, 35, 39, 43, 47, 49, 53, 57, 61, 65, 69, 79, 83, 87, 91, 99, 105, 113}
35 cases checked
There’s this set of of hanging weights at the end {13, 27, 35, 39, 43, 47, 49, 53, 57, 61, 65, 69, 79, 83, 87, 91, 99, 105, 113}.
So I go over my inventory to check for an item with any of those weights, and find one that gives decent effects that don’t conflict with existing effects. Let’s say I found one that weights 65.
So I look above in the list and find one combination “65 + [76, 70, 48] | [74, 63, 63, 59]”, and that’s how I’ll arrange the Libra. Both sides will each weight 259.
Be the first to comment