Raku: Advent of Code 2020 - Day Thirteen

Raku solutions to Day Thirteen of the 2020 Advent of Code. Today's puzzle involved bus routes. Part One requires you to figure out which route id will arrive closest to a timestamp. The answer to Part Two involves identifying when an alignment of departure times across route ids will occur.

#!/usr/bin/env raku
use v6.d;

my ($depart, $line) = 'input'.IO.slurp.split("\n");
$depart .= Int;

say "Part One: " ~
[*] $line.split(',').grep(none 'x')».Int
.map({ $_, $_ - ($depart) mod $_ })
.min({ .[1] })
.flat;

# index of @l is the offset from factor
my @l = $line.split(',');
my ($r, $factor) = (0, @l[0].Int);
for 1..^@l -> $i {
next if @l[$i] eq 'x';
$r = ($r, {$_ + $factor} ... -> $a { ($a + $i) mod @l[$i].Int == 0 }).tail;
$factor *= @l[$i];
}
say "Part Two: $r";

Part One

Given the sample input and the code for part one:

939
7,13,x,x,59,x,31,19
my ($depart, $line) = 'input'.IO.slurp.split("\n");
$depart .= Int;

say "Part One: " ~
[*] $line.split(',').grep(none 'x')».Int
.map({ $_, $_ - ($depart) mod $_ })
.min({ .[1] })
.flat;

We start by processing the input to get the target departure time and the line holding the bus route ids.

my ($depart, $line) = 'input'.IO.slurp.split("\n");
$depart .= Int;

$depart .= Int;  uses the .= method infix operator to replace the string value in the scalar with an integer.

$line.split(',').grep(none 'x')».Int

Gets the list of bus route ids filtering out the x's. ».Int uses the hyper methodcall operator to insure that each bus route id passed to .map is an integer.

Here is where we solve the puzzle. The answer is the product of the bus route id and the amount of time we have to wait past our earliest departure time ($depart). 

.map({ $_, $_ - ($depart) mod $_ })

So we map the bus route ids to a list containing two elements: 0) the route id and 1) the difference of the id and the remainder of the departure time divided by the route id. The difference is the amount of time we have to wait.

.min({ .[1] })

Here we get the two element list for the route which has the smallest wait time. 

.flat;

Because the result of these chained method calls will be provide to the list consumed by the [*] reduce metaoperator, we flatten it to insure it multiplies the values instead of the number of elements stored in the list.

say "Part One: " ~
[*] ... .flat;

Prints out our answer, the product of the bus route id and the wait time... 295 (for the sample input listed above).

Part Two

The puzzle for part two was to find the earliest timestamp such that the first bus ID departs at that time and each subsequent listed bus ID departs at that subsequent minute... given the input. The sample input being:

7,13,x,x,59,x,31,19

Where the nth item in the list departs n minutes after the zeroeth item departs. The x items are ignored.

I struggled for good bit before I figured out how best to approach this problem. The light bulb went on in my head when I wrote out an example for the input 

2,3
   2  3
1  .  .
2  D  .
3  .  D
4  D  .
5  .  .
6  D  D
7  .  .
8  D  .
9  .  D 

I noticed that the timestamps 2 and 8 which satisfy the conditions repeat at an interval which is the common factor of 2 and 3.

So I came up with the approach below which iteratively creates a sequence for each input "bus route". The sequence terminates when a multiple of the common factor with offset has no remainder. It uses the compound factor and last value of that sequence as the seed for the next sequence.  The final sequence returns the answer to part two.

# index of @l is the offset from factor
my @l = $line.split(',');
my ($r, $factor) = (0, @l[0].Int);
for 1..^@l -> $i {
next if @l[$i] eq 'x';
$r = ($r, {$_ + $factor} ... -> $a { ($a + $i) mod @l[$i].Int == 0 }).tail;
$factor *= @l[$i];
}
say "Part Two: $r";

For the example sample input... the result produced is:

1068781

Comments

Popular posts from this blog

Raku: Setting up Raku (for Contributors)

Raku: Advent of Code 2020 - Day Twelve