You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.7 KiB
3.7 KiB
Solution to p12
This is the problem about fencing
Load the file into a list of lines
(require 'dash)
(with-temp-buffer
(insert-file-contents "input")
(goto-char (point-min))
(replace-regexp "^" "\"")
(goto-char (point-min))
(replace-regexp "$" "\"")
(goto-char (point-min))
(insert "(setq data '(")
(goto-char (point-max))
(insert "))")
(eval-buffer))
and split into a list of list of chars
(setq data-chars (-map (lambda (str) (--map (string-to-char it) (split-string str "\\|.+" t)))
data)
height (length data-chars)
width (length (car data-chars)))
(defun acceptable-p (p)
(let ((x (car p))
(y (cadr p)))
(and (>= x 0) (>= y 0) (< x width) (< y height)) ))
(defun neighbours (p)
(let ((x (car p))
(y (cadr p)))
(-map (lambda (q) (list (+ x (car q)) (+ y (cadr q)))) '((+1 0) (-1 0) (0 1) (0 -1)))))
(defun acceptable-neighbours (p)
(-filter #'acceptable-p (neighbours p)))
(defun plant (p)
(if (not (acceptable-p p)) ?.
(nth (car p) (nth (cadr p) data-chars))))
Collect all plants type and return a list of list of positions
(setq plants (-distinct (-mapcat #'identity data-chars)))
(setq groups (-map (lambda (pl)
(-map #'cadr
(--filter (eq (car it) pl)
(-mapcat #'identity
(-map-indexed (lambda (y l) (-map-indexed (lambda (x el) (list el (list x y))) l)) data-chars)))))
plants))
Now we need to split each group into contiguous regions
(defun agglomerate (acc el)
(let ((nbhs (acceptable-neighbours el)))
(cons (-mapcat #'identity (cons (list el) (--filter (-intersection it nbhs) acc)))
(--filter (not (-intersection it nbhs)) acc))))
(setq regions (--mapcat (-reduce-from #'agglomerate nil it) groups))
We define auxiliary functions to get to the solution
(defun count-fence (el)
"This counts the number of sides of the fence around the element EL"
(let ((i (plant el))
(pos el))
(length (--filter (not (eq i it)) (-map #'plant (neighbours pos))))))
(defun vertex-p (el dir)
"This checks if there is a vertex in the direction DIR at the
element EL. We need to avoid double-counting, so we return t
only under appropriate circumstances (for instance on a concave
angle, we only count the vertex attached to the element that
shares only a vertex with the other region"
(let* ((x (car el))
(y (cadr el))
(a (car dir))
(b (cadr dir))
(opposite (list (+ x a) (+ y b)))
(adjh (list (+ x a) y))
(adjv (list x (+ y b))))
(or (and (not (eq (plant el) (plant adjh)))
(not (eq (plant el) (plant adjv))))
(and (eq (plant el) (plant adjh))
(eq (plant el) (plant adjv))
(not (eq (plant el) (plant opposite)))))))
(defun count-vertices (el)
(length (-non-nil (--map (vertex-p el it) '((1 1) (1 -1) (-1 1) (-1 -1))))))
The solution of part 1 is
(-reduce #'+ (--map (* (car it) (cdr it))
(-zip-pair (-map #'length regions)
(-map (lambda (region) (-reduce #'+ (-map #'count-fence region))) regions))))
The solution of part 2 is
(-reduce #'+ (--map (* (car it) (cdr it))
(-zip-pair (-map #'length regions)
(-map (lambda (region) (-reduce #'+ (-map #'count-vertices region))) regions))))
859494