Skip to content

Instantly share code, notes, and snippets.

@mscha
Created December 12, 2024 14:16
Show Gist options
  • Save mscha/f46c4a834724fcf0af618732f1787431 to your computer and use it in GitHub Desktop.
Save mscha/f46c4a834724fcf0af618732f1787431 to your computer and use it in GitHub Desktop.
Advent of Code 2024 day 12
#!/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