summaryrefslogtreecommitdiffstats
path: root/2021/16/code.tl
blob: 8f2016e8399c4fd65195b6dd6930d0d8bc350818 (plain)
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
(defstruct bitstream ()
  bf
  st
  pfx
  (count 0))

(defstruct packet ()
  version
  type
  payload)

(defmeth bitstream read-file (bs : (name "input"))
  bs.(read-string (file-get-string name)))

(defmeth bitstream read-string (bs s)
  (set bs.bf (buf-uint (toint s 16))
       bs.st (make-buf-stream bs.bf))
  bs.(ensure-prefix 24))

(defmeth bitstream ensure-prefix (bs n)
  (whilet ((b (and (< (len bs.pfx) n) (get-byte bs.st))))
    (upd bs.pfx (nconc @1 (flow b (+ 256) (digits @1 2) cdr))))
  bs)

(defmeth bitstream set-pfx (bs npfx)
  (placelet ((pfx (read-once bs.pfx))
             (count (read-once bs.count)))
    (while (neq pfx npfx)
      (inc count)
      (upd pfx cdr)))
  bs.(ensure-prefix 24))

(defmeth bitstream drop-zeros (bs)
  (while-match (0 . @rest) bs.pfx
    bs.(set-pfx rest))
  bs)

(defmacro val (. bits)
  ^(poly 2 (list ,*bits)))

(defmeth bitstream parse-header (bs)
  (match (@v2 @v1 @v0 @t2 @t1 @t0 . @rest)
         bs.pfx
    bs.(set-pfx rest)
    (new packet
         version (val v2 v1 v0)
         type (val t2 t1 t0))))

(defmeth packet parse-payload (pk bs)
  (caseq pk.type
    (4 (let ((value 0))
         (while-match (@more @v3 @v2 @v1 @v0 . @rest) bs.pfx
           bs.(set-pfx rest)
           (upd value (* 16) (+ (val v3 v2 v1 v0)))
           (if (zerop more)
             (return)))
         (set pk.payload value)))
    (t (match-case bs.pfx
         ((0 . @rest)
          bs.(set-pfx (drop 15 rest))
          (let ((nbits (poly 2 (take 15 rest)))
                (count bs.count))
            (set pk.payload
                 (build
                   (while (< (- bs.count count) nbits)
                     (add bs.(parse-header).(parse-payload bs)))))))
         ((1 . @rest)
          bs.(set-pfx (drop 11 rest))
          (let ((npkt (poly 2 (take 11 rest))))
            (set pk.payload
                 (build
                   (dotimes (i npkt)
                     (add bs.(parse-header).(parse-payload bs))))))))))
  pk)

(defmeth packet version-sum (pk)
  (+ pk.version
     (ifa (listp pk.payload)
       (sum it .(version-sum))
       0)))

(defun get-packet (: (name "input"))
  (let* ((bs (new bitstream).(read-file name).(drop-zeros)))
    bs.(parse-header).(parse-payload bs)))

(defun compile-packet (pk)
  (let ((mathfun (relate '(0 1 2 3) '(+ * min max) nil))
        (relfun (relate '(5 6 7) '(> < =) nil)))
    (match-case pk
      (@(struct packet type 4 payload @(integerp @literal)) literal)
      (@(struct packet type @(@fun [mathfun]) payload @args) ^(,fun ,*[mapcar compile-packet args]))
      (@(struct packet type @(@fun [relfun]) payload @args) ^(if (,fun ,*[mapcar compile-packet args]) 1 0))
      (@else (error "invalid syntax ~s" else)))))

(defun solve-one (: (name :))
  (let ((pk (get-packet name)))
    pk.(version-sum)))

(defun solve-two (: (name :))
  (let ((pk (get-packet name)))
    (eval (compile-packet pk))))