-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadvent-of-code-2024.lisp
181 lines (154 loc) · 5.66 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
(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 taxicab-distance (point1 point2)
"Returns the taxicab distance between POINT1 and POINT2."
(destructuring-bind (row-diff . col-diff)
(point- point1 point2)
(+ (abs row-diff)
(abs col-diff))))
(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 parse-direction (char)
"Returns direction for the chars ^v<>"
(ecase char
(#\^ :up)
(#\v :down)
(#\< :left)
(#\> :right)))
(defun move (point direction &optional (distance 1))
"Return the the coordinates DISTANCE away from POINT in DIRECTION."
(ecase 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 (sequences)
"Transpose a list of SEQUENCES."
(apply #'map 'list 'list sequences)
(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 print-map (map)
"Prints MAP, a hash-map of (row . col) to characters."
(loop with rows = (apply #'max (mapcar #'car (hash-table-keys map)))
with cols = (apply #'max (mapcar #'cdr (hash-table-keys map)))
for row upto rows
do (loop for col upto cols
do (write-char (@ map (cons row col))))
(terpri)))
(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))