I really love the Fallout series. In the more recent Bethesda-era installments of the game, completing a “hacking” mini-game is required to gain access to restricted terminals. It’s not a huge problem, but I can only play it so many times before it gets tiresome. In Fallout 3 and Fallout: New Vegas, you can even be permanently locked out of the terminal if you fail.
For those unaware, the game works like so:
- You are presented with a screen similar to the screenshot above.
- Among the symbols and memory addresses are random unrelated passwords (some of the symbols — those between open and closed brackets — can be used to either refill your available attempts or remove ‘dud’ words from the screen).
- When a password is chosen, if it is correct, the user gains access to the terminal.
- If it is not correct, the user loses one attempt (represented by the green squares near the top).
- When an incorrect password is chosen, the right side of the screen displays feedback explaining how many characters in the chosen word match the actual password and are in the correct position.
- If you use up all your attempts, mini-game is over.
Of course there are mods to make hacking trivial, but I thought it would be fun to write my own Python code to solve the mini-game itself (or at least do most of the hard work).
First, I needed a way to group all of the possible choices into one list. Retrieving the words automatically would pose a problem that requires much more complicated code than I wanted to write, so I decided to prompt the user to type each word:
def get_entered_words():
"""Prompt user to enter words from the hacking terminal"""
words = []
print('Enter listed words (empty string to finish):')
while True:
word = input()
if word == '':
break
words.append(word.upper())
return words
Now upon running the program, the user is prompted to begin entering each word. Once the user enters an empty string, the loop breaks and the full list of words is returned.
Next, I needed to figure out how to narrow down the list so it only includes possibly correct answers, filtering certain words out if they are invalidated by the mini-game’s feedback. I thought about using enumerate()
to do this, but I couldn’t quite make it work. After some searching I found this great answer on Stack Overflow and was reminded of the usefulness of the zip()
function:
match_num = sum(c1 == c2 for c1, c2 in zip(word, used_word)
In short, this line uses a generator expression which first creates a list of tuples (via zip()
) with each tuple containing the corresponding characters from each word (used_word
being the word chosen in-game and word
being each item in the user-supplied list).
The above is a little difficult to explain, but here is an example of what is generates:
word = 'GREED'
used_word = 'BREAD'((G, B), (R, R), (E, E), (E, A), (D, D))
Next the generator iterates through each tuple and checks if the given characters are the same with c1 == c2
. If they are equal, the output is True
, otherwise False
is returned. In Python these two values have an implicit value of 1 and 0, respectively. Finally sum()
is called on the sequence of ones and zeroes, which represents the number of correct character matches.
Now that the hard part is done, we simply compare the match_num
of the current word
with the word the user chose and remove it from the list if it does not match (if the number of correct characters doesn’t match, then it’s the wrong word).
Now, this method will eventually whittle down the list to the correct answer, but unfortunately it might take longer than four attempts. In my experience it will lead to the correct answer within four attempts somewhere around ~80% of the time. This problem can be mitigated by using some of the extra “symbol” choices around the screen, but if the list still isn’t narrowed down enough at that point, well, that’s what quicksaves are for!
Thanks for reading!