HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Finding items of related dependencies in Factorio

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
relateddependenciesitemsfindingfactorio

Problem

Factorio is a game about building factories and about automation. Items can be crafted (just like Minecraft) yourself, or crafted automatically by assembling machines. To help me expand my factories I created a little script in Python 3 that shows me what other items that I can produce once I create factory of an item.

Given a list of Factorio item recipes in CSV format extracted from the game by this script (the sample input file is also there) and a "tolerance" which is a natural number that describes how many other dependent items I have to add, I produce a list of related recipes.

import csv

recipes = {}
tolerance = 0

def append_ignore_none(dictionary, key, value):
    if key in dictionary:
        dictionary[key].add(value)
    else:
        dictionary[key] = {value}

with open('rec.csv') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        append_ignore_none(recipes, row[0], row[1])

dependents = {}

for kleft, vleft in recipes.items():
    for kright, vright in recipes.items():
        if kleft != kright and vleft.issuperset(vright):
            needed_items = vleft.symmetric_difference(vright)
            if len(needed_items) <= tolerance:
                if len(needed_items) != 0:
                    print("Factory of {0} can also produce {1}, just add {2}".format(kleft, kright, ",".join(needed_items)))
                else:
                    print("Factory of {0} can also produce {1}".format(kleft, kright))


Sample output (tolerance = 0):

`Factory of logistic-chest-storage can also produce logistic-chest-requester
Factory of logistic-chest-storage can also produce logistic-chest-active-provider
Factory of logistic-chest-storage can also produce logistic-chest-passive-provider
Factory of empty-barrel can also produce steel-chest
Factory of piercing-shotgun-shell can also produce big-electric-pole
Factory of piercing-shotgun-shell can also produce piercing-bullet-magazine
Factory of piercing-shotgun-shell can also produc

Solution

def append_ignore_none(dictionary, key, value):
    if key in dictionary:
        dictionary[key].add(value)
    else:
        dictionary[key] = {value}


Dictionaries already have a method to do that, it's called setdefault:

dictionary.setdefault(key, set()).add(value)


But since that's kind of clunky, and you do it every time you update the dictionary, use make recipes a defaultdict(set), and it's just recipes[key].add(value).

for kleft, vleft in recipes.items():
    for kright, vright in recipes.items():


Iterate over the cartesian product instead using itertools:

for kleft, vleft, kright, vright in it.product(recipes.items(), repeat=2):
     ...


Sets have easy-to-read operator forms for a lot of operations, so:

if kleft != kright and vleft.issuperset(vright):
    needed_items = vleft.symmetric_difference(vright)


Can be:

if kleft != kright and vleft >= vright:
    needed_items = vleft ^ vright


Are you sure that this does the right thing, by the way? I would have expected either needed_items should be the asymmetric difference (vleft - vright), or the condition to be vleft.is_disjoint(vright).

You define dependents = {}, but you don't seem to use it. I'm guessing that you'll be expanding the script later and you plan to use it then, but it's better to leave it out until you are ready to write that code.

Instead of doing:

if len(needed_items) <= tolerance:
    if len(needed_items) != 0:
        print("Factory of {0} can also produce {1}, just add {2}".format(kleft, kright, ",".join(needed_items)))
    else:
        print("Factory of {0} can also produce {1}".format(kleft, kright))


You can flatten this and save a level of indentation by reversing the order of the conditions and using elif. While we're at it, the preferred way of testing if a set (or any other collection) is empty is just if needed_items:

if not needed_items:
    print("Factory of {0} can also produce {1}".format(kleft, kright))
elif len(needed_items) <= tolerance:
    print("Factory of {0} can also produce {1}, just add {2}"
        .format(kleft, kright, ",".join(needed_items)))


I've also line wrapped the second print, since it was very long.

Finally, you can omit the numbers inside the braces in the format strings, like this:

"Factory of {} can also produce {}".format(kleft, kright)

Code Snippets

def append_ignore_none(dictionary, key, value):
    if key in dictionary:
        dictionary[key].add(value)
    else:
        dictionary[key] = {value}
dictionary.setdefault(key, set()).add(value)
for kleft, vleft in recipes.items():
    for kright, vright in recipes.items():
for kleft, vleft, kright, vright in it.product(recipes.items(), repeat=2):
     ...
if kleft != kright and vleft.issuperset(vright):
    needed_items = vleft.symmetric_difference(vright)

Context

StackExchange Code Review Q#98640, answer score: 6

Revisions (0)

No revisions yet.