Advertisement
mscha

AoC 2016 day 24 - second version

Dec 24th, 2016
555
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 6 4.09 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 TARGET = '★' but True;
  8. constant PATH = '·' but True;
  9.  
  10. class Pos
  11. {
  12.     has Int $.x;
  13.     has Int $.y;
  14.  
  15.     method Str { "<$!x,$!y>" }
  16.     method gist { self.Str }
  17.  
  18.     method infix:<eq>(Pos $a, Pos $b) { $a.x == $b.x && $a.y == $b.y }
  19.  
  20.     method WHICH { "Pos|$!x|$!y" }
  21. }
  22.  
  23. sub pos($x,$y) { Pos.new(:$x,:$y) }
  24.  
  25. class Maze
  26. {
  27.     has $.width;
  28.     has $.height;
  29.     has @.grid;
  30.     has @.target;
  31.     has %.target-at;
  32.  
  33.     submethod BUILD(:@lines)
  34.     {
  35.         for @lines.kv -> $y, $line {
  36.             @!grid.push: $line.comb.map({ when '#' { WALL }; when '.' { SPACE }; when '0'..'9' { TARGET }; });
  37.  
  38.             for $line.match(/ <[0..9]> /, :g) {
  39.                 my $pos = pos($_.from, $y);
  40.                 @!target[$_] = $pos;
  41.                 %!target-at{$pos} = +$_;
  42.             }
  43.         }
  44.  
  45.         $!width = +@!grid[0;*];
  46.         $!height = +@!grid[*;0];
  47.     }
  48.  
  49.     #| Determine the contents of cell <$x,$y> with an optional path
  50.     multi method cell(Int $x, Int $y, $path=())
  51.     {
  52.         # Don't go outside the boundaries
  53.         return WALL unless $!width > $x >= 0 && $!height > $y >= 0;
  54.  
  55.         # Check if we're on the path
  56.         return PATH if $path.elems && pos($x,$y) eq any(@$path);
  57.  
  58.         # Return the cell
  59.         return @!grid[$y;$x];
  60.     }
  61.  
  62.     #| Determine the contents of cell $p in the maze with an optional $path
  63.     multi method cell(Pos $p, $path=()) { self.cell($p.x, $p.y, $path); }
  64.  
  65.     #| Draw the maze
  66.     method draw()
  67.     {
  68.         for ^$!height -> $y {
  69.             say (^$!width).map({ self.cell($_, $y) }).join;
  70.         }
  71.     }
  72.  
  73.     #| Draw the maze with a given path
  74.     method draw-path($path)
  75.     {
  76.         for ^$!height -> $y {
  77.             say (^$!width).map({ self.cell($_, $y, $path) }).join;
  78.         }
  79.     }
  80.  
  81.     #| Find a path from $from to $to
  82.     method find-path(Pos $from, Pos $to)
  83.     {
  84.         my $visited = SetHash.new;
  85.         my @moves = [[$from, [],],];
  86.  
  87.         while @moves {
  88.             # Try the next move in the queue
  89.             my ($pos, $path0) = @moves.shift;
  90.             my @path = |$path0[*], $pos;
  91.  
  92.             # Are we there yet?
  93.             return @path if $pos eq $to;
  94.  
  95.             # Add possible moves from this location
  96.             for pos($pos.x+1,$pos.y), pos($pos.x,$pos.y+1),
  97.                         pos($pos.x-1,$pos.y), pos($pos.x,$pos.y-1) -> $new {
  98.                 if self.cell($new) && $new$visited {
  99.                     @moves.push([$new, @path]);
  100.                     $visited{$new} = True;
  101.                 }
  102.             }
  103.         }
  104.  
  105.         # No moves remailing, give up.
  106.         return ();
  107.     }
  108. }
  109.  
  110. sub MAIN(IO() $inputfile where *.f, Bool :v(:$verbose)=False)
  111. {
  112.     my $maze = Maze.new(:lines($inputfile.lines));
  113.     $maze.draw if $verbose;
  114.     say '' if $verbose;
  115.  
  116.     # Create a table of all distances between targets
  117.     my @distance;
  118.     for $maze.target.keys.combinations(2) -> ($t1, $t2) {
  119.         my @path = $maze.find-path($maze.target[$t1], $maze.target[$t2]);
  120.         say "Distance from target $t1 to target $t2: { @path - 1 } steps" if $verbose;
  121.         @distance[$t1;$t2] = @distance[$t2;$t1] = @path-1;
  122.     }
  123.  
  124.     # Calculate distance for all permutations of targets
  125.     my %total-distance;
  126.     my %return-distance;
  127.     for $maze.target.keys[1..*].permutations -> @p {
  128.         my $key = @p.join('-');
  129.         %total-distance{$key} = (0,|@p).rotor(2=>-1).map({ @distance[$_[0];$_[1]] }).sum;
  130.         %return-distance{$key} = %total-distance{$key} + @distance[@p[*-1];0];
  131.     }
  132.  
  133.     # Find (one of) the best path(s), both without and with return to target 0
  134.     my $best-total = %total-distance.keys.sort({ %total-distance{$_} })[0];
  135.     my $best-return = %return-distance.keys.sort({ %return-distance{$_} })[0];
  136.  
  137.     say '' if $verbose;
  138.     say "The shortest path without return is 0-{$best-total}; %total-distance{$best-total} steps.";
  139.     say "The shortest path with return is 0-{$best-return}-0; %return-distance{$best-return} steps.";
  140. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement