Rules: no spoilers.

The other rules are made up as we go along.

Share code by link to a forge, home page, pastebin (Eric Wastl has one here) or code section in a comment.

  • datarama@awful.systems
    link
    fedilink
    English
    arrow-up
    4
    ·
    edit-2
    1 year ago

    I will mostly be using Scheme. Comments and questions are very welcome.

    Day 2: Cube Conundrum

    Scheme Code and Explanation

    The string format of a game is, for example: "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green". I’ll transform a game string into a list, such that the above example becomes (1 (4 0 3) (1 2 6) (0 2 0)).

    I start by simply splitting on : and chopping off "Game" from the first string to get the game number. The second string consists of a sequence of draws, which need a bit further parsing effort.

    (define (parse-game game)
      (let* ((parts (string-split game ":"))
             (number (string->number (string-drop (car parts) 5)))
             (draws (map parse-draws (string-split (cadr parts) ";"))))
        (cons number draws)))
    

    Parsing a draw is done by first splitting it on ;, to yield strings on the form "3 blue, 4 red". Each such string represents a cubeset, which I represent as lists on the form (r g b). Since the input strings don’t have any well-defined ordering of cube colours, I split on , and iterate on the resulting list of strings until I find one that has the proper colour name as its suffix, in the order red -> green -> blue. If none exists, I return 0 for that colour.

    (define (parse-draws cstr)
      (let ((cubes (map string-trim (string-split cstr ","))))
        (list (get-col cubes "red")
              (get-col cubes "green")
              (get-col cubes "blue"))))
    
    (define (get-col cubeset col)
      (cond ((null? cubeset) 0)
            ((string-suffix? col (car cubeset))
             (string->number (car (string-split (car cubeset) " "))))
            (else (get-col (cdr cubeset) col))))
    

    To determine whether a game is possible given some numbers of different colours, I need to find the maxima of each colour for all draws in a game:

    (define (maxima cubesets)
      (let loop ((cubesets cubesets) (r 0) (g 0) (b 0))
        (if (null? cubesets)
          (list r g b)
          (loop (cdr cubesets)
                (max r (caar cubesets))
                (max g (cadar cubesets))
                (max b (caddar cubesets))))))
    

    The maxima of the draw ((4 0 3) (1 2 6) (0 2 0)) is (4 2 6). With that, it’s easy to determine whether a game is possible:

    (define (game-possible? game r g b)
      (let ((mx (maxima (cdr game))))
        (and (>= r (car mx))
             (>= g (cadr mx))
             (>= b (caddr mx)))))
    

    The solution to part 1 is now simple:

    (define (solve-part1 lines)
      (let solve ((sum 0) (games (map parse-game lines)))
        (cond ((null? games) sum)
              ((game-possible? (car games) 12 13 14)
               (solve (+ sum (caar games)) (cdr games)))
              (else (solve sum (cdr games))))))
    

    …where lines is all the lines of the input file.

    Part 2 is simple, given that I already have a way to get the maxima of the draws in a game. All I need to do is to multiply them together:

    (define (power-cubeset cubeset)
      (* (car cubeset) (cadr cubeset) (caddr cubeset)))
    
    (define (solve-part2 lines)
      (let solve ((sum 0) (games (map parse-game lines)))
        (cond ((null? games) sum)
              (else (solve (+ sum (power-cubeset (maxima (cdar games))))
                           (cdr games))))))