aboutsummaryrefslogtreecommitdiffstats
path: root/AoC2023/day07/solver.lisp
blob: 4cb0a9ec153562b8d729dad7490145e83cdc4cf5 (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
102
103
104
105
106
107
108
109
110
111
(ql:quickload '(fiveam arrows trivia str))
(defpackage #:day07
  (:use #:cl)
  (:local-nicknames (#:f #:fiveam)))

(in-package :day07)

(defparameter eg-input "32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483")

(defun occurences (str &optional joker)
  (let* ((bag (make-hash-table))
         (jokers
           (loop
             for le across str
             do (incf (gethash le bag 0))
             count (eq le #\J)))
         (result (sort (alexandria:hash-table-values bag) #'>)))
    (when joker
      (setf result (remove jokers result :count 1))
      (if (= jokers 5)
          (setf result '(5))
          (incf (car result) jokers)))
    result))

(defun hand-type (str &optional joker)
  (trivia:match (occurences str joker)
    ((list 5) 5)
    ((list 4 1) 4)
    ((list 3 2) 3)
    ((list 3 1 1) 2)
    ((list 2 2 1) 1)
    ((list 2 1 1 1) 0)
    ((list 1 1 1 1 1) -1)))

(defun card-strength (card &optional joker)
  (position card
            (if joker
                "J23456789TQKA"
                "23456789TJQKA")))

(defun card-< (a b &optional joker)
  (< (card-strength a joker) (card-strength b joker)))

(defun hand-< (a b &optional joker)
  (let ((a-t (hand-type a joker))
        (b-t (hand-type b joker)))
    (cond
      ((< a-t b-t))
      ((= a-t b-t)
       (loop for l across a
             for s across b
             unless (eq l s)
               return (card-< l s joker))))))

(f:test partial
  (f:is
   (equal '(3 1 1)
          (occurences "QJQAQ")))
  (f:is
   (equal '(4 1)
          (occurences "QJQAQ" 'joker)))
  (f:is
   (equal '(5)
          (occurences "JJJJJ" 'joker)))
  (f:is (card-< #\2 #\J))
  (f:is-false (card-< #\2 #\J 'joker))
  (f:is (hand-< "2AAAA" "33332"))
  (f:is (hand-< "77788" "77888"))
  (f:is (hand-< "77788" "77778")))

(defun parse-input (str)
  (let ((data (arrows:->>
               (ppcre:split "\\s" str)
               (remove-if #'str:empty?))))
    (loop for (hand bid) on data by #'cddr
          collect (cons hand (parse-integer bid)))))

(defun total-win (sorted-hands)
  (loop for (hand . bid) in sorted-hands
        for c from 1
        sum (* c bid)))

(defun solver (lines &optional joker)
  (arrows:->
   (parse-input lines)
   (sort (lambda (a b)
           (hand-< a b joker))
         :key #'car)
   (total-win)))

(f:test solutions
  (f:is
   (= 6440
      (solver eg-input)))
  (f:is
   (= 5905
      (solver eg-input 'joker)))
  (f:is
   (= 253205868
      (solver
       (uiop:read-file-string "input"))))
  (f:is
   (= 253907829
      (solver
       (uiop:read-file-string "input") 'joker))))

;; part2