ASTLIBRA: Revision – How to Balance Libra Scales with Python Code

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.

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.

Egor Opleuha
About Egor Opleuha 4436 Articles
Egor Opleuha, also known as Juzzzie, is the Editor-in-Chief of Gameplay Tips. He is a writer with more than 12 years of experience in writing and editing online content. His favorite game was and still is the third part of the legendary Heroes of Might and Magic saga. He prefers to spend all his free time playing retro games and new indie games.

Be the first to comment

Leave a Reply

Your email address will not be published.


*