(ql:quickload 'cl-ppcre)
(defun read-data (path)
(with-open-file (stream path)
(loop for line = (read-line stream nil)
while line collect line)))
(defparameter *input-source*
(cadr *posix-argv*))
;; Useful in testing
;; (defparameter *input-source* "example_input")
;; (defparameter *input-source* "input")
(defstruct line start end)
(defstruct point x y)
(defun parse-line (row)
(cl-ppcre:register-groups-bind ((#'parse-integer x1 y1 x2 y2))
("(\\d+),(\\d+) -> (\\d+),(\\d+)" row :sharedp t)
(make-line :start (make-point :x x1 :y y1)
:end (make-point :x x2 :y y2))))
(defun parse-input (input)
(map 'list #'parse-line input))
(defparameter *data* (parse-input (read-data *input-source*)))
(defun direction-diff (x1 x2)
(cond ((> x2 x1) 1)
((= x2 x1) 0)
((< x2 x1) -1)))
(defun orientation-diff (p1 p2)
(values (direction-diff (point-x p1) (point-x p2))
(direction-diff (point-y p1) (point-y p2))))
(defun inc-hash-count (hash key)
(setf (gethash key hash)
(if (gethash key hash)
(1+ (gethash key hash))
1)))
(defun expand-line (line hash)
(let ((point1 (line-start line))
(point2 (line-end line)))
(multiple-value-bind (delta-x delta-y) (orientation-diff point1 point2)
(loop for x = (point-x point1) then (+ x delta-x)
for y = (point-y point1) then (+ y delta-y)
do (inc-hash-count hash (cons x y))
until (and (= x (point-x point2))
(= y (point-y point2)))))))
(defun build-count-hash (lines)
(let ((hash (make-hash-table :test 'equal)))
(dolist (line lines) (expand-line line hash))
hash))
(defun angled-line-p (line)
(let ((p1 (line-start line))
(p2 (line-end line)))
(and (/= (point-x p1) (point-x p2))
(/= (point-y p1) (point-y p2)))))
(defun count-crossing-points (lines)
(let ((count-hash (build-count-hash lines)))
(loop for k being the hash-keys in count-hash using (hash-value v)
count (>= v 2))))
(defun part1 (input)
(count-crossing-points (remove-if #'angled-line-p input)))
(defun part2 (input)
(count-crossing-points input))
(format t "===== Part 1 =====")
(format t "Result: ~A~%~%" (time (part1 *data*)))
(format t "===== Part 2 =====")
(format t "Result: ~A~%" (time (part2 *data*)))