Advertisement
brainwagon

[ 2024 Day 17 Part 2 ] Genetic Algorithm

Dec 17th, 2024
784
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.13 KB | Source Code | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import re
  4. import random
  5.  
  6. data = """Register A: 32916674
  7. Register B: 0
  8. Register C: 0
  9.  
  10. Program: 2,4,1,1,7,5,0,3,1,4,4,0,5,5,3,0"""
  11.  
  12. #data = """Register A: 2024
  13. #Register B: 0
  14. #Register C: 0
  15. #
  16. #Program: 0,3,5,4,3,0"""
  17.  
  18. init, prog = data.split("\n\n")
  19.  
  20. A, B, C = 0, 0, 0
  21.  
  22. lines = init.split("\n")
  23. m = re.match(r"Register A: (\d+)", lines[0].rstrip())
  24. A = int(m.group(1))
  25. m = re.match(r"Register B: (\d+)", lines[1].rstrip())
  26. B = int(m.group(1))
  27. m = re.match(r"Register C: (\d+)", lines[2].rstrip())
  28. C = int(m.group(1))
  29.  
  30. prog = prog.split(" ")[1]
  31. prog_str = prog
  32. prog = prog.split(",")
  33. prog = [ int(x) for x in prog ]
  34.  
  35. ninst = len(prog)
  36.  
  37. print(f"prog has {ninst} instructions")
  38.  
  39. def disasm(prog):
  40. pc = 0
  41. ninst = len(prog)
  42. first = True
  43.  
  44. print(prog)
  45.  
  46. f = open("dis.asm", "w")
  47.  
  48. for pc in range(0, ninst, 2):
  49. inst = prog[pc]
  50. oper = prog[pc+1]
  51.  
  52. if inst == 0:
  53. print(f"ADV {oper}", file=f)
  54. elif inst == 1:
  55. print(f"BXL {oper}", file=f)
  56. elif inst == 2:
  57. print(f"BST {oper}", file=f)
  58. elif inst == 3:
  59. print(f"JNZ {oper}", file=f)
  60. elif inst == 4:
  61. print("BXC ---", file=f)
  62. elif inst == 5:
  63. print(f"OUT {oper}", file=f)
  64. elif inst == 6:
  65. print(f"BDIV {oper}", file=f)
  66. elif inst == 7:
  67. print(f"CDIV {oper}", file=f)
  68. else:
  69. print(f"odd, we shouldn't have instruction {inst}")
  70. print(f"PC={pc}")
  71. raise IndexError
  72. f.close()
  73.  
  74.  
  75. def combo(A, B, C, oper):
  76. if oper == 0:
  77. return oper
  78. elif oper == 1:
  79. return oper
  80. elif oper == 2:
  81. return oper
  82. elif oper == 3:
  83. return oper
  84. elif oper == 4:
  85. return A
  86. elif oper == 5:
  87. return B
  88. elif oper == 6:
  89. return C
  90. elif oper == 7:
  91. print("7 isn't a valid operand for a combo instruction")
  92. raise IndexError
  93. else:
  94. print(f"{oper} isn't a valid operand.")
  95. raise IndexError
  96.  
  97. f = open("trace.txt", "w")
  98.  
  99. def lprint(x):
  100. print(x, file=f)
  101.  
  102. def runprog(A, B, C, prog):
  103. pc = 0
  104. ninst = len(prog)
  105. first = True
  106.  
  107. output = ""
  108.  
  109.  
  110. while pc < ninst:
  111. inst = prog[pc]
  112. oper = prog[pc+1]
  113. pc += 2
  114.  
  115. lprint(f"PC: {pc} running inst {inst}")
  116. if inst == 0:
  117. # adv instruction
  118. den = pow(2, combo(A, B, C, oper))
  119. lprint(f"ADV {den}")
  120. A = int(A / den)
  121. elif inst == 1:
  122. # bxl instruction
  123. B = B ^ oper
  124. lprint(f"BXL {oper}")
  125. elif inst == 2:
  126. # bst instructions
  127. val = combo(A, B, C, oper) & 7
  128. lprint(f"BXL {val}")
  129. B = val
  130. elif inst == 3:
  131. # jnz instruction
  132. if A != 0:
  133. lprint(f"JNZ {A}")
  134. pc = oper
  135. elif inst == 4:
  136. lprint("BXC")
  137. B = B ^ C
  138. elif inst == 5:
  139. # out instruction
  140. # lprint(f"OUT {combo(oper)%8}")
  141. if not first:
  142. output += ","
  143. first = False
  144. output += str(combo(A, B, C, oper)%8)
  145. elif inst == 6:
  146. # bdiv instruction
  147. den = pow(2, combo(A, B, C, oper))
  148. lprint(f"BDIV {den}")
  149. B = int(A / den)
  150. elif inst == 7:
  151. # cdiv instruction
  152. den = pow(2, combo(A, B, C, oper))
  153. lprint(f"CDIV {den}")
  154. C = int(A / den)
  155. else:
  156. print(f"odd, we shouldn't have instruction {inst}")
  157. print(f"PC={pc}")
  158. raise IndexError
  159. return output
  160.  
  161. disasm(prog)
  162.  
  163. def ioc(s0, s1):
  164. if len(s0) != len(s1):
  165. raise IndexError
  166. cnt = 0
  167. for a, b in zip(s0, s1):
  168. if a == "," or b == ",":
  169. continue
  170. if a == b:
  171. cnt += 1
  172. return cnt
  173.  
  174. from termcolor import colored
  175.  
  176. def cprint(matches, x):
  177. global prog_str
  178. print(f"{matches}: ", end="")
  179. for c0, c1 in zip(x, prog_str):
  180. if c0 == ',':
  181. print(",", end="")
  182. elif c0 == c1:
  183. print(colored(c0, "green"), end="")
  184. else:
  185. print(c0, end="")
  186. print()
  187.  
  188. lo = 2**45
  189. hi = 2**48-1
  190.  
  191. poolsize = 10000
  192. pool = []
  193.  
  194. def computescore(out):
  195. global prog_str
  196. if len(prog_str) != len(out):
  197. return 100
  198. else:
  199. return 16-ioc(prog_str, out)
  200.  
  201. for _ in range(poolsize):
  202. A = random.randint(lo, hi)
  203. B = 0
  204. C = 0
  205. out = runprog(A, B, C, prog)
  206. score = computescore(out)
  207. pool.append([score, A, out])
  208.  
  209. pool.sort()
  210.  
  211. gen = 0
  212.  
  213. def mutate(a):
  214. return a ^ (1 << random.randint(0, 47))
  215.  
  216. def crossover(a, b):
  217. ba = bin(a)
  218. bb = bin(b)
  219. if len(ba) != len(bb):
  220. return a
  221. else:
  222. s = random.randint(0, len(ba)-1)
  223. r = ba[:s] + bb[s:]
  224. if len(r) != len(ba):
  225. raise IndexError
  226. return int(r, 2)
  227.  
  228.  
  229.  
  230. while True:
  231.  
  232. gen += 1
  233. print(f"Generation {gen}")
  234.  
  235. acheck = set()
  236. for s, a, o in pool:
  237. acheck.add(a)
  238.  
  239. for i in range(poolsize//2, poolsize):
  240. if random.random() < 0.02:
  241. s, a, o = pool[i]
  242. a = mutate(a)
  243. while a in acheck:
  244. a = mutate(a)
  245. acheck.add(a)
  246. o = runprog(a, 0, 0, prog)
  247. s = computescore(o)
  248. pool[i] = [s, a, o]
  249. else:
  250. # crossover
  251. p0 = random.randint(0, poolsize//2-1)
  252. p1 = random.randint(0, poolsize//2-1)
  253. a = crossover(pool[p0][1], pool[p1][1])
  254. while a in acheck:
  255. p0 = random.randint(0, poolsize//2-1)
  256. p1 = random.randint(0, poolsize//2-1)
  257. a = crossover(pool[p0][1], pool[p1][1])
  258. acheck.add(a)
  259. o = runprog(a, 0, 0, prog)
  260. s = computescore(o)
  261. pool[i] = [s, a, o]
  262. pool.sort()
  263. for i in range(5):
  264. print(i, pool[i])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement