On this page:
<day03>
3.1 How many of the listed triangles are possible?
<day03-setup>
<day03-q1>
3.2 How many triangles are possible when reading the list vertically?
<day03-q2>
3.3 Testing Day 3
<day03-test>
6.7

3 Day 3: Testing Triangles

Read the description of today’s puzzle. Here is my input.

3.1 How many of the listed triangles are possible?

This turned out to be the easiest pair of puzzles to date. Maybe I’m getting better at this.

The puzzle tells us that for a triangle to be valid, the sum of any two sides must be greater than the remaining side. If you think about it for a bit, you’ll see that the two smallest sides are the only ones you need to add.

(require racket rackunit)
 
(define input-string (file->string "day03input.txt"))
 
(define (triangle-valid? triangle-spec)
  (let [(spec (sort triangle-spec >))]
    (> (+ (second spec) (third spec)) (first spec))))

There’s nothing left to do except convert each line of the input into a triangle—that is, a list containing three numbers—and map the triangle-valid? function onto the resulting list of triangles, converting each one into a simple boolean #t or #f value.

Racket’s count function counts values list that pass a given set of criteria. The first argument is the name of the function we want to use to tell it which values to count: if that function returns anything besides #f for a given value, that value is included in the count. And since (identity x) simply returns x, passing identity to count amounts to saying, "count all the non-false values in this list".

(define (string->triangles str)
  (for/list [(ts (in-list (map string-split (string-split str "\n"))))]
            (map string->number ts)))
 
(define (q1 str)
  (count identity (map triangle-valid? (string->triangles str))))

3.2 How many triangles are possible when reading the list vertically?

You’ve got to be kidding me. You’re asking me to rearrange a list of lists into a new list of lists, in a language where lists are basically everything?

Please, don’t throw me in that briar patch!

Ok, so: all we need is a new string->triangles function that looks at the lines three at a time, and pulls from the first, second, and third columns of each. It was in the course of writing this function that I learned of Racket’s for*/list function: it works exactly like nested for loops work in other languages, but without the redundancy.

(define (q2-string->triangles str)
  (define lines (map string-split (string-split str "\n")))
  (for*/list ([row (in-range 0 (length lines) 3)]
              [col (in-range 3)])
             (map string->number (list (list-ref (list-ref lines row) col)
                                       (list-ref (list-ref lines (+ row 1)) col)
                                       (list-ref (list-ref lines (+ row 2)) col)))))
 
(define (q2 str)
  (count identity (map triangle-valid? (q2-string->triangles str))))
 
(define q2-test-string
  (string-append "101 301 501\n"
                 "102 302 502\n"
                 "103 303 503\n"
                 "201 401 601\n"
                 "202 402 602\n"
                 "203 403 603"))

3.3 Testing Day 3

(module+ test
  (check-equal? (q1 "5 10 25") 0)
  (check-equal? (q1 input-string) 1032)
  (check-equal? (q2-string->triangles q2-test-string)
                '((101 102 103)
                  (301 302 303)
                  (501 502 503)
                  (201 202 203)
                  (401 402 403)
                  (601 602 603)))
  (check-equal? (q2 input-string) 1838))