3 ms·
The deep nesting and negation of the tests made this look weird to me and harder to see how the main idea was exhaustive searching of all permutations. I think
by falsedan 7y ago
The deep nesting and negation of the tests made this look weird to me and harder to see how the main idea was exhaustive searching of all permutations. I think it's clearer when the constraints are separated from the searching:
People = collections.namedtuple('People', ['Englishman', 'Spaniard', 'Ukrainian', 'Norwegian', 'Japanese']) # etc.
def rule_10(people): return people.Norwegian == 0
def rule_2(people, color): return people.Englishman == color.Red
def rule_6(color): return color.Ivory - color.Green == 1
def rule_15(people, color): return abs(people.Norwegian - color.Blue) == 1
def rule_4(beverage, color): return beverage.Coffee == color.Green
# etc.
constraints = [ rule_1, rule_2, ... ] # etc.
def inject(func, vars=locals):
signature = inspect.signature(func)
args_from = operator.attrgetter(signature.parameters.keys())
return func(*args_from(vars))
houses = range(0,5)
for people in map(People._make, perms(houses)):
for color in map(Color._make, perms(houses)):
for beverage in map(Beverage._make, perms(houses)):
for cigarette in map(Cigarette._make, perms(houses)):
for pet in map(Pet._make, perms(houses)):
if all(map(inject, constraints)):
print(f'The {People._fields[beverage.Water]} drinks water')
print(f'The {People._fields[pet.Zebra]} owns the zebra')
sys.exit(0)
print(f'Sorry, no idea who drinks water or owns a zebra')
sys.exit(1)
No pruning of the solution space but `all` will exit early if it can. You could change `inject` (terrible name) to pass the list of rules & call those whose signatures are fulfilled at the beginning of each for-loop, and return a list of the uncalled rules + the results of the called rules, and test the results with `all` & keep the uncalled rules handy for the next for-loop. I didn't do it because there's already too much boilerplate in the for-loops