Advertisement
musifter

AoC 2022, day 22 (Perl part 2)

Dec 22nd, 2022 (edited)
1,988
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 4.91 KB | Source Code | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use List::AllUtils      qw(firstidx pairwise);
  7.  
  8. $/ = '';    # read in paragraph mode
  9.  
  10. # definition of vetor sum
  11. sub vec_sum ($$) { my ($v, $w) = @_; return ([pairwise {$a + $b} @$v, @$w]) }
  12.  
  13. # directions in widdershins order from facing East
  14. my @dirs = ([0,1], [1,0], [0,-1], [-1,0]);
  15.  
  16. # Slurp input, break up command list
  17. my @input = map { [split "\n"] } <>;
  18. my @cmds  = ($input[1][0] =~ m#(\d+|[LR])#g);
  19.  
  20. # ASSUME: Net has a specific layout, top line is 3 faces wide
  21. my @grid = $input[0]->@*;
  22. my $size = length($input[0][0]) / 3;
  23.  
  24. # Assumed Layout:
  25. #    .WB
  26. #    .R.
  27. #    GY.
  28. #    O..
  29. my %sides = ( 'white'  => [0,1], 'blue'  => [0,2], 'red'    => [1,1],
  30.               'yellow' => [2,1], 'green' => [2,0], 'orange' => [3,0] );
  31.  
  32. # Magnify by size to get the net coord of upper left
  33. %sides = map { $_ => [map {$_ * $size} $sides{$_}->@*] } keys %sides;
  34.  
  35. # Grab the cube face data out of the grid (and convert to list).
  36. # Add sentinels to right and bottom edges.
  37. my %cube;
  38. foreach my $side (keys %sides) {
  39.     my @p = $sides{$side}->@*;
  40.     $cube{$side} = [ map {
  41.                         [split( //, substr($grid[$_], $p[1], $size) . '*' )]
  42.                      } ($p[0] .. $p[0] + $size - 1)
  43.                    ];
  44.     push( $cube{$side}->@*, [split(//, '*' x ($size + 1))] );
  45. }
  46.  
  47. # Table for wrapping around the cube:
  48. # Array index is starting dir, facing is resulting dir on target side.
  49. my %wrap;
  50. $wrap{white}[0]  = { side => 'blue',   facing => 0 };
  51. $wrap{white}[1]  = { side => 'red',    facing => 1 };
  52. $wrap{white}[2]  = { side => 'green',  facing => 0 };
  53. $wrap{white}[3]  = { side => 'orange', facing => 0 };
  54.  
  55. $wrap{red}[0]    = { side => 'blue',   facing => 3 };
  56. $wrap{red}[1]    = { side => 'yellow', facing => 1 };
  57. $wrap{red}[2]    = { side => 'green',  facing => 1 };
  58. $wrap{red}[3]    = { side => 'white',  facing => 3 };
  59.  
  60. $wrap{blue}[0]   = { side => 'yellow', facing => 2 };
  61. $wrap{blue}[1]   = { side => 'red',    facing => 2 };
  62. $wrap{blue}[2]   = { side => 'white',  facing => 2 };
  63. $wrap{blue}[3]   = { side => 'orange', facing => 3 };
  64.  
  65. $wrap{yellow}[0] = { side => 'blue',   facing => 2 };
  66. $wrap{yellow}[1] = { side => 'orange', facing => 2 };
  67. $wrap{yellow}[2] = { side => 'green',  facing => 2 };
  68. $wrap{yellow}[3] = { side => 'red',    facing => 3 };
  69.  
  70. $wrap{orange}[0] = { side => 'yellow', facing => 3 };
  71. $wrap{orange}[1] = { side => 'blue',   facing => 1 };
  72. $wrap{orange}[2] = { side => 'white',  facing => 1 };
  73. $wrap{orange}[3] = { side => 'green',  facing => 3 };
  74.  
  75. $wrap{green}[0]  = { side => 'yellow', facing => 0 };
  76. $wrap{green}[1]  = { side => 'orange', facing => 1 };
  77. $wrap{green}[2]  = { side => 'white',  facing => 0 };
  78. $wrap{green}[3]  = { side => 'red',    facing => 0 };
  79.  
  80. # Returns the (side, dir, pos) resulting plus a boolean if we hit a wall.
  81. sub wrap_side {
  82.     my ($side, $dir, $pos) = @_;
  83.  
  84.     my $new_side = $wrap{$side}[$dir]{side};
  85.     my $new_dir  = $wrap{$side}[$dir]{facing};
  86.  
  87.     # Only one coord value is important, the other is either -1 or $side
  88.     # Because of dir order, parity tells us which of y or x we want.
  89.     my $idx = $pos->[$dir % 2];
  90.  
  91.     # When going between 0 and 2 facing, the edge flips
  92.     if ($dir % 2 == 0 && $new_dir % 2 == 0 && $dir != $new_dir) {
  93.         $idx = $size - $idx - 1;
  94.     }
  95.  
  96.     # $new_pos similarly has one coord as 0 or $size-1, the other
  97.     # being set to the index value, based on the direction parity.
  98.     my $new_pos = ($new_dir <= 1) ? [0,0] : [$size-1,$size-1];
  99.     $new_pos->[$new_dir % 2] = $idx;
  100.  
  101.     # Check if wrapping immediately hit a wall, back out, and report
  102.     if ($cube{$new_side}[$new_pos->[0]][$new_pos->[1]] eq '#') {
  103.         return ($side, $dir, $pos, 1);
  104.     } else {
  105.         return ($new_side, $new_dir, $new_pos, 0);
  106.     }
  107. }
  108.  
  109. #
  110. # Mainline
  111. #
  112. my $side = 'white';
  113. my $pos  = [0, firstidx {$_ eq '.'} $cube{$side}[0]->@*];
  114. my $face = 0;
  115.  
  116. foreach my $cmd (@cmds) {
  117.     if ($cmd eq 'R') {
  118.         $face = ($face + 1) % 4;
  119.     } elsif ($cmd eq 'L') {
  120.         $face = ($face + 3) % 4;
  121.     } else {
  122.         # Step cmd number of times
  123.         for (my $i = 0; $i < $cmd; $i++) {
  124.             my $next = vec_sum( $pos, $dirs[$face] );
  125.  
  126.             if ($cube{$side}[$next->[0]][$next->[1]] eq '.') {      # good, step
  127.                 $pos = $next;
  128.             } elsif ($cube{$side}[$next->[0]][$next->[1]] eq '#') { # wall, stop
  129.                 last;
  130.             } else {  # we hit a '*' sentinel
  131.                 my $wall;
  132.                 ($side, $face, $next, $wall) = &wrap_side( $side, $face, $pos );
  133.                 last if ($wall);    # wall, don't set and stop moving
  134.                 $pos = $next;       # good, step ahead
  135.             }
  136.         }
  137.     }
  138. }
  139.  
  140. print "Side: $side, facing: $face, pos: ($pos->[0], $pos->[1])\n";
  141.  
  142. my $net = vec_sum( $sides{$side}, $pos );
  143. print "Part 2: ", 1000 * ($net->[0] + 1) + 4 * ($net->[1] + 1) + $face, "\n";
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement