If you have a copy of Python 3 lying around, you can do this in a jiffy.
First off, transcribe the passwords in an array like this. (Don't type in the >>>
characters.)
>>> passwords = ["hideout", "flowing", "blanket", "dealing", "banning", "leaving",
"studies", "fencing", "battles", "chamber", "parties", "carrier",
"blazing", "various", "working", "beating", "mention", "wanting"]
We can make a sanity check and make sure all passwords are long the same:
>>> list(map(len, passwords))
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
Now we need to see how many letters are the same between any two passwords. It's a one-liner:
>>> def distance(a,b):
return sum(1 for al, bl in zip(a,b) if al == bl) if a != b else -1
If the passwords are actually the same we return -1 instead of 7 so we can ignore those cases.
Now we need the code to only pick the passwords that have as many matching characters as the game tells us. This is another one-liner. You give it the list of passwords, what you tried and didn't work and how many characters were right.
>>> def refine(candidates, wrong_pw, matches):
result = list(x for x in candidates if distance(x, wrong_pw) == matches)
return result
So let's say you tied the password "flowing" and got 1 character right. Ouch. You then use what we've defined like this:
>>> passwords = refine(passwords, "flowing", 1); passwords
['blanket', 'studies', 'parties', 'carrier', 'mention']
Okay, how about "studies"... nope, 3 correct letters.
>>> passwords = refine(passwords, "studies", 3); passwords
['parties']
Oh look, that must be the password!
So, how do we automize the choice of passwords from a pool? What we can do is look at each option and see, in the worst case, how many passwords that leaves us with. We can then pick the one with the lowest amount of worst-case remaining options. That password is the one with the best guarantee of whittling down the field.
This code checks every password against every other password against every other password to find the best candidate.
def get_next_guess(passwords):
scores = {}
for candidate in passwords:
results = []
remainder = (x for x in passwords if x != candidate)
for correct_pw in remainder:
no_alternatives = len(refine(remainder, candidate,
distance(candidate, correct_pw)))
results.append(no_alternatives)
scores[candidate] = max(results)
return min(scores, key = lambda x: scores[x])
This guesses 'parties' immediately on my computer, which is kind of lucky because 'beating' would've been just as good of a guess. Let's instead look at the one in the screenshot, where the correct password was 'WAITING'.
>>> passwords = ["cleanse", "grouped", "gaining", "wasting", "dusters", "letting", "endings", "fertile", "seeking", "certain", "bandits", "stating", "wanting", "parties", "waiting", "station", "maltase", "monster"]
>>> get_next_guess(passwords)
'wanting'
>>> #incorrect, 6/7 correct
>>> passwords = refine(passwords, "wanting", 6); passwords
['wasting', 'waiting']
With two passwords left, get_next_guess
becomes basically a die roll. Just try the first and then the second, and you're done in three tries, no restarts.
(Fun aside. You know the link Colin D shared with the list of 12-character passwords? The writer there picked APPRECIATION
. That's actually the best guess! In that case, it leaves you with no more than two alternatives regardless of what the correct password actually is.)