aboutsummaryrefslogtreecommitdiff
path: root/day12.scm
blob: e48c4330c30f526d21f5f2d48ed967d04569930b (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
112
113
114
115
(define-module (day12)
	       #:use-module (ice-9 rdelim)
	       #:use-module (srfi srfi-1))

(define (read-input port)
  (define (iter acc)
    (define line (read-line port))
    (if (or (eof-object? line) (string= line ""))
      acc
      (iter (cons (string->list line) acc))))
  (list->array 2 (reverse (iter '()))))

(define (rectangle-fold proc init x0 xpast y0 ypast)
  (let yloop ((y y0) (result init))
    (if (>= y ypast)
        result
        (let xloop ((x x0) (result result))
          (if (>= x xpast)
              (yloop (+ y 1) result)
              (xloop (+ x 1) (proc x y result)))))))

(define (add-margin input)
  (define-values (h w) (apply values (array-dimensions input)))
  (define result
    (make-array #\space (list -1 h) (list -1 w)))
  (rectangle-fold
    (lambda (i j acc)
      (array-set! result (array-ref input i j) i j))
    #f 0 h 0 w)
  result)

(define (get-area mp visited i j)
  (define c (array-ref mp i j))
  (array-set! visited #t i j)
  (fold (lambda (di dj acc)
	  (define ni (+ i di))
	  (define nj (+ j dj))
	  (if (or (array-ref visited ni nj)
		  (not (char=? c (array-ref mp ni nj))))
	    acc
	    (+ acc (get-area mp visited ni nj))))
	1 '(1 -1 0 0) '(0 0 1 -1)))

(define (set-area! mp areas val i j)
  (define c (array-ref mp i j))
  (when (zero? val) (error "Zero val!"))
  (array-set! areas val i j)
  (fold (lambda (di dj acc)
	  (define ni (+ i di))
	  (define nj (+ j dj))
	  (when (and (zero? (array-ref areas ni nj))
		     (char=? c (array-ref mp ni nj)))
	    (set-area! mp areas val ni nj)))
	#f '(1 -1 0 0) '(0 0 1 -1)))

(define (get-areas mp h w)
  (define result (make-array 0 (list -1 h) (list -1 w)))
  (define visited (make-array #f (list -1 h) (list -1 w)))
  (rectangle-fold (lambda (i j acc)
		    (unless (array-ref visited i j)
		      (set-area! mp result
				 (get-area mp visited i j)
				 i j)))
		  #f 0 h 0 w)
  result)

(define (get-costs mp areas h w)
  (rectangle-fold
    (lambda (i j acc)
      (define c (array-ref mp i j))
      (+ acc
	 (if (char=? c (array-ref mp (+ i 1) j))
	   0
	   (+ (array-ref areas i j) (array-ref areas (+ i 1) j)))
	 (if (char=? c (array-ref mp i (+ j 1)))
	   0
	   (+ (array-ref areas i j) (array-ref areas i (+ j 1))))))
    0 -1 h -1 w))

(define (part1 port)
  (define input (read-input port))
  (define-values (h w) (apply values (array-dimensions input)))
  (define mp (add-margin input))
  (define areas (get-areas mp h w))
  (get-costs mp areas h w))

(define (get-discount-costs mp areas h w)
  (define (count di dj)
    (define visited
      (make-array #f (list -1 h) (list -1 w)))
    (define (visit! i j c)
      (unless (or (not (char=? c (array-ref mp i j)))
		  (char=? c (array-ref mp (+ i di) (+ j dj))))
	(array-set! visited #t i j)
	(visit! (if (zero? di) (+ i 1) i)
		(if (zero? dj) (+ j 1) j) c)))
    (lambda (i j acc)
      (if (array-ref visited i j)
	acc
	(let ((c (array-ref mp i j)))
	  (if (char=? c (array-ref mp (+ i di) (+ j dj)))
	    acc
	    (begin (visit! i j c)
		   (+ acc (array-ref areas i j))))))))
  (+ (rectangle-fold (count -1 0) 0 0 h 0 w)
     (rectangle-fold (count 1 0) 0 0 h 0 w)
     (rectangle-fold (count 0 -1) 0 0 h 0 w)
     (rectangle-fold (count 0 1) 0 0 h 0 w)))

(define (part2 port)
  (define input (read-input port))
  (define-values (h w) (apply values (array-dimensions input)))
  (define mp (add-margin input))
  (define areas (get-areas mp h w))
  (get-discount-costs mp areas h w))