-
Notifications
You must be signed in to change notification settings - Fork 0
/
advent-of-code-2024.lisp
156 lines (132 loc) · 4.89 KB
/
advent-of-code-2024.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
(in-package :advent-of-code-2024)
(defun list-reader-macro (stream char)
(declare (ignore char))
`(list ,@(read-delimited-list #\] stream t)))
(defun dict-reader-macro (stream char)
(declare (ignore char))
`(serapeum:dict ,@(read-delimited-list #\} stream t)))
(named-readtables:defreadtable
:aoc-sugar
(:merge
;; λ(* 2 _) style lambda shorthand syntax
:fn.reader
;; string interpolation and regex literals
;; see: http://edicl.github.io/cl-interpol/
:interpol-syntax)
(:macro-char #\[ #'list-reader-macro)
(:macro-char #\] (get-macro-character #\)))
(:macro-char #\{ #'dict-reader-macro)
(:macro-char #\} (get-macro-character #\))))
(defun string-to-num-list (string)
"Return a list of all numbers in STRING."
(mapcar #'parse-integer (ppcre:all-matches-as-strings "[-\\d]+" string)))
(defun string-to-num-lists (string)
"Return a list containing a list of all numbers for each line of STRING."
(mapcar #'string-to-num-list (lines string)))
(defun point+ (point1 point2)
(cons (+ (car point1) (car point2))
(+ (cdr point1) (cdr point2))))
(defun point- (point1 point2)
(cons (- (car point1) (car point2))
(- (cdr point1) (cdr point2))))
(defun point-in-bounds-p (point rows cols)
(and (<= 0 (car point) (1- rows))
(<= 0 (cdr point) (1- cols))))
(defun row+ (point &optional (distance 1))
"Add DISTANCE to the row of POINT."
(destructuring-bind (row . col) point
(cons (+ row distance) col)))
(defun row- (point &optional (distance 1))
"Subtract DISTANCE from the row of POINT."
(destructuring-bind (row . col) point
(cons (- row distance) col)))
(defun col+ (point &optional (distance 1))
"Add DISTANCE to the col of POINT."
(destructuring-bind (row . col) point
(cons row (+ col distance))))
(defun col- (point &optional (distance 1))
"Subtract DISTANCE from the col of POINT."
(destructuring-bind (row . col) point
(cons row (- col distance))))
(defun neighbors (point &key directions)
"Returns a list of the four points adjacent to POINT."
(loop for f in '(row+ row- col+ col-)
for direction in '(:down :up :right :left)
for neighbor = (funcall f point)
if directions
collect (cons neighbor direction)
else
collect neighbor))
(defun move (point direction &optional (distance 1))
"Return the the coordinates DISTANCE away from POINT in DIRECTION."
(case direction
(:up (row- point distance))
(:down (row+ point distance))
(:left (col- point distance))
(:right (col+ point distance))))
(defun right-turn (direction)
"Rotates DIRECTION to the right 90 degrees."
(ecase direction
(:up :right)
(:right :down)
(:down :left)
(:left :up)))
(defun cross-product (point1 point2)
(bind (((x1 . y1) point1)
((x2 . y2) point2))
(- (* x1 y2) (* x2 y1))))
;; https://en.wikipedia.org/wiki/Shoelace_formula
(defun shoelace (points)
"Compute the area of the polygon defined by POINTS."
(abs (/ (loop for prev = (lastcar points) then point
for point in points
sum (cross-product prev point))
2)))
(defun remove-nth (n seq)
"Remove element with index N from SEQ."
(append (subseq seq 0 n)
(subseq seq (1+ n))))
(defun transpose (lists)
"Transpose a list of LISTS."
(apply #'mapcar #'list lists))
(defun count-subseq (sequence-1 sequence-2)
"Returns the number of matches for SEQUENCE-1 within SEQUENCE-2."
(loop for pos = (search sequence-1 sequence-2)
then (search sequence-1 sequence-2 :start2 (1+ pos))
while pos
sum 1))
(defun parse-map (string &key (parse #'identity))
"Returns a hash table mapping (row . col) to the character at that position in
STRING."
(loop with map = (dict)
for line in (lines string)
for row from 0
do (loop for char across line
for col from 0
do (setf (@ map (cons row col)) (funcall parse char)))
finally (return map)))
(defun digits (number)
"Returns a list of digits in NUMBER."
(loop with result
for n = number then (floor n 10)
while (plusp n)
do (push (mod n 10) result)
finally (return result)))
(defun digits-to-num (digits)
"Converts a list of DIGITS to a number."
(loop for n = 0 then (+ (* 10 n) digit)
for digit in digits
finally (return n)))
(defun flood-fill (start map visited)
"Returns a list of points forming the contiguous region around START that all
contain the same char in MAP. VISITED is updated with the points in the
region."
(loop with points = (list start)
and char = (@ map start)
for point = (pop points) while point
when (not (@ visited point))
do (setf (@ visited point) t)
(loop for neighbor in (neighbors point)
when (eql char (@ map neighbor))
do (push neighbor points))
and collect point))