Advertisement
musifter

AoC day 8, Perl with curses

Dec 8th, 2020
1,156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 3.43 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use Curses;
  7. use Time::HiRes  qw(sleep);
  8.  
  9. my $DELAY = 0.01;
  10.  
  11. # Read code file
  12. my @code = map { chomp; [split] } <>;
  13.  
  14. # Init curses - draw initial display
  15. my $cur = new Curses;
  16.  
  17. Curses::initscr();
  18. $cur->clear();
  19.  
  20. $cur->addstr( 1, 10, "Part 1: " );
  21. $cur->addstr( 3, 10, "Part 2: " );
  22. foreach (my $i = 0; $i * 50 < $#code; $i++) {
  23.     $cur->addstr( 5 + $i, 5, sprintf( "%3s", $i * 50 ) );
  24.     $cur->addstr( 5 + $i, 10, '.' x ((($i + 1) * 50 > $#code) ? $#code % 50
  25.                                                               : 50) );
  26. }
  27. $cur->refresh();
  28.  
  29. sub mark_code {
  30.     my ($inst, $char) = @_;
  31.  
  32.     $cur->addch( 5 + int( $inst / 50 ), 10 + $inst % 50, $char );
  33.     $cur->refresh();
  34.     sleep( $DELAY );
  35. }
  36.  
  37. #
  38. # Virtual Machine
  39. #
  40.  
  41. # Registers:
  42. my ($acc, $ip);
  43. my @ran;
  44.  
  45. my %op_codes = (
  46.     'acc' => sub { $acc += shift; $ip++ },
  47.     'jmp' => sub { $ip  += shift; },
  48.     'nop' => sub { $ip++ },
  49. );
  50.  
  51. sub run_vm {
  52.     my $line = shift;
  53.  
  54.     $acc = 0;
  55.     $ip  = 0;
  56.  
  57.     @ran = map { 0 } (0 .. $#code);
  58.  
  59.     while (1) {
  60.         last  if ($ip == $#code);
  61.         last  if (++$ran[$ip] == 2);
  62.  
  63.         &mark_code( $ip, ($line == 1) ? '#' : '+' );
  64.         &{$op_codes{ $code[$ip][0] }}( $code[$ip][1] );
  65.  
  66.         $cur->addstr( $line, 19, "$acc   " );
  67.         $cur->refresh();
  68.  
  69.         sleep( $DELAY );
  70.     }
  71.  
  72.     $cur->addstr( $line, 25,
  73.                   sprintf( "%35s", (($ip == $#code) ? "Stopped"
  74.                                                     : "Looped") . " at $ip" ));
  75.  
  76.     return ($acc);
  77. }
  78.  
  79. $cur->addstr( 1, 19, &run_vm(1) . "  " );
  80. $cur->refresh();
  81.  
  82. #
  83. #  Analyse code
  84. #
  85.  
  86. # Find start of block leading up to a jump
  87. sub find_block_start {
  88.     my $inst = shift;
  89.  
  90.     do {
  91.         $inst--;
  92.     } while ($inst > 0 && ($code[$inst][0] ne 'jmp' || $code[$inst][1] == 1));
  93.  
  94.     return ($inst + 1);
  95. }
  96.  
  97. # Returns a list of jumps that jump into a block
  98. sub backtrack {
  99.     my ($start, $end) = @_;
  100.     my @ret  = ();
  101.  
  102.     for (my $i = 0; $i < $#code; $i++) {
  103.         next if ($i >= $start && $i <= $end);
  104.  
  105.         if ($code[$i][0] eq 'jmp') {
  106.             my $next = $i + $code[$i][1];
  107.             if ($next >= $start && $next <= $end) {
  108.                 push( @ret, $i );
  109.             }
  110.         }
  111.     }
  112.  
  113.     return (@ret);
  114. }
  115.  
  116. # Find the magic sequence of code locations whose execution leads to the end.
  117. my @seq = map { 0 } (0 .. $#code);
  118. my @jobs = ($#code);
  119.  
  120. $cur->addstr( 2, 10, "Analysing..." );
  121. while (my $targ = shift @jobs) {
  122.     my $start = &find_block_start( $targ );
  123.     foreach my $i ($start .. $targ) {
  124.         $seq[$i] = 1;
  125.         &mark_code( $i, '>' );
  126.     }
  127.     $cur->refresh();
  128.     sleep( $DELAY );
  129.  
  130.     push( @jobs, &backtrack( $start, $targ ) );
  131. }
  132.  
  133. # Look for a jmp/nop that can be toggled to get onto the magic sequence
  134. foreach my $i (0 .. $#code) {
  135.     next if (!$ran[$i] || $code[$i][0] eq 'acc');
  136.  
  137.     if ($code[$i][0] eq 'jmp' && $seq[$i + 1]) {
  138.         $code[$i][0] = 'nop';
  139.  
  140.         $cur->addstr( 2, 25, sprintf( "%35s", "Changing $i to nop" ) );
  141.         &mark_code( $i, '*' );
  142.         last;
  143.     } elsif ($code[$i][0] eq 'nop' && $seq[$i + $code[$i][1]]) {
  144.         $code[$i][0] = 'jmp';
  145.  
  146.         $cur->addstr( 2, 25, sprintf( "%35s", "Changing $i to jmp" ) );
  147.         &mark_code( $i, '*' );
  148.         last;
  149.     }
  150. }
  151.  
  152. $cur->addstr( 3, 19, &run_vm(3) . "  " );
  153. $cur->refresh();
  154.  
  155. Curses::endwin();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement