Last active
October 2, 2024 07:50
-
-
Save HebeHH/0056784787d26fcc3fd95f97e9e7e55a to your computer and use it in GitHub Desktop.
Solver for The Password Game, specifically the section "The elements in your password must have atomic numbers that add up to 200"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a solver for The Password Game: https://neal.fun/password-game/, | |
# Specifically the section "The elements in your password must have atomic numbers that add up to 200" | |
# It's written as a Flask app because I was sharing it with my friends that were also playing. | |
# However you can just use the function `get_required_elements_for_password_game(password)` | |
# to do it locally | |
# Got ChatGPT to write the element list for me | |
elements = [ | |
{"name": "Hydrogen", "symbol": "H", "atomic_number": 1}, | |
{"name": "Helium", "symbol": "He", "atomic_number": 2}, | |
{"name": "Lithium", "symbol": "Li", "atomic_number": 3}, | |
{"name": "Beryllium", "symbol": "Be", "atomic_number": 4}, | |
{"name": "Boron", "symbol": "B", "atomic_number": 5}, | |
{"name": "Carbon", "symbol": "C", "atomic_number": 6}, | |
{"name": "Nitrogen", "symbol": "N", "atomic_number": 7}, | |
{"name": "Oxygen", "symbol": "O", "atomic_number": 8}, | |
{"name": "Fluorine", "symbol": "F", "atomic_number": 9}, | |
{"name": "Neon", "symbol": "Ne", "atomic_number": 10}, | |
{"name": "Sodium", "symbol": "Na", "atomic_number": 11}, | |
{"name": "Magnesium", "symbol": "Mg", "atomic_number": 12}, | |
{"name": "Aluminum", "symbol": "Al", "atomic_number": 13}, | |
{"name": "Silicon", "symbol": "Si", "atomic_number": 14}, | |
{"name": "Phosphorus", "symbol": "P", "atomic_number": 15}, | |
{"name": "Sulfur", "symbol": "S", "atomic_number": 16}, | |
{"name": "Chlorine", "symbol": "Cl", "atomic_number": 17}, | |
{"name": "Argon", "symbol": "Ar", "atomic_number": 18}, | |
{"name": "Potassium", "symbol": "K", "atomic_number": 19}, | |
{"name": "Calcium", "symbol": "Ca", "atomic_number": 20}, | |
{"name": "Scandium", "symbol": "Sc", "atomic_number": 21}, | |
{"name": "Titanium", "symbol": "Ti", "atomic_number": 22}, | |
{"name": "Vanadium", "symbol": "V", "atomic_number": 23}, | |
{"name": "Chromium", "symbol": "Cr", "atomic_number": 24}, | |
{"name": "Manganese", "symbol": "Mn", "atomic_number": 25}, | |
{"name": "Iron", "symbol": "Fe", "atomic_number": 26}, | |
{"name": "Cobalt", "symbol": "Co", "atomic_number": 27}, | |
{"name": "Nickel", "symbol": "Ni", "atomic_number": 28}, | |
{"name": "Copper", "symbol": "Cu", "atomic_number": 29}, | |
{"name": "Zinc", "symbol": "Zn", "atomic_number": 30}, | |
{"name": "Gallium", "symbol": "Ga", "atomic_number": 31}, | |
{"name": "Germanium", "symbol": "Ge", "atomic_number": 32}, | |
{"name": "Arsenic", "symbol": "As", "atomic_number": 33}, | |
{"name": "Selenium", "symbol": "Se", "atomic_number": 34}, | |
{"name": "Bromine", "symbol": "Br", "atomic_number": 35}, | |
{"name": "Krypton", "symbol": "Kr", "atomic_number": 36}, | |
{"name": "Rubidium", "symbol": "Rb", "atomic_number": 37}, | |
{"name": "Strontium", "symbol": "Sr", "atomic_number": 38}, | |
{"name": "Yttrium", "symbol": "Y", "atomic_number": 39}, | |
{"name": "Zirconium", "symbol": "Zr", "atomic_number": 40}, | |
{"name": "Niobium", "symbol": "Nb", "atomic_number": 41}, | |
{"name": "Molybdenum", "symbol": "Mo", "atomic_number": 42}, | |
{"name": "Technetium", "symbol": "Tc", "atomic_number": 43}, | |
{"name": "Ruthenium", "symbol": "Ru", "atomic_number": 44}, | |
{"name": "Rhodium", "symbol": "Rh", "atomic_number": 45}, | |
{"name": "Palladium", "symbol": "Pd", "atomic_number": 46}, | |
{"name": "Silver", "symbol": "Ag", "atomic_number": 47}, | |
{"name": "Cadmium", "symbol": "Cd", "atomic_number": 48}, | |
{"name": "Indium", "symbol": "In", "atomic_number": 49}, | |
{"name": "Tin", "symbol": "Sn", "atomic_number": 50}, | |
{"name": "Antimony", "symbol": "Sb", "atomic_number": 51}, | |
{"name": "Tellurium", "symbol": "Te", "atomic_number": 52}, | |
{"name": "Iodine", "symbol": "I", "atomic_number": 53}, | |
{"name": "Xenon", "symbol": "Xe", "atomic_number": 54}, | |
{"name": "Cesium", "symbol": "Cs", "atomic_number": 55}, | |
{"name": "Barium", "symbol": "Ba", "atomic_number": 56}, | |
{"name": "Lanthanum", "symbol": "La", "atomic_number": 57}, | |
{"name": "Cerium", "symbol": "Ce", "atomic_number": 58}, | |
{"name": "Praseodymium", "symbol": "Pr", "atomic_number": 59}, | |
{"name": "Neodymium", "symbol": "Nd", "atomic_number": 60}, | |
{"name": "Promethium", "symbol": "Pm", "atomic_number": 61}, | |
{"name": "Samarium", "symbol": "Sm", "atomic_number": 62}, | |
{"name": "Europium", "symbol": "Eu", "atomic_number": 63}, | |
{"name": "Gadolinium", "symbol": "Gd", "atomic_number": 64}, | |
{"name": "Terbium", "symbol": "Tb", "atomic_number": 65}, | |
{"name": "Dysprosium", "symbol": "Dy", "atomic_number": 66}, | |
{"name": "Holmium", "symbol": "Ho", "atomic_number": 67}, | |
{"name": "Erbium", "symbol": "Er", "atomic_number": 68}, | |
{"name": "Thulium", "symbol": "Tm", "atomic_number": 69}, | |
{"name": "Ytterbium", "symbol": "Yb", "atomic_number": 70}, | |
{"name": "Lutetium", "symbol": "Lu", "atomic_number": 71}, | |
{"name": "Hafnium", "symbol": "Hf", "atomic_number": 72}, | |
{"name": "Tantalum", "symbol": "Ta", "atomic_number": 73}, | |
{"name": "Tungsten", "symbol": "W", "atomic_number": 74}, | |
{"name": "Rhenium", "symbol": "Re", "atomic_number": 75}, | |
{"name": "Osmium", "symbol": "Os", "atomic_number": 76}, | |
{"name": "Iridium", "symbol": "Ir", "atomic_number": 77}, | |
{"name": "Platinum", "symbol": "Pt", "atomic_number": 78}, | |
{"name": "Gold", "symbol": "Au", "atomic_number": 79}, | |
{"name": "Mercury", "symbol": "Hg", "atomic_number": 80}, | |
{"name": "Thallium", "symbol": "Tl", "atomic_number": 81}, | |
{"name": "Lead", "symbol": "Pb", "atomic_number": 82}, | |
{"name": "Bismuth", "symbol": "Bi", "atomic_number": 83}, | |
{"name": "Polonium", "symbol": "Po", "atomic_number": 84}, | |
{"name": "Astatine", "symbol": "At", "atomic_number": 85}, | |
{"name": "Radon", "symbol": "Rn", "atomic_number": 86}, | |
{"name": "Francium", "symbol": "Fr", "atomic_number": 87}, | |
{"name": "Radium", "symbol": "Ra", "atomic_number": 88}, | |
{"name": "Actinium", "symbol": "Ac", "atomic_number": 89}, | |
{"name": "Thorium", "symbol": "Th", "atomic_number": 90}, | |
{"name": "Protactinium", "symbol": "Pa", "atomic_number": 91}, | |
{"name": "Uranium", "symbol": "U", "atomic_number": 92}, | |
{"name": "Neptunium", "symbol": "Np", "atomic_number": 93}, | |
{"name": "Plutonium", "symbol": "Pu", "atomic_number": 94}, | |
{"name": "Americium", "symbol": "Am", "atomic_number": 95}, | |
{"name": "Curium", "symbol": "Cm", "atomic_number": 96}, | |
{"name": "Berkelium", "symbol": "Bk", "atomic_number": 97}, | |
{"name": "Californium", "symbol": "Cf", "atomic_number": 98}, | |
{"name": "Einsteinium", "symbol": "Es", "atomic_number": 99}, | |
{"name": "Fermium", "symbol": "Fm", "atomic_number": 100}, | |
{"name": "Mendelevium", "symbol": "Md", "atomic_number": 101}, | |
{"name": "Nobelium", "symbol": "No", "atomic_number": 102}, | |
{"name": "Lawrencium", "symbol": "Lr", "atomic_number": 103}, | |
{"name": "Rutherfordium", "symbol": "Rf", "atomic_number": 104}, | |
{"name": "Dubnium", "symbol": "Db", "atomic_number": 105}, | |
{"name": "Seaborgium", "symbol": "Sg", "atomic_number": 106}, | |
{"name": "Bohrium", "symbol": "Bh", "atomic_number": 107}, | |
{"name": "Hassium", "symbol": "Hs", "atomic_number": 108}, | |
{"name": "Meitnerium", "symbol": "Mt", "atomic_number": 109}, | |
{"name": "Darmstadtium", "symbol": "Ds", "atomic_number": 110}, | |
{"name": "Roentgenium", "symbol": "Rg", "atomic_number": 111}, | |
{"name": "Copernicium", "symbol": "Cn", "atomic_number": 112}, | |
{"name": "Nihonium", "symbol": "Nh", "atomic_number": 113}, | |
{"name": "Flerovium", "symbol": "Fl", "atomic_number": 114}, | |
{"name": "Moscovium", "symbol": "Mc", "atomic_number": 115}, | |
{"name": "Livermorium", "symbol": "Lv", "atomic_number": 116}, | |
{"name": "Tennessine", "symbol": "Ts", "atomic_number": 117}, | |
{"name": "Oganesson", "symbol": "Og", "atomic_number": 118}, | |
] | |
element_symbols = [x['symbol'] for x in elements] | |
elements_by_symbol = {x['symbol']: x['atomic_number']-1 for x in elements} | |
roman_numerals = ['I','V','X','L','C','D','M'] | |
for e in elements: | |
e['symbol_contains_roman_numerals'] = e['symbol'][0] in roman_numerals | |
def get_element_by_symbol(s): | |
return elements[elements_by_symbol[s]] | |
def atomic_sum(list_of_symbols): | |
return sum([get_element_by_symbol(s)['atomic_number'] for s in list_of_symbols]) | |
def stringify_elementlist(symbols): | |
return ', '.join([s + "(atomic number: " + str(get_element_by_symbol(s)['atomic_number']) +")" for s in symbols]) | |
def elements_to_target(required_symbols, target): | |
symbols = required_symbols | |
current_count = atomic_sum(symbols) | |
while current_count < target: | |
diff = target - current_count | |
new_element_idx = diff - 1 | |
while elements[new_element_idx]['symbol_contains_roman_numerals']: | |
new_element_idx -= 1 | |
current_count += elements[new_element_idx]['atomic_number'] | |
symbols.append(elements[new_element_idx]['symbol']) | |
assert(atomic_sum(symbols)) | |
return symbols | |
def find_elements(text): | |
# Sort symbols by length, longest first | |
element_symbols.sort(key=len, reverse=True) | |
result = [] | |
used = [False] * len(text) | |
for i in range(len(text)): | |
# Skip if this char is already part of another symbol | |
if used[i]: | |
continue | |
for symbol in element_symbols: | |
if text.startswith(symbol, i): | |
# Check if any characters have been used in the range | |
if all(not used[j] for j in range(i, i+len(symbol))): | |
result.append(symbol) | |
# Mark these characters as used | |
for j in range(i, i+len(symbol)): | |
used[j] = True | |
break # No need to check shorter symbols | |
return result | |
# Actually get the required elements | |
def get_required_elements_for_password_game(password): | |
required_symbols = find_elements(password) | |
if atomic_sum(required_symbols) == 200: | |
return { | |
"INSTRUCTIONS" : "No need to do anything", | |
"RATIONALE": "The atomic numbers in your password already add up to 200!", | |
} | |
if atomic_sum(required_symbols) > 200: | |
return { | |
"INSTRUCTIONS" : "Delete everything.", | |
"RATIONALE": "The atomic numbers already in your password are: "+ stringify_elementlist(required_symbols)+ ". These add up to " + str(atomic_sum(required_symbols)) + ", which is over 200! We can't do anything until you bring that down :(", | |
} | |
symbol_list = elements_to_target(required_symbols.copy(), 200) # there's probably a better way to do this than list.copy() but eh I'm in a rush | |
print(required_symbols) | |
print(symbol_list) | |
new_symbols = symbol_list.copy() | |
for x in required_symbols: | |
new_symbols.remove(x) | |
# This could be nicer but oh well I wrote it quick just to help a friend understand | |
instructions = "Add to your password the text: " + ''.join(new_symbols) | |
rationale = "Currently, your password includes the elements: " + stringify_elementlist(required_symbols)+ ". " | |
rationale += "These sum to " + str(atomic_sum(required_symbols)) + ". " | |
rationale += "In order to equal a sum of 200, you need to add "+ str(200 - atomic_sum(required_symbols)) + ". " | |
rationale += "In order to do this, you are adding the elements: " +stringify_elementlist(new_symbols) + ", " | |
rationale += "which sum to " + str(atomic_sum(new_symbols)) + "." | |
return { | |
"INSTRUCTIONS" : instructions, | |
"RATIONALE": rationale, | |
"full_list":symbol_list, | |
"existing_elements": required_symbols, | |
"elements_to_add" : new_symbols | |
} | |
# ChatGPT wrote the Flask code for me because who can be bothered | |
from flask import Flask, request, jsonify | |
from urllib.parse import unquote | |
app = Flask(__name__) | |
@app.route('/process', methods=['POST']) | |
def process(): | |
data = request.json # Expecting a JSON payload | |
if 'password' not in data: | |
return jsonify({"error": "Missing 'password' in payload"}), 400 | |
processed = get_required_elements_for_password_game(data['password']) | |
return jsonify({"result": processed}) | |
@app.route('/process', methods=['GET']) | |
def process_get(): | |
text_param = request.args.get('password', None) | |
if text_param is None: | |
return jsonify({"error": "Missing 'password' parameter"}), 400 | |
# Decode the URL-encoded text | |
text = unquote(text_param) | |
processed = get_required_elements_for_password_game(text) | |
return jsonify({"result": processed}) | |
if __name__ == '__main__': | |
app.run(host='0.0.0.0', port=5002, debug=True) |
If you're not familiar with the password game, it's quite fun: https://neal.fun/password-game/
I hosted it here by creating it into static website
https://password-game-element-solver.xeonzolt.com/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Currently accessible at: https://15e5-121-7-185-69.ngrok-free.app/process
Go to: https://15e5-121-7-185-69.ngrok-free.app/process?password=%3CIndonesiaceKlJunecaptcha57KiP%3E
Or run:
curl -X POST -H "Content-Type: application/json" -d '{"password":"IndonesiaceKlJunecaptcha57KiPlAg"}' [https://15e5-121-7-185-69.ngrok-free.app/process](https://15e5-121-7-185-69.ngrok-free.app/process)
I make no promises about how emojis are handled.