Most of day 6

This commit is contained in:
Daniel Ziltener 2023-12-07 18:53:30 +01:00
parent 6228043bf0
commit e01dc06f7a
Signed by: zilti
GPG Key ID: B38976E82C9DAE42
1 changed files with 285 additions and 0 deletions

View File

@ -2419,3 +2419,288 @@ humidity-to-location map:
4182586423 2386599151 112380873
2564353322 975015905 381274978
#+end_src
* Day 6: Wait For It
** Part One
*** Quest
The ferry quickly brings you across Island Island. After asking around, you discover that there is
indeed normally a large pile of sand somewhere near here, but you don't see anything besides lots of
water and the small island where the ferry has docked.
As you try to figure out what to do next, you notice a poster on a wall near the ferry dock. "Boat
races! Open to the public! Grand prize is an all-expenses-paid trip to *Desert Island*!" That must
be where the sand comes from! Best of all, the boat races are starting in just a few minutes.
You manage to sign up as a competitor in the boat races just in time. The organizer explains that
it's not really a traditional race - instead, you will get a fixed amount of time during which your
boat has to travel as far as it can, and you win if your boat goes the farthest.
As part of signing up, you get a sheet of paper (your puzzle input) that lists the *time* allowed
for each race and also the best *distance* ever recorded in that race. To guarantee you win the
grand prize, you need to make sure you *go farther in each race* than the current record holder.
The organizer brings you over to the area where the boat races are held. The boats are much smaller
than you expected - they're actually *toy boats*, each with a big button on top. Holding down the
button *charges the boat*, and releasing the button *allows the boat to move*. Boats move faster if
their button was held longer, but time spent holding the button counts against the total race
time. You can only hold the button at the start of the race, and boats don't move until the button
is released.
For example:
#+begin_example
Time: 7 15 30
Distance: 9 40 200
#+end_example
This document describes three races:
- The first race lasts 7 milliseconds. The record distance in this race is 9 millimeters.
- The second race lasts 15 milliseconds. The record distance in this race is 40 millimeters.
- The third race lasts 30 milliseconds. The record distance in this race is 200 millimeters.
Your toy boat has a starting speed of *zero millimeters per millisecond*. For each whole millisecond
you spend at the beginning of the race holding down the button, the boat's speed increases by *one
millimeter per millisecond*.
So, because the first race lasts 7 milliseconds, you only have a few options:
- Don't hold the button at all (that is, hold it for *=0= milliseconds*) at the start of the
race. The boat won't move; it will have traveled *=0= millimeters* by the end of the race.
- Hold the button for *=1= millisecond* at the start of the race. Then, the boat will travel at a
speed of =1= millimeter per millisecond for =6= milliseconds, reaching a total distance traveled
of *=6 millimeters=.
- Hold the button for *=2= milliseconds*, giving the boat a speed of =2= millimeters per
millisecond. It will then get =5= milliseconds to move, reaching a total distance of *=10=
millimeters*.
- Hold the button for *=3= milliseconds*. After its remaining =4= milliseconds of travel time, the
boat will have gone *=12= millimeters=.
- Hold the button for *=4= milliseconds*. After its remaining =3= milliseconds of travel time, the
boat will have gone *=12= millimeters*.
- Hold the button for *=5= milliseconds*, causing the boat to travel a total of *=10= millimeters*.
- Hold the button for *=6= milliseconds*, causing the boat to travel a total of *=6= millimeters*.
- Hold the button for *=7= milliseconds*. That's the entire duration of the race. You never let go
of the button. The boat can't move until you let go of the button. Please make sure you let go of
the button so the boat gets to move. *=0= millimeters*.
Since the current record for this race is =9= millimeters, there are actually *=4=* different ways
you could win: you could hold the button for =2=, =3=, =4=, or =5= milliseconds at the start of the
race.
In the second race, you could hold the button for at least =4= milliseconds and at most =11=
milliseconds and beat the record, a total of *=8=* different ways to win.
In the third race, you could hold the button for at least =11= milliseconds and no more than =19=
milliseconds and still beat the record, a total of *=9=* ways you could win.
To see how much margin of error you have, determine the *number of ways you can beat the record* in
each race; in this example, if you multiply these values together, you get *=288=* (=4= * =8= *
=9=).
Determine the number of ways you could beat the record in each race. *What do you get if you
multiply these numbers together?*
*** Puzzle Solution
#+NAME: day6-part1-imports
#+begin_src scheme :exports none :noweb yes :tangle day6.scm
(import (chicken string)
(chicken irregex))
#+end_src
#+NAME: day6-input-scm
#+begin_src scheme :tangle day6.scm :noweb yes :exports none
(define input "
<<day6-input>>")
#+end_src
**** Data Extraction
First, we need to define the record for the race data.
#+NAME: day6-part1-race-record
#+begin_src scheme :tangle day6.scm
(define-record race time-limit record-distance winning-distances losing-distances)
#+end_src
The solution starts with the usual data extraction using ~irregex~.
#+NAME: day6-part1-input-extraction-irregex
#+begin_src scheme :tangle day6.scm
(define input-extraction-irregex
'(: "Time:"
(submatch-named time-vals (+ (or numeric whitespace)))
"Distance:"
(submatch-named distance-vals (+ (or numeric whitespace)))))
#+end_src
Next, the data is converted into ~race~ records.
#+NAME: day6-part1-input--race-records
#+begin_src scheme :tangle day6.scm
(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))))))
#+end_src
**** Race Calculations
#+NAME: day6-part1-get-distance
#+begin_src scheme :tangle day6.scm
(define (get-distance race hold-time)
(let ((time-remaining (- (race-time-limit race) hold-time)))
(* time-remaining hold-time)))
#+end_src
#+NAME: day6-part1-calc-race-distances
#+begin_src scheme :tangle day6.scm
(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)))))))
#+end_src
**** Getting The Result
#+NAME: day6-part1-calc-fn
#+begin_src scheme :tangle day6.scm :noweb strip-tangle
<<day6-part1-imports>>
<<day6-input-scm>>
<<day6-part1-race-record>>
<<day6-part1-input-extraction-irregex>>
<<day6-part1-input--race-records>>
<<day6-part1-get-distance>>
<<day6-part1-calc-race-distances>>
(define (calc-part-1)
(foldl *
1 (map (compose length race-winning-distances)
(map calc-race-distances (input->race-records input)))))
#+end_src
#+CALL: day6-part1-calc-fn[:epilogue "(calc-part-1)"]()
#+RESULTS:
: 1159152
** Part Two
*** Quest
As the race is about to start, you realize the piece of paper with race times and record distances
you got earlier actually just has very bad [[https://en.wikipedia.org/wiki/Kerning][kerning]]. There's really *only one race* - ignore the
spaces between the numbers on each line.
So, the example from before:
#+begin_example
Time: 7 15 30
Distance: 9 40 200
#+end_example
...now instead means this:
#+begin_example
Time: 71530
Distance: 940200
#+end_example
Now, you have to figure out how many ways there are to win this single race. In this example, the
race lasts for *=71530= milliseconds* and the record distance you need to beat is *=940200=
millimeters*. You could hold the button anywhere from =14= to =71516= milliseconds and beat the
record, a total of =71503= ways!
*How many ways can you beat the record in this one much longer race?*
*** Puzzle Solution
**** Code Changes
The ~input->race-records~ function has to be rewritten:
#+NAME: day6-part2-input--race-record
#+begin_src scheme :tangle day6.scm
(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)))
#+end_src
And instead of keeping a list of all winning and losing distances, I use a counter.
#+NAME: day6-part2-calc-race-distances
#+begin_src scheme :tangle day6.scm
(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
race (+ hold-time 1)
(if is-new-record? (+ 1 winning-distances) winning-distances)
(if is-new-record? losing-distances (+ 1 losing-distances)))))))
#+end_src
**** Race Variant Results
#+NAME: day6-part2-calc-fn
#+begin_src scheme :tangle day6.scm :noweb strip-tangle
<<day6-part1-imports>>
<<day6-input-scm>>
<<day6-part1-race-record>>
<<day6-part1-input-extraction-irregex>>
<<day6-part2-input--race-record>>
<<day6-part1-get-distance>>
<<day6-part2-calc-race-distances>>
(define (calc-part-2)
(race-winning-distances
(calc-race-distances-with-counter (input->race-record input))))
#+end_src
#+CALL: day6-part2-calc-fn[:epilogue "(calc-part-2)"]()
#+RESULTS:
** Puzzle Input
#+NAME: day6-input
#+begin_src org
Time: 58 81 96 76
Distance: 434 1041 2219 1218
#+end_src