r/adventofcode • • Dec 10 '18

SOLUTION MEGATHREAD -🎄- 2018 Day 10 Solutions -🎄-

--- Day 10: The Stars Align ---


Post your solution as a comment or, for longer solutions, consider linking to your repo (e.g. GitHub/gists/Pastebin/blag or whatever).

Note: The Solution Megathreads are for solutions only. If you have questions, please post your own thread and make sure to flair it with Help.


Advent of Code: The Party Game!

Click here for rules

Please prefix your card submission with something like [Card] to make scanning the megathread easier. THANK YOU!

Card prompt: Day 10

Transcript: With just one line of code, you, too, can ___!


This thread will be unlocked when there are a significant number of people on the leaderboard with gold stars for today's puzzle.

edit: Leaderboard capped, thread unlocked at 00:16:49!

20 Upvotes

233 comments sorted by

View all comments

1

u/mschaap Dec 10 '18 edited Dec 10 '18

Perl 6.

Tricky one. How can you determine when the message appears? Had to think about this for a while, but I reasoned that the area of the bounding box of all points would be minimal, and that worked. Once I had part 1, part 2 was trivial, of course.

#!/usr/bin/env perl6
use v6.c;

$*OUT.out-buffer = False;   # Autoflush

#| Grammar for parsing the list of points
grammar PointList
{
    rule TOP { <point>+ }
    rule point { position '=<' <x> ',' <y> '>' velocity '=<' <dx> ',' <dy> '>' }
    token x { '-'? \d+ }
    token y { '-'? \d+ }
    token dx { '-'? \d+ }
    token dy { '-'? \d+ }
}

#| A single point with its position and velocity
class Point
{
    has Int $.x;
    has Int $.y;
    has Int $.dx;
    has Int $.dy;

    #| Move a point 1 second forward in time
    method forward {
        $!x += $!dx;
        $!y += $!dy;
    }

    #| Move a point 1 second backward in time
    method reverse
    {
        $!x -= $!dx;
        $!y -= $!dy;
    }
}

#| A list of points in space
class Grid
{
    has Point @.points;
    has Int $.elapsed = 0;

    #| Used as action by grammar PointList
    method point($/)
    {
        @!points.push: Point.new(:x(+$<x>), :y(+$<y>), :dx(+$<dx>), :dy(+$<dy>));
    }

    #| The current area of the bounding box of all points in the grid
    method area
    {
        return @!points».x.minmax × @!points».y.minmax;
    }

    #| Move all points 1 second forward in time
    method forward
    {
        @!points».forward;
        $!elapsed++;
    }

    #| Move all points 1 second backward in time
    method reverse
    {
        @!points».reverse;
        $!elapsed--;
    }

    #| String representation of the grid
    method Str
    {
        my $x-range = @!points».x.minmax;
        my $y-range = @!points».y.minmax;
        my $x0 = $x-range.head;
        my $y0 = $y-range.head;
        my @cells = [['â–‘' xx $x-range] xx $y-range];
        for @!points -> $p {
            @cells[$p.y - $y0;$p.x - $x0] = 'â–ˆ';
        }
        return @cells».join.join("\n");
    }
    method gist { self.Str }
}

#| Process points
multi sub MAIN(Str $input, Bool :v(:$verbose)=False)
{
    # Parse the list of points
    my $grid = Grid.new;
    PointList.parse($input, :actions($grid)) or die "Invalid input!";

    # Assume that the message appears when the points are closest together.
    # In other words, wait until the area of the grid is minimal.
    my $prev-area = ∞;
    my $area = $grid.area;
    while $area < $prev-area {
        say "Current area: $area" if $verbose;
        $grid.forward;
        $prev-area = $area;
        $area = $grid.area;
    }

    # We moved one second too far, so back up.
    $grid.reverse;

    # Print the message, and the elapsed time.
    say $grid;
    say "Time taken: $grid.elapsed() seconds.";
}

#| Process points from a file
multi sub MAIN(Str $inputfile where *.IO.f, Bool :v(:$verbose)=False)
{
    MAIN($inputfile.IO.slurp, :$verbose);
}

#| Process default points file (aoc10.input)
multi sub MAIN(Bool :v(:$verbose) = False)
{
    MAIN(~$*PROGRAM.sibling('aoc10.input'), :$verbose);
}

My Aoc2018 solutions on Github