Guest User

Untitled

a guest
Dec 8th, 2015
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.36 KB | None | 0 0
  1. #coordinates are in region 4 - (0,0) is in the upper left corner, and all positive coords move southeast from there
  2.  
  3. class Bbox:
  4. def __init__(self, left, top, right, bottom):
  5. self.left = left
  6. self.top = top
  7. self.right = right
  8. self.bottom = bottom
  9. @property
  10. def width(self):
  11. return 1 + self.right - self.left
  12. @property
  13. def height(self):
  14. return 1 + self.bottom - self.top
  15.  
  16. @property
  17. def nw(self):
  18. return self.left, self.top
  19. @property
  20. def se(self):
  21. return self.right, self.bottom
  22.  
  23. @property
  24. def area(self):
  25. return self.width * self.height
  26.  
  27. def quadrisect(self):
  28. mid_x = (self.left + self.right)/2
  29. mid_y = (self.top + self.bottom)/2
  30. x_coords = (self.left, mid_x, self.right)
  31. y_coords = (self.top, mid_y, self.bottom)
  32. boxes = []
  33. for x in ((self.left, mid_x), (mid_x+1, self.right)):
  34. for y in ((self.top, mid_y), (mid_y+1, self.bottom)):
  35. box = Bbox(x[0], y[0], x[1], y[1])
  36. if box:
  37. boxes.append(box)
  38. return boxes
  39.  
  40. def contains(self, other):
  41. if isinstance(other, tuple):
  42. x,y = other
  43. return self.left <= x <= self.right and self.top <= y <= self.bottom
  44. else:
  45. return all(self.contains(p) for p in other.iter_corners())
  46.  
  47. def is_real(self):
  48. return self.left <= self.right and self.top <= self.bottom
  49.  
  50. def intersect(self, other):
  51. box = Bbox(
  52. max(self.left, other.left),
  53. max(self.top, other.top),
  54. min(self.right, other.right),
  55. min(self.bottom, other.bottom)
  56. )
  57. if not box:
  58. return None
  59. return box
  60. def iter_corners(self):
  61. for x in (self.left, self.right):
  62. for y in (self.top, self.bottom):
  63. yield (x,y)
  64. def iter_lattice_points(self):
  65. for i in range(self.left, self.right+1):
  66. for j in range(self.top, self.bottom+1):
  67. yield (i,j)
  68. def tuple(self):
  69. return (self.left, self.top, self.right, self.bottom)
  70. def __nonzero__(self):
  71. #wait, this doesn't work because bboxes always have nonzero area.
  72. #return self.right-self.left > 0 and self.bottom - self.top > 0
  73. return self.is_real()
  74. def __eq__(self, other):
  75. return self.left == other.left and self.right == other.right and self.top == other.top and self.bottom == other.bottom
  76. def __repr__(self):
  77. return "Bbox({}, {}, {}, {})".format(self.left, self.top, self.right, self.bottom)
  78.  
  79. class QuadRegion:
  80. def __init__(self, bbox, value=None):
  81. self.bbox = bbox
  82. self.value = value
  83. self.children = None
  84. def split(self):
  85. assert self.is_leaf_node()
  86. self.children = [QuadRegion(bbox, self.value) for bbox in self.bbox.quadrisect()]
  87.  
  88. def is_leaf_node(self):
  89. return self.children is None
  90.  
  91. #check if all children have the same value, and if so delete them and become a leaf node with that value.
  92. #returns True if any changes occurred, False otherwise.
  93. def maybe_coalesce(self):
  94. if self.is_leaf_node():
  95. return False
  96. for child in self.children:
  97. child.maybe_coalesce()
  98. if not child.is_leaf_node():
  99. return False
  100. if all(child.value == self.children[0].value for child in self.children):
  101. self.value = self.children[0].value
  102. self.children = None
  103.  
  104. def apply(self, bbox, func, depth=0):
  105. assert self.bbox.contains(bbox), "{} does not contain {}".format(self, bbox)
  106. assert depth < 50, "exceeded maximum depth for {}, {}".format(self, bbox)
  107. if not self.is_leaf_node():
  108. for child in self.children:
  109. child_bbox = child.bbox.intersect(bbox)
  110. if child_bbox:
  111. child.apply(child_bbox, func, depth+1)
  112. else:
  113. if self.bbox == bbox:
  114. self.value = func(self.value)
  115. else:
  116. self.split()
  117. self.apply(bbox, func, depth+1)
  118. self.maybe_coalesce()
  119.  
  120. def iter_leaf_nodes(self):
  121. if self.is_leaf_node():
  122. yield self
  123. else:
  124. for child in self.children:
  125. for leaf in child.iter_leaf_nodes():
  126. yield leaf
  127.  
  128. def __repr__(self):
  129. if not self.is_leaf_node():
  130. return "QuadRegion({}, {}, {})".format(self.bbox, self.value, self.children)
  131. else:
  132. return "QuadRegion({}, {})".format(self.bbox, self.value)
  133.  
  134. from PIL import Image, ImageDraw
  135.  
  136. def fancy_render(region):
  137. x_offset = region.bbox.left
  138. y_offset = region.bbox.top
  139. to_screen_coord = lambda i,j: (1+(i - x_offset)*2, 1+(j - y_offset)*2)
  140. img = Image.new("RGB", (2*region.bbox.width+1, 2*region.bbox.height+1), "gray")
  141. draw = ImageDraw.Draw(img)
  142. pix = img.load()
  143. def _render(region):
  144. if region.children:
  145. for child in region.children:
  146. _render(child)
  147. else:
  148. color = (255,255,255) if region.value else (0,0,0)
  149. a,b = to_screen_coord(*region.bbox.nw)
  150. c,d = to_screen_coord(*region.bbox.se)
  151. draw.rectangle((a-1,b-1,c+1,d+1), outline="red", fill=color)
  152. _render(region)
  153. return img
  154.  
  155. import re
  156. from collections import defaultdict
  157. import time
  158.  
  159. instructions = []
  160. with open("p6_input.txt") as file:
  161. for line in file:
  162. m = re.match(r"(toggle|turn on|turn off) (\d*?),(\d*?) through (\d*?),(\d*?)$", line)
  163. rule = [m.group(1)] + [int(m.group(x)) for x in range(2, 6)]
  164. instructions.append(rule)
  165.  
  166. t = time.time()
  167. r = QuadRegion(Bbox(0,0,999,999), False)
  168. funcs = {"turn on": lambda v: True, "turn off": lambda v: False, "toggle": lambda v: not v}
  169. for idx, (rule, left, top, right, bottom) in enumerate(instructions):
  170. print "{} / {}\r".format(idx, len(instructions)),
  171. assert rule in funcs
  172. r.apply(Bbox(left, top, right, bottom), funcs[rule])
  173.  
  174. total = 0
  175. for region in r.iter_leaf_nodes():
  176. if region.value:
  177. total += region.bbox.area
  178.  
  179. print "\n", total
  180. print "finished in {} seconds".format(time.time() - t)
  181.  
  182. fancy_render(r).save("output.png")
Advertisement
Add Comment
Please, Sign In to add comment