|
# 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) |