advent-of-code/2019/day14/day14.rkt
2024-11-12 21:46:18 +01:00

81 lines
2.6 KiB
Racket

#lang racket
(module+ test
(require rackunit))
(module+ main
(displayln "Day 14"))
(define (read-rules filename)
(with-input-from-file filename
(lambda ()
(define h (make-hash))
(for ([line (in-lines)])
(define rule
(for/list ([elt (regexp-match* #rx"([0-9]+) ([A-Z]+)" line #:match-select cdr)])
(list (string->number (car elt)) (string->symbol (cadr elt)))))
(define output (last rule))
(hash-set! h
(cadr output)
(cons (car output)
(make-hash (for/list ([elt (drop-right rule 1)])
(cons (cadr elt) (car elt)))))))
h)))
(define (generate! rules needed available)
(for ([(target target-amount) (in-hash needed)]
#:unless (eq? target 'ORE))
(define inputs (cdr (hash-ref rules target)))
(define output-amount (car (hash-ref rules target)))
(define new-target-amount (- target-amount (hash-ref available target 0)))
(define multiple (ceiling (/ new-target-amount output-amount)))
(hash-set! available target (- (* multiple output-amount) new-target-amount))
(hash-set! needed target 0)
(for ([(input input-amount) (in-hash inputs)])
(hash-update! needed input (lambda (v) (+ v (* multiple input-amount))) 0))))
(define (ore-per-fuel rules amount)
(define needed (make-hash `((FUEL . ,amount))))
(define available (make-hash))
(for ([i (in-naturals)]
#:break (= (apply + (hash-values needed)) (hash-ref needed 'ORE 0)))
(generate! rules needed available))
(hash-ref needed 'ORE))
(define (part1 filename)
(define rules (read-rules filename))
(ore-per-fuel rules 1))
(module+ test
(check-eq? 31 (part1 "test1"))
(check-eq? 165 (part1 "test2"))
(check-eq? 13312 (part1 "test3"))
(check-eq? 180697 (part1 "test4"))
(check-eq? 2210736 (part1 "test5")))
(module+ main
(display "Part 1: ")
(displayln (part1 "input")))
(define (part2 filename)
(define rules (read-rules filename))
(define max-fuel 1000000000000)
(define min-fuel 1)
(bisect rules 1000000000000 min-fuel max-fuel))
(define (bisect rules target-ore min-fuel max-fuel)
(define mid-fuel (floor (+ min-fuel (/ (- max-fuel min-fuel) 2))))
(define ore (ore-per-fuel rules mid-fuel))
(cond
[(= 1 (- max-fuel min-fuel)) min-fuel]
[(< ore target-ore) (bisect rules target-ore mid-fuel max-fuel)]
[(< target-ore ore) (bisect rules target-ore min-fuel mid-fuel)]))
(module+ test
(check-eq? 82892753 (part2 "test3"))
(check-eq? 5586022 (part2 "test4"))
(check-eq? 460664 (part2 "test5")))
(module+ main
(display "Part 2: ")
(displayln (part2 "input")))