Skip to content

Commit 2e3476b

Browse files
committed
Day 18 in Raku with flat lists
1 parent 2151883 commit 2e3476b

File tree

5 files changed

+271
-0
lines changed

5 files changed

+271
-0
lines changed

2021/day18/day18.raku

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env raku
2+
# Copyright 2021 Google LLC
3+
#
4+
# Use of this source code is governed by an MIT-style
5+
# license that can be found in the LICENSE file or at
6+
# https://opensource.org/licenses/MIT.
7+
#
8+
# https://adventofcode.com/2021/day/18
9+
use v6.d;
10+
use fatal;
11+
12+
# Input is nested pairs with brackets and digits. Throw away comments and work with a list ints and
13+
# open/close bracket characters.
14+
grammar InputFormat {
15+
rule TOP { <line>+ }
16+
token ws { <!ww>\h* }
17+
token num { \d+ }
18+
token bracket { \[ | \] }
19+
token thing { [ <bracket> | <num> ] \,? }
20+
token line { <thing>+ \n }
21+
}
22+
23+
class Actions {
24+
method TOP($/) { make $<line>».made.List }
25+
method num($/) { make $/.Int }
26+
method bracket($/) { make $/.Str }
27+
method thing($/) { make $<bracket>.made || $<num>.made }
28+
method line($/) { make $<thing>».made.List }
29+
}
30+
31+
sub reduce-pair(*@stuff is copy) {
32+
REDUCE: loop {
33+
my $depth = 0;
34+
my $i = 0;
35+
EXPLODE: for ^@stuff -> $i {
36+
given @stuff[$i] {
37+
when '[' {
38+
$depth++;
39+
if $depth > 4 {
40+
my ($first, $second) = @stuff[$i+1, $i+2];
41+
die "Not ints after $i $first $second @stuff" unless $first ~~ Int && $second ~~ Int;
42+
die "Not simple pair $i @stuff[]" unless @stuff[$i+3] eq ']';
43+
my $j = $i;
44+
$j-- until $j < 0 || @stuff[$j] ~~ Int;
45+
# say "Setting $j to @stuff[$j] + $first" unless $j < 0;
46+
# TODO Why does this sometimes fail with "Cannot assign to an immutable value"?
47+
# @stuff[$j] += $first unless $j < 0;
48+
@stuff.splice($j, 1, @stuff[$j] + $first) unless $j < 0;
49+
$j = $i + 3;
50+
$j++ until $j ≥ +@stuff || @stuff[$j] ~~ Int;
51+
# say "Setting $j to @stuff[$j] + $second" unless $j ≥ +@stuff;
52+
# @stuff[$j] += $second unless $j ≥ +@stuff;
53+
@stuff.splice($j, 1, @stuff[$j] + $second) unless $j ≥ +@stuff;
54+
@stuff.splice($i, 4, 0);
55+
next REDUCE;
56+
}
57+
}
58+
when ']' { $depth-- }
59+
when Int { }
60+
default { die "unexpected value $_ in @stuff[]" }
61+
}
62+
}
63+
die "Depth $depth after @stuff" unless $depth == 0;
64+
SPLIT: for ^@stuff -> $i {
65+
if @stuff[$i] ~~ Int && @stuff[$i] ≥ 10 {
66+
@stuff.splice($i, 1, ('[', .floor, .ceiling, ']')) given @stuff[$i]/2;
67+
next REDUCE;
68+
}
69+
}
70+
last REDUCE;
71+
}
72+
@stuff
73+
}
74+
75+
sub add-pair($a, $b) { reduce-pair(('[', |$a, |$b, ']')).cache }
76+
77+
sub magnitude(@pair) {
78+
my @nums;
79+
for @pair {
80+
given $_ {
81+
when Int { @nums.push($_) };
82+
when '[' { }
83+
when ']' { @nums.push((@nums.splice(*-2) Z* (3, 2)).sum) } # 3, 2 are specified in the problem
84+
default { die "unexpected value $_ in @pair[]" };
85+
}
86+
}
87+
die "Nums remainder: @nums[] from @pair[]" unless +@nums == 1;
88+
@nums[0]
89+
}
90+
91+
# Pairs must be reduced using explode and split operations, applying the leftmost explode until
92+
# there are no more explodes, then the leftmost split until there are no more splits. Explodes
93+
# happen to any pair that's nested in four other pairs. Splits happen to any int ≥ 10.
94+
class Solver {
95+
has Str $.input is required;
96+
has $.parsed = InputFormat.parse($!input, :actions(Actions.new)) || die 'Parse failed';
97+
has @.lines = $!parsed<line.made;
98+
}
99+
100+
# Add (wrap in a pair) all lines in the input, then determine the recursive magnitude:
101+
# 3 * first value + 2 * second value.
102+
class Part1 is Solver { method solve( --> Str(Cool)) { magnitude(@.lines.reduce: &add-pair) } }
103+
104+
# Determine the maximum magnitude of adding any two pairs in the input, noting that addition is
105+
# not commutative.
106+
class Part2 is Solver {
107+
method solve( --> Str(Cool)) { (@.lines X @.lines).map({magnitude(add-pair($_[0], $_[1]))}).max }
108+
}
109+
110+
class RunContext {
111+
has $.input-file;
112+
has $.input;
113+
has %.expected is rw;
114+
has @.passed;
115+
116+
method run-part(Solver:U $part) {
117+
my $num = $part.^name.comb(/\d+/).head;
118+
my $expected = $.expected«$num» // '';
119+
say "Running Day18 part $num on $!input-file expecting '$expected'";
120+
my $start = now;
121+
my $solver = $part.new(:$!input);
122+
my $result = $solver.solve();
123+
my $end = now;
124+
put $result;
125+
"Part $num took %.3fms\n".printf(($end - $start) * 1000);
126+
@!passed.push($result eq 'TODO' || $expected && $expected eq $result);
127+
if $expected {
128+
if $expected eq $result {
129+
say "\c[CHECK MARK] PASS with expected value '$result'";
130+
} else {
131+
say "\c[CROSS MARK] FAIL expected '$expected' but got '$result'";
132+
}
133+
}
134+
}
135+
}
136+
137+
sub MAIN(*@input-files) {
138+
my $exit = all();
139+
for @input-files -> $input-file {
140+
if $input-file.IO.slurp -> $input {
141+
my $context = RunContext.new(:$input, :$input-file);
142+
if (my $expected-file = $input-file.IO.extension('expected')).f {
143+
for $expected-file.lines {
144+
$context.expected«$0» = $1.trim if m/part (\d+) \: \s* (.*)/;
145+
}
146+
}
147+
$context.run-part(Part1);
148+
say '';
149+
$context.run-part(Part2);
150+
$exit &= all($context.passed);
151+
} else {
152+
say "EMPTY INPUT FILE: $input-file";
153+
}
154+
say '=' x 40;
155+
}
156+
exit $exit ?? 0 !! 1;
157+
}

2021/day18/input.actual.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
part1: 4111
2+
part2: 4917

2021/day18/input.actual.txt

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
[[7,[8,[3,5]]],[[[3,6],9],1]]
2+
[[[[1,7],8],[0,4]],[[[0,9],2],[2,[5,6]]]]
3+
[[[4,[4,4]],[8,[4,4]]],[[5,4],6]]
4+
[[[1,[1,3]],[[9,6],1]],[[4,[5,4]],[[4,4],[0,8]]]]
5+
[[[[6,2],[2,5]],[2,1]],[[1,5],7]]
6+
[[[[5,0],[2,7]],[[2,5],2]],[2,[3,2]]]
7+
[[[6,[6,6]],[0,[2,8]]],[[8,[5,6]],[4,5]]]
8+
[[[6,[3,5]],8],[[2,[7,0]],5]]
9+
[[[[7,8],[3,6]],[1,6]],[[[4,2],1],[[0,7],[5,6]]]]
10+
[8,2]
11+
[[5,5],[[2,[9,1]],8]]
12+
[[[4,8],[[1,8],2]],[9,2]]
13+
[2,[[8,[8,3]],[0,6]]]
14+
[[[6,6],[[6,0],6]],[0,[[3,4],3]]]
15+
[[[[2,9],[5,9]],[2,[4,3]]],[6,0]]
16+
[[[6,2],0],[7,7]]
17+
[[[9,6],5],[2,[[0,1],[5,5]]]]
18+
[[6,[[0,1],[5,1]]],5]
19+
[4,[[[4,2],3],[2,[5,0]]]]
20+
[[[7,9],2],2]
21+
[[[5,[2,1]],1],[[1,1],[8,5]]]
22+
[[[[5,9],0],[[1,9],0]],4]
23+
[[7,[0,5]],[[0,3],[8,2]]]
24+
[[6,[9,[7,7]]],6]
25+
[2,[[1,[1,0]],[4,[6,1]]]]
26+
[[0,6],[[[5,1],6],[[4,7],[8,0]]]]
27+
[[[1,[4,7]],[0,[1,2]]],[[1,1],[[1,2],[1,3]]]]
28+
[[8,[3,0]],[3,[1,[8,1]]]]
29+
[[[7,[4,0]],[[8,7],2]],[[7,[7,3]],7]]
30+
[3,[[1,7],2]]
31+
[8,[[[1,5],0],1]]
32+
[[6,[1,4]],7]
33+
[[[[2,6],2],8],1]
34+
[9,7]
35+
[9,[[[1,1],1],[[3,0],[7,3]]]]
36+
[[[[8,5],7],[[5,1],[6,4]]],[4,[[7,6],[2,7]]]]
37+
[[[[8,7],1],0],[[9,9],[[1,0],8]]]
38+
[[9,[[1,1],7]],[[3,0],4]]
39+
[[[[8,2],4],[9,[7,9]]],[[0,2],[[3,0],5]]]
40+
[[[[3,6],3],[[9,7],[0,6]]],[[[4,9],[1,3]],[2,[7,3]]]]
41+
[[[[3,8],0],[[3,6],5]],[[3,[4,2]],[[6,1],[8,5]]]]
42+
[[2,7],[[0,0],8]]
43+
[[[[0,3],7],[2,0]],3]
44+
[[0,2],[[[3,1],0],[0,0]]]
45+
[[[[6,1],7],[[8,4],[2,4]]],[[1,6],[2,3]]]
46+
[[2,[2,[9,1]]],[4,[[0,4],9]]]
47+
[[[3,[5,6]],7],1]
48+
[[[3,0],[8,[9,3]]],[[[1,1],1],[6,7]]]
49+
[[[6,[4,4]],[[1,9],1]],[[[8,1],[9,8]],[[6,3],1]]]
50+
[[[5,8],[[0,3],[1,7]]],[[[3,2],[4,7]],1]]
51+
[[5,5],[[[8,3],[6,6]],2]]
52+
[[[[1,9],[8,5]],[[7,7],8]],[0,[8,[7,4]]]]
53+
[[6,[4,[4,3]]],[5,[6,[7,2]]]]
54+
[[0,[[9,0],0]],5]
55+
[[[[5,6],[1,3]],[[0,5],[7,5]]],[[[0,4],[3,6]],[8,[3,6]]]]
56+
[[3,[[4,7],[7,0]]],[[[4,1],5],[[6,6],[7,4]]]]
57+
[[[[4,3],[0,1]],[[7,3],[2,3]]],[[[3,7],[2,2]],[6,5]]]
58+
[[1,1],[[[1,4],6],[6,[3,9]]]]
59+
[[[[0,8],[2,0]],5],[4,[[6,1],[2,1]]]]
60+
[[7,[3,[7,2]]],[[7,9],8]]
61+
[[[4,[9,8]],[8,[3,2]]],[7,9]]
62+
[[[4,[4,2]],[5,[0,3]]],[[[4,9],[2,9]],[[1,5],[0,8]]]]
63+
[[1,[[9,8],0]],[5,[[4,3],5]]]
64+
[[[[5,1],3],[[2,9],[9,0]]],[1,[6,3]]]
65+
[[[6,4],[6,1]],7]
66+
[[4,8],[[7,2],6]]
67+
[[[5,[4,8]],[[1,7],[2,8]]],[6,[[8,4],[3,5]]]]
68+
[[5,8],[[[4,0],[6,0]],[5,[6,0]]]]
69+
[[3,[[5,3],8]],[8,5]]
70+
[[[2,6],[1,[2,3]]],[[[1,7],[5,7]],[[0,0],[0,5]]]]
71+
[[0,[[4,3],[3,6]]],[[2,[6,6]],[0,[2,9]]]]
72+
[[[5,9],[6,2]],[[7,6],8]]
73+
[[9,2],[1,[[0,5],[5,0]]]]
74+
[[[3,1],[9,3]],3]
75+
[[[[2,0],[4,2]],6],[[[5,2],[7,8]],[[0,7],3]]]
76+
[[7,[[3,9],[6,3]]],[2,[[6,4],3]]]
77+
[[5,[3,[7,4]]],[[2,5],[0,9]]]
78+
[3,7]
79+
[[3,9],[[[4,4],9],[[3,1],7]]]
80+
[[[[4,0],1],[8,[3,6]]],[[9,[4,4]],[[9,9],9]]]
81+
[[8,[[8,1],5]],[[[9,1],4],[[8,5],3]]]
82+
[[6,[[6,3],[3,7]]],4]
83+
[[[1,[0,8]],9],[[8,5],[3,[0,5]]]]
84+
[[[[3,1],0],[[8,5],[1,0]]],[0,2]]
85+
[[2,[4,7]],2]
86+
[[[2,0],[2,2]],4]
87+
[4,[[5,8],5]]
88+
[[[2,[0,5]],[[3,3],[6,6]]],1]
89+
[[[2,[2,4]],[5,[7,1]]],[3,5]]
90+
[[2,[9,[3,9]]],9]
91+
[[[7,[7,1]],[[5,2],1]],[[2,1],[9,[7,3]]]]
92+
[[4,[4,6]],4]
93+
[[[4,2],[9,[3,8]]],[[2,4],0]]
94+
[[[7,[0,3]],4],[[[1,8],4],[[5,1],1]]]
95+
[[[1,3],3],[[4,9],[[0,0],6]]]
96+
[[[4,1],0],[[[5,6],[0,8]],[[2,1],1]]]
97+
[[3,[3,[7,9]]],[[[6,8],8],[[7,9],3]]]
98+
[[4,[[1,6],[4,6]]],[[1,8],[3,8]]]
99+
[[[[5,9],2],[[6,7],4]],3]
100+
[[[[2,1],[1,9]],7],[[[0,9],[0,5]],[[2,5],[5,0]]]]

2021/day18/input.example.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
part1: 4140
2+
part2: 3993

2021/day18/input.example.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
2+
[[[5,[2,8]],4],[5,[[9,9],0]]]
3+
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
4+
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
5+
[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]
6+
[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]
7+
[[[[5,4],[7,7]],8],[[8,3],8]]
8+
[[9,3],[[9,9],[6,[4,9]]]]
9+
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
10+
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]

0 commit comments

Comments
 (0)