Advent-of-Code-2023/day6.scm

185 lines
5.9 KiB
Scheme

;; Puzzle Solution
;; #+NAME: day6-part1-imports
;; [[file:chicken-src.org::day6-part1-imports][day6-part1-imports]]
;; -*- geiser-scheme-implementation: chicken -*-
(import (chicken string)
(chicken irregex))
;; day6-part1-imports ends here
;; #+NAME: day6-input-scm
;; [[file:chicken-src.org::day6-input-scm][day6-input-scm]]
;; -*- geiser-scheme-implementation: chicken -*-
(define input "
Time: 58 81 96 76
Distance: 434 1041 2219 1218")
;; day6-input-scm ends here
;; Data Extraction
;; First, we need to define the record for the race data.
;; #+NAME: day6-part1-race-record
;; [[file:chicken-src.org::day6-part1-race-record][day6-part1-race-record]]
;; -*- geiser-scheme-implementation: chicken -*-
(define-record race time-limit record-distance winning-distances losing-distances)
;; day6-part1-race-record ends here
;; The solution starts with the usual data extraction using ~irregex~.
;; #+NAME: day6-part1-input-extraction-irregex
;; [[file:chicken-src.org::day6-part1-input-extraction-irregex][day6-part1-input-extraction-irregex]]
;; -*- geiser-scheme-implementation: chicken -*-
(define input-extraction-irregex
'(: "Time:"
(submatch-named time-vals (+ (or numeric whitespace)))
"Distance:"
(submatch-named distance-vals (+ (or numeric whitespace)))))
;; day6-part1-input-extraction-irregex ends here
;; Next, the data is converted into ~race~ records.
;; #+NAME: day6-part1-input--race-records
;; [[file:chicken-src.org::day6-part1-input--race-records][day6-part1-input--race-records]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (input->race-records input)
(let ((match (irregex-search input-extraction-irregex
input)))
(map (lambda (time distance)
(make-race time distance #f #f))
(map string->number (string-split (irregex-match-substring match 'time-vals)))
(map string->number (string-split (irregex-match-substring match 'distance-vals))))))
;; day6-part1-input--race-records ends here
;; Race Calculations
;; #+NAME: day6-part1-get-distance
;; [[file:chicken-src.org::day6-part1-get-distance][day6-part1-get-distance]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (get-distance race hold-time)
(let ((time-remaining (- (race-time-limit race) hold-time)))
(* time-remaining hold-time)))
;; day6-part1-get-distance ends here
;; #+NAME: day6-part1-calc-race-distances
;; [[file:chicken-src.org::day6-part1-calc-race-distances][day6-part1-calc-race-distances]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (calc-race-distances race
#!optional (hold-time 1) (winning-distances '()) (losing-distances '()))
(let* ((record-distance (race-record-distance race))
(new-distance (get-distance race hold-time))
(is-new-record? (> new-distance record-distance)))
(cond
((= new-distance 0)
(begin
(race-winning-distances-set! race winning-distances)
(race-losing-distances-set! race losing-distances)
race))
(else
(calc-race-distances
race (+ hold-time 1)
(if is-new-record? (cons new-distance winning-distances) winning-distances)
(if is-new-record? losing-distances (cons new-distance losing-distances)))))))
;; day6-part1-calc-race-distances ends here
;; Getting The Result
;; #+NAME: day6-part1-calc-fn
;; [[file:chicken-src.org::day6-part1-calc-fn][day6-part1-calc-fn]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (calc-part-1)
(foldl *
1 (map (compose length race-winning-distances)
(map calc-race-distances (input->race-records input)))))
;; day6-part1-calc-fn ends here
;; Code Changes
;; The ~input->race-records~ function has to be rewritten:
;; #+NAME: day6-part2-input--race-record
;; [[file:chicken-src.org::day6-part2-input--race-record][day6-part2-input--race-record]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (input->race-record input)
(let* ((match (irregex-search input-extraction-irregex input))
(time-val ((compose string->number
(cut foldl string-append "" <>)
string-split
(cut irregex-match-substring <> 'time-vals))
match))
(distance-val ((compose string->number
(cut foldl string-append "" <>)
string-split
(cut irregex-match-substring <> 'distance-vals))
match)))
(make-race time-val distance-val #f #f)))
;; day6-part2-input--race-record ends here
;; And instead of keeping a list of all winning and losing distances, I use a counter.
;; #+NAME: day6-part2-calc-race-distances
;; [[file:chicken-src.org::day6-part2-calc-race-distances][day6-part2-calc-race-distances]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (calc-race-distances-with-counter race
#!optional (hold-time 1) (winning-distances 0) (losing-distances 0))
(let* ((record-distance (race-record-distance race))
(new-distance (get-distance race hold-time))
(is-new-record? (> new-distance record-distance)))
(cond
((= new-distance 0)
(begin
(race-winning-distances-set! race winning-distances)
(race-losing-distances-set! race losing-distances)
race))
(else
(calc-race-distances-with-counter
race (+ hold-time 1)
(if is-new-record? (+ 1 winning-distances) winning-distances)
(if is-new-record? losing-distances (+ 1 losing-distances)))))))
;; day6-part2-calc-race-distances ends here
;; Race Variant Results
;; #+NAME: day6-part2-calc-fn
;; [[file:chicken-src.org::day6-part2-calc-fn][day6-part2-calc-fn]]
;; -*- geiser-scheme-implementation: chicken -*-
(define (calc-part-2)
(race-winning-distances
(calc-race-distances-with-counter (input->race-record input))))
;; day6-part2-calc-fn ends here