Advertisement
Guest User

Untitled

a guest
Jul 28th, 2024
404
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.36 KB | Source Code | 0 0
  1. from multiprocessing import Pool, Queue
  2. import random
  3.  
  4. # Gender globals
  5. GENDER_MALE = 0
  6. GENDER_FEMALE = 1
  7.  
  8. # Ability globals
  9. ABILITY_ONE = 0
  10. ABILITY_TWO = 1
  11.  
  12. # Wurmple globals
  13. WURMPLE_EVO_SILCOON = 0
  14. WURMPLE_EVO_CASCOON = 1
  15.  
  16.  
  17. def brute_force_pid(gender=None, ability=None, nature=None, is_shiny=False, tid=None, sid=None, unown_letter=None,
  18.                     wurmple_evo=None):
  19.     # Domain checks
  20.     if gender is not None and gender is not GENDER_MALE and gender is not GENDER_FEMALE:
  21.         print(f"ERROR: Supplied gender restriction <{gender}> is invalid")
  22.         return None
  23.     if ability is not None and ability is not ABILITY_ONE and ability is not ABILITY_TWO:
  24.         print(f"ERROR: Supplied ability restriction <{ability}> is invalid")
  25.         return None
  26.     if nature is not None and not 0 <= nature <= 24:
  27.         print(f"ERROR: Supplied nature restriction <{nature}> is invalid")
  28.         return None
  29.     if tid is not None and not 0 <= tid <= 0xFFFF:
  30.         print(f"ERROR: Supplied Trainer ID <{tid}> is invalid")
  31.         return None
  32.     if sid is not None and not 0 <= sid <= 0xFFFF:
  33.         print(f"ERROR: Supplied Secret ID <{sid}> is invalid")
  34.         return None
  35.     if unown_letter is not None and unown_letter not in "abcdefghijklmnopqrstuvwxyz!?":
  36.         print(f"ERROR: Supplied Unown letter restriction <{unown_letter}> is invalid")
  37.         return None
  38.     if wurmple_evo is not None and wurmple_evo is not WURMPLE_EVO_SILCOON and wurmple_evo is not WURMPLE_EVO_CASCOON:
  39.         print(f"ERROR: Supplied Wurmple evolution restriction <{wurmple_evo}> is invalid")
  40.         return None
  41.  
  42.     # Sanity checks
  43.     if is_shiny and (tid is None or sid is None):
  44.         print("ERROR: Must supply a TID/SID to compute a shiny PID")
  45.         return None
  46.  
  47.     if unown_letter is not None and gender is not None:
  48.         print("WARN: Unown is genderless, setting a gender restriction for Unown can cause unnecessarily high compute "
  49.               "times")
  50.     if unown_letter is not None and ability is not None:
  51.         print("ERROR: Unown does not have multiple abilities, and Unown letter restrictions are incompatible with "
  52.               "Ability restrictions")
  53.         return None
  54.  
  55.     if unown_letter is not None and wurmple_evo is not None:
  56.         print("ERROR: Cannot restrict by both Unown letter and Wurmple evolution")
  57.  
  58.     if unown_letter is not None:
  59.         unown_letter = ord(unown_letter)
  60.         if ord("a") <= unown_letter <= ord("z"):
  61.             unown_letter = unown_letter - 97
  62.         elif unown_letter == ord("!"):
  63.             unown_letter = 26
  64.         elif unown_letter == ord("?"):
  65.             unown_letter = 27
  66.  
  67.     result = None
  68.     with Pool() as p:
  69.         num_workers = p._processes
  70.  
  71.         print(f"Spawning {num_workers} workers")
  72.         await_result = Queue(maxsize=num_workers)
  73.         for worker_id in range(0, num_workers):
  74.             p.apply_async(
  75.                 generate_and_test_pids,
  76.                 (worker_id, num_workers, gender, ability, nature, is_shiny, tid, sid, unown_letter, wurmple_evo),
  77.                 callback=lambda pid: await_result.put(pid))
  78.  
  79.         p.close()
  80.  
  81.         for i in range(0, num_workers):
  82.             result = await_result.get()
  83.             if result is not None:
  84.                 break
  85.  
  86.         p.terminate()
  87.         p.join()
  88.  
  89.     return result
  90.  
  91.  
  92. def generate_and_test_pids(worker_id, num_workers, gender, ability, nature, is_shiny, tid, sid, unown_letter,
  93.                            wurmple_evo):
  94.     start = int(worker_id * (0xFFFFFFFF + 1) / num_workers)
  95.     stop = int((worker_id + 1) * (0xFFFFFFFF + 1) / num_workers)
  96.  
  97.     pid = start
  98.     tried_msg = 1
  99.     tried_msg_threshold = start + (stop - start) // 100
  100.     while pid < stop:
  101.         if pid > tried_msg_threshold:
  102.             print(f"Tried {tried_msg}% of all PIDs for worker #{worker_id}")
  103.             tried_msg += 1
  104.             tried_msg_threshold = start + int((stop - start) / 100 * tried_msg)
  105.  
  106.         if gender == GENDER_FEMALE and not pid & 0xFF < 31:
  107.             pid += 0x100 - pid & 0xFF
  108.             continue
  109.         elif gender == GENDER_MALE and not pid & 0xFF >= 191:
  110.             pid += 191 - pid & 0xFF
  111.             continue
  112.  
  113.         if ability == ABILITY_ONE and not pid & 1 == 0:
  114.             pid += 1
  115.             continue
  116.         elif ability == ABILITY_TWO and not pid & 1 == 1:
  117.             pid += 1
  118.             continue
  119.  
  120.         if nature is not None and not pid % 25 == nature:
  121.             pid += (25 + nature - pid % 25) % 25
  122.             continue
  123.  
  124.         if is_shiny and not (pid >> 16 & 0xFFFF) ^ (pid & 0xFFFF) ^ tid ^ sid < 8:
  125.             pid += 1
  126.             continue
  127.  
  128.         if unown_letter is not None and not \
  129.                 (pid >> 18 & 0xC0
  130.                  | pid >> 12 & 0x30
  131.                  | pid >> 6 & 0x0C
  132.                  | pid & 0x03) % 28 == unown_letter:
  133.             pid += 1
  134.             continue
  135.  
  136.         if wurmple_evo == WURMPLE_EVO_SILCOON and not (pid >> 16) % 10 <= 4:
  137.             pid += 1
  138.             continue
  139.         elif wurmple_evo == WURMPLE_EVO_CASCOON and not (pid >> 16) % 10 > 4:
  140.             pid += 1
  141.             continue
  142.  
  143.         return pid
  144.  
  145.     return None
  146.  
  147.  
  148. def get_str_input(prompt, valid_responses=None):
  149.     while True:
  150.         response = input(prompt)
  151.         if response and (valid_responses is None or response in valid_responses):
  152.             return response
  153.         print(f'"{response}" is an invalid response')
  154.  
  155.  
  156. def get_int_input(prompt, min_val=None, max_val=None):
  157.     while True:
  158.         response = input(prompt)
  159.         if response.isdigit():
  160.             response = int(response)
  161.             if min_val is None and max_val is None:
  162.                 return response
  163.             if min_val is None and response <= max_val:
  164.                 return response
  165.             if max_val is None and response >= min_val:
  166.                 return response
  167.             if min_val <= response <= max_val:
  168.                 return response
  169.         print(f'"{response}" is an invalid response')
  170.  
  171.  
  172. def main():
  173.     print("Generation 3 Personality Value (PID) Generator")
  174.     print("##############################################")
  175.     print()
  176.    
  177.     # Gender
  178.     should_gender = get_str_input('Restrict by gender? ("y" or "n"): ', "yn")
  179.     if should_gender == "y":
  180.         gender = get_int_input(f'Enter the desired Gender (\"{GENDER_MALE}\" for Male, \"{GENDER_FEMALE}\" for '
  181.                                f'Female): ', GENDER_MALE, GENDER_FEMALE)
  182.     else:  # should_gender == "n":
  183.         gender = None
  184.    
  185.     # Ability
  186.     should_ability = get_str_input('Restrict by ability? ("y" or "n"): ', "yn")
  187.     if should_ability == "y":
  188.         ability = get_int_input(f'Enter the desired ability (\"{ABILITY_ONE}\" for first ability, '
  189.                                 f'\"{ABILITY_TWO}\" for second ability): ', ABILITY_ONE, ABILITY_TWO)
  190.     else:  # should_ability == "n":
  191.         ability = None
  192.    
  193.     # Nature
  194.     should_nature = get_str_input('Restrict by nature? ("y", or "n"): ', "yn")
  195.     if should_nature == "y":
  196.         print("Natures:\n"
  197.               "        |-ATK       |-DEF       |-SPD        |-SpA        |-SpD\n"
  198.               "    +ATK|Hardy  (0) |Lonely (1) |Brave   (2) |Adamant (3) |Naughty (4)\n"
  199.               "    +DEF|Bold   (5) |Docile (6) |Relaxed (7) |Impish  (8) |Lax     (9)\n"
  200.               "    +SPD|Timid  (10)|Hasty  (11)|Serious (12)|Jolly   (13)|Naive   (14)\n"
  201.               "    +SpA|Modest (15)|Mild   (16)|Quiet   (17)|Bashful (18)|Rash    (19)\n"
  202.               "    +SpD|Calm   (20)|Gentle (21)|Sassy   (22)|Careful (23)|Quirky  (24)")
  203.         nature = get_int_input(f'Enter the desired nature by its number: ', 0, 24)
  204.     else:  # should_nature == "n":
  205.         nature = random.randint(0, 24)  # because if you just skip everything you may well just always get the first hit
  206.  
  207.     # Shiny
  208.     should_shiny = get_str_input('Restrict by shiny? ("y" or "n"): ', "yn")
  209.     if should_shiny == "y":
  210.         shiny = True
  211.         tid = get_int_input(f'Enter your Trainer ID (0 to {0xFFFF}): ', 0, 0xFFFF)
  212.         sid = get_int_input(f'Enter your Secret ID (0 to {0xFFFF}): ', 0, 0xFFFF)
  213.     else:  # should_shiny == "n":
  214.         shiny = False
  215.         tid = None
  216.         sid = None
  217.  
  218.     # Unown letter
  219.     should_unown = get_str_input('Restrict by Unown letter? ("y" or "n"): ', "yn")
  220.     if should_unown == "y":
  221.         unown_letter = get_str_input(f'Enter the desired Unown letter (\"a\" to \"z\", \"!\", or \"?\"): ',
  222.                                      "abcdefghijklmnopqrstuvwxyz!?")
  223.     else:  # should_unown == "n":
  224.         unown_letter = None
  225.  
  226.     # Wurmple evolution
  227.     should_wurmple = get_str_input('Restrict by Wurmple evolution? ("y" or "n"): ', "yn")
  228.     if should_wurmple == "y":
  229.         wurmple_evo = get_int_input(f'Enter the desired Wurmple evolution (\"{WURMPLE_EVO_SILCOON}\" for '
  230.                                     f'Silcoon, \"{WURMPLE_EVO_CASCOON}\" for Cascoon): ',
  231.                                     WURMPLE_EVO_SILCOON, WURMPLE_EVO_CASCOON)
  232.     else:  # should_wurmple == "n":
  233.         wurmple_evo = None
  234.  
  235.     pid = brute_force_pid(gender, ability, nature, shiny, tid, sid, unown_letter, wurmple_evo)
  236.     if pid is not None:
  237.         print(f"Found matching PID: 0x{pid:08X}")
  238.     else:
  239.         print("Failed to find a matching PID")
  240.  
  241.  
  242. if __name__ == "__main__":
  243.     main()
  244.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement