24_alu.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python3
  2. import sys
  3. from operator import add, mul, floordiv, mod, eq
  4. OPS = {'add': add, 'mul': mul, 'div': floordiv, 'mod': mod, 'eql': eq}
  5. def isoffset(e):
  6. return isinstance(e, Expr) \
  7. and e.opcode == 'add' \
  8. and isinstance(e.right, int) \
  9. and isinstance(e.left, Expr) \
  10. and e.left.opcode == 'inp'
  11. class Expr:
  12. def __init__(self, opcode, left, right):
  13. self.opcode = opcode
  14. self.left = left
  15. self.right = right
  16. def reduce(self, conditions):
  17. lconst = isinstance(self.left, int)
  18. rconst = isinstance(self.right, int)
  19. if lconst and rconst:
  20. return int(OPS[self.opcode](self.left, self.right))
  21. elif lconst and self.opcode in ('add', 'mul', 'eql'):
  22. return Expr(self.opcode, self.right, self.left).reduce(conditions)
  23. if self.opcode == 'add':
  24. if self.right == 0:
  25. return self.left
  26. if rconst and self.left.opcode == 'add' and isinstance(self.left.right, int):
  27. return Expr('add', self.left.left, self.left.right + self.right)
  28. elif self.opcode == 'mul':
  29. if self.right == 0:
  30. return 0
  31. if self.right == 1:
  32. return self.left
  33. elif self.opcode == 'eql':
  34. if rconst:
  35. if self.left.opcode == 'inp' and self.right < 1 or self.right > 9:
  36. return 0
  37. elif self.right.opcode == 'inp':
  38. if self.left.opcode == 'add' \
  39. and isinstance(self.left.right, int) \
  40. and self.left.right > 9:
  41. return 0
  42. else:
  43. assert isoffset(self.left)
  44. index_a = self.left.left.left
  45. offset = self.left.right
  46. index_b = self.right.left
  47. assert index_a < index_b
  48. conditions.append((index_a, index_b, offset))
  49. return 1
  50. elif self.opcode == 'div':
  51. if self.right == 1:
  52. return self.left
  53. if rconst and self.left.opcode == 'add' \
  54. and isoffset(self.left.right) \
  55. and self.left.left.opcode == 'mul' \
  56. and self.left.left.right == self.right:
  57. return self.left.left.left
  58. elif self.opcode == 'mod':
  59. if self.left.opcode == 'add' and self.right == 26:
  60. return self.left if isoffset(self.left) else self.left.right
  61. return self
  62. def parse(line):
  63. parts = line.split()
  64. if len(parts) == 2:
  65. operand = None
  66. elif parts[2].isdigit() or parts[2].startswith('-'):
  67. operand = int(parts[2])
  68. else:
  69. operand = parts[2]
  70. return parts[0], parts[1], operand
  71. def input_conditions(program):
  72. conditions = []
  73. regs = {'w': 0, 'x': 0, 'y': 0, 'z': 0}
  74. index = 0
  75. for opcode, reg, operand in program:
  76. if opcode == 'inp':
  77. regs[reg] = Expr('inp', index, None)
  78. index += 1
  79. else:
  80. if not isinstance(operand, int):
  81. operand = regs[operand]
  82. regs[reg] = Expr(opcode, regs[reg], operand).reduce(conditions)
  83. return conditions
  84. def modelnum(conditions, largest):
  85. nr = [0] * 14
  86. for a, b, offset in conditions:
  87. nr[a] = 9 - max(offset, 0) if largest else 1 - min(offset, 0)
  88. nr[b] = nr[a] + offset
  89. return ''.join(map(str, nr))
  90. conditions = input_conditions(map(parse, sys.stdin))
  91. print(modelnum(conditions, True))
  92. print(modelnum(conditions, False))