Created
December 12, 2024 14:16
-
-
Save mscha/f46c4a834724fcf0af618732f1787431 to your computer and use it in GitHub Desktop.
Advent of Code 2024 day 12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env raku | |
use v6.d; | |
$*OUT.out-buffer = False; # Autoflush | |
# Advent of Code 2024 day 12 -- https://adventofcode.com/2024/day/12 | |
enum Direction <north east south west>; | |
sub left(Direction $d --> Direction) { Direction(($d - 1) % 4) } | |
sub right(Direction $d --> Direction) { Direction(($d + 1) % 4) } | |
sub turn(Direction $d --> Direction) { Direction(($d + 2) % 4) } | |
class Position | |
{ | |
has Int $.x; | |
has Int $.y; | |
my Int %dx{Direction} = north, 0, east,1, south,0, west,-1; | |
my Int %dy{Direction} = north,-1, east,0, south,1, west, 0; | |
method step(Direction $d) { pos($!x+%dx{$d}, $!y+%dy{$d}) } | |
method neighbours { Direction::.values.map({ self.step($_) }) } | |
method dir-neighbours { Direction::.values.map({ $_ => self.step($_) }) } | |
method Str { "($!x,$!y)" } | |
method gist { self.Str } | |
method WHICH { ValueObjAt.new("Position|$!x|$!y") } | |
} | |
multi sub pos(Int() $x, Int() $y) { Position.new(:$x, :$y) } | |
multi sub pos(@xy) { pos(@xy[0], @xy[1]) } | |
class GardenMap | |
{ | |
has @.grid; | |
has $!height = @!grid.elems; | |
has $!width = @!grid[0].elems; | |
has $!num-regions; | |
has %!region; | |
has @!type; | |
has @!pos; | |
has @!perimeter; | |
has @!area; | |
has @!fences; | |
has @!sides; | |
method plant-type($pos) | |
{ | |
return '' unless 0 ≤ $pos.x < $!width && 0 ≤ $pos.y < $!height; | |
return @!grid[$pos.y;$pos.x]; | |
} | |
submethod TWEAK | |
{ | |
# Go through the grid and identify regions | |
my $region-id = -1; | |
$!num-regions = 0; | |
for (^$!width X ^$!height).map({ pos($_) }) -> $pos { | |
# Already found the region? Don't bother | |
next if %!region{$pos}:exists; | |
# Assign region ID | |
%!region{$pos} = ++$region-id; | |
$!num-regions++; | |
# Keep track of positions and plant type of the region | |
@!pos[$region-id].append($pos); | |
my $type = self.plant-type($pos); | |
@!type[$region-id] //= $type; | |
# Check this position, and its neighbours with the same plant type, | |
# and its neighbours etc., and assign the region if necessary | |
my @check = $pos; | |
while @check.shift -> $p { | |
for $p.neighbours -> $n { | |
next if self.plant-type($n) ne $type; | |
next if %!region{$n}:exists; | |
%!region{$n} = $region-id; | |
@check.append($n); | |
} | |
} | |
} | |
# Go through the grid again, detect fences, calculate the perimeter | |
# and area of each region | |
for (^$!width X ^$!height).map({ pos($_) }) -> $pos { | |
my $type = self.plant-type($pos); | |
my $region = %!region{$pos}; | |
# Check neighbours | |
for $pos.dir-neighbours -> $dn { | |
my $dir = $dn.key; | |
my $n = $dn.value; | |
if self.plant-type($n) eq $type { | |
# Same type of plant: add to region | |
%!region{$n} //= $region; | |
} | |
else { | |
# Different type of plant: | |
# store fence and increment region perimeter | |
@!fences[$region] //= :{}; # ensure object hash | |
@!fences[$region]{$dir} //= :{}; # ensure object hash | |
@!fences[$region]{$dir}{$pos} = True; | |
@!perimeter[$region]++; | |
} | |
} | |
# Increment region area | |
@!area[$region]++; | |
} | |
# Finally, calculate the sides of each region | |
for ^$!num-regions -> $region-id { | |
# Count all fences that don't have a fence to the left of it | |
for @!fences[$region-id].kv -> $dir, $fences { | |
for $fences.keys -> $p { | |
@!sides[$region-id]++ unless $fences{$p.step(left($dir))}; | |
} | |
} | |
} | |
} | |
method total-fence-price { (@!area Z× @!perimeter).sum } | |
method total-discounted-fence-price { (@!area Z× @!sides).sum } | |
method Str | |
{ | |
@!type.kv.map(-> $i, $type { | |
" - A region of $type plants with area @!area[$i]," ~ | |
" perimeter @!perimeter[$i], and @!sides[$i] sides." | |
}).join("\n"); | |
} | |
method gist { self.Str } | |
} | |
sub MAIN(IO() $inputfile where *.f = 'aoc12.input', Bool :v(:$verbose) = False) | |
{ | |
my $map = GardenMap.new(:grid($inputfile.lines».comb)); | |
say $map if $verbose; | |
say "Part 1: ", $map.total-fence-price; | |
say "Part 2: ", $map.total-discounted-fence-price; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment