Advertisement
mscha

AoC 2016 day 13

Dec 16th, 2016
383
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 6 4.51 KB | None | 0 0
  1. #!/usr/bin/env perl6
  2.  
  3. use v6.c;
  4.  
  5. constant WALL = '█' but False;
  6. constant SPACE = '░' but True;
  7. constant PATH = '·' but True;
  8.  
  9. class Pos
  10. {
  11.     has $.x;
  12.     has $.y;
  13.  
  14.     method Str { "<$!x,$!y>" }
  15.     method gist { self.Str }
  16.  
  17.     method infix:<eq>(Pos $a, Pos $b) { $a.x == $b.x && $a.y == $b.y }
  18.  
  19.     method WHICH { "Pos|$!x|$!y" }
  20. }
  21.  
  22. sub pos($x,$y) { Pos.new(:$x,:$y) }
  23.  
  24. class Maze
  25. {
  26.     has $.id;
  27.     has @!grid;
  28.  
  29.     #| Determine the contents of cell <$x,$y> with an optional path
  30.     multi method cell(Int $x, Int $y, $path=())
  31.     {
  32.         # Don't go outside the boundaries
  33.         return WALL unless $x >= 0 && $y >= 0;
  34.  
  35.         # Check if we're on the path
  36.         return PATH if $path.elems && pos($x,$y) eq any(@$path);
  37.  
  38.         # Calculate cell value if not yet cached, and return it
  39.         @!grid[$x;$y] //= ($x*$x + 3*$x +2*$x*$y + $y + $y*$y + $!id)\
  40.                             .base(2).comb('1').elems %% 2 ?? SPACE !! WALL;
  41.         return @!grid[$x;$y];
  42.     }
  43.  
  44.     #| Determine the contents of cell $p in the maze with an optional $path
  45.     multi method cell(Pos $p, $path=()) { self.cell($p.x, $p.y, $path); }
  46.  
  47.     #| Draw the maze up to a given width and height
  48.     method draw(Int $width, Int $height)
  49.     {
  50.         for ^$height -> $y {
  51.             say (^$width).map({ self.cell($_, $y) }).join;
  52.         }
  53.     }
  54.  
  55.     #| Draw the maze with a given path
  56.     method draw-path($path)
  57.     {
  58.         my $width = $path».x.max + 2;
  59.         my $height = $path».y.max + 2;
  60.  
  61.         for ^$height -> $y {
  62.             say (^$width).map({ self.cell($_, $y, $path) }).join;
  63.         }
  64.     }
  65.  
  66.     #| Find a path from $from to $to
  67.     method find-path(Pos $from, Pos $to)
  68.     {
  69.         my $visited = SetHash.new;
  70.         my @moves = [[$from, [],],];
  71.  
  72.         while @moves {
  73.             # Try the next move in the queue
  74.             my ($pos, $path0) = @moves.shift;
  75.             my @path = |$path0[*], $pos;
  76.             $visited{$pos} = True;
  77.  
  78.             # Are we there yet?
  79.             return @path if $pos eq $to;
  80.  
  81.             # Add possible moves from this location
  82.             for pos($pos.x+1,$pos.y), pos($pos.x,$pos.y+1),
  83.                         pos($pos.x-1,$pos.y), pos($pos.x,$pos.y-1) -> $new {
  84.                 if self.cell($new) && $new$visited {
  85.                     @moves.push([$new, @path]);
  86.                 }
  87.             }
  88.         }
  89.  
  90.         # No moves remailing, give up.
  91.         return ();
  92.     }
  93.  
  94.     #| Find all positions that can be reached from $from in $max-moves moves
  95.     method find-positions(Pos $from, Int $max-moves)
  96.     {
  97.         my $visited = SetHash.new;
  98.         my @moves = [[$from, [],],];
  99.  
  100.         while @moves {
  101.             # Try the next move in the queue
  102.             my ($pos, $path0) = @moves.shift;
  103.             my @path = |$path0[*], $pos;
  104.             $visited{$pos} = True;
  105.  
  106.             # Are we done yet?
  107.             next if @path.elems - 1 == $max-moves;
  108.  
  109.             # Add possible moves from this location
  110.             for pos($pos.x+1,$pos.y), pos($pos.x,$pos.y+1),
  111.                         pos($pos.x-1,$pos.y), pos($pos.x,$pos.y-1) -> $new {
  112.                 if self.cell($new) && $new$visited {
  113.                     @moves.push([$new, @path]);
  114.                 }
  115.             }
  116.         }
  117.  
  118.         # Return all visited positions
  119.         return $visited.keys;
  120.     }
  121. }
  122.  
  123. #| Solve maze with ID from input file
  124. multi sub MAIN(IO() $inputfile where *.f,
  125.                Int :$x = 31, Int :$y = 39,
  126.                Int :$max-moves = 50)
  127. {
  128.     my ($maze-id) = $inputfile.words;
  129.     MAIN($maze-id, :$x, :$y, :$max-moves);
  130. }
  131.  
  132. #| Solve maze with ID on command line
  133. multi sub MAIN(Str $maze-id where !*.IO.f,
  134.                Int :$x = 31, Int :$y = 39,
  135.                Int :$max-moves = 50)
  136. {
  137.     my $maze = Maze.new(:id($maze-id));
  138.  
  139.     say '';
  140.     say 'Part 1:';
  141.  
  142.     my $from = pos(1,1);
  143.     my $to = pos($x,$y);
  144.     my @path = $maze.find-path($from, $to);
  145.     if @path {
  146.         say "Shortest path from $from to $to: {@path.elems-1} moves.";
  147.         say "";
  148.         $maze.draw-path(@path);
  149.     }
  150.     else {
  151.         say "Sorry, can't find a path from $from to $to!";
  152.     }
  153.  
  154.     say '';
  155.     say 'Part 2:';
  156.  
  157.     my @pos = $maze.find-positions($from, $max-moves);
  158.     if @pos {
  159.         say "Number of reachable positions in $max-moves moves: @pos.elems().";
  160.         say "";
  161.         $maze.draw-path(@pos);
  162.     }
  163.     else {
  164.         say "Sorry, can't find any positions from $from in $max-moves moves!";
  165.     }
  166. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement