Advent of Intcode update day 5

Day 5 saw indeed another use if Intcode, so i did not prepare the module in vain. But the new tasks added some annoying features, which made it necessary to rewrite a lot of the code.

Besides adding a bunch of opcodes (which would have been easy to implement), day 5 added parameter modes. Previously, the arguments to an opcode always pointed to other locations in the code. Now, an opcode can have different modes per argument, where the new mode ( immediate mode, represented by 1 in the arglist) needs you to just fetch the literal value stored at the location, instead of using the value as the index and fetch the value from there (which is now called position mode and is still the default).

This made my fancy get_n method very ineffective, so I ditched it for some actually simpler get and set methods. The op 1 (add) changed from:

sub op_1 ($self) {
    my ( $x, $y, $t ) = $self->get_n( 3 );
    $self->code->[$t] = $self->code->[$x] + $self->code->[$y];
}

to

sub op_1 ($self) { # add
    $self->set($self->get + $self->get);
}

get now gets the next argument according to the parameter mode and increments pos. set just stores the value at the location indicated by the current pos (writes never use immediate mode!)

New helpers

sub get ($self) {
    my $mode = shift(@{$self->modes}) || 0;
    my $pointer = $self->code->[$self->read_pos];
    return $mode ? $pointer : $self->code->[ $pointer];
}

This method gets the current mode, defaulting to 0. Then it calls read_pos to get and increment the position, and fetches the value from the code. Depending on the mode, it either returns the value (immediate mode) or returns whatever is at the position pointed to by the value.

sub set ($self, $val) {
    $self->code->[ $self->code->[$self->read_pos] ] = $val;
}

set is rather boring, it just stores the value into the field pointed to by the value at the current position.

sub read_pos ($self) {
    my $pos = $self->pos;
    $self->pos($pos + 1);
    return $pos;
}

I put the whole pos handling into a small helper method. It's a bit ugly and the opposite of side-effect free ;-)

Parsing the input

I'm not very happy with the input parser, but was not motivated enough to clean it up:

my $raw = $self->code->[ $self->read_pos ];
 chomp($raw);
 my @modes= split(//,$raw);

 my $op = join('',reverse (grep {$_} (pop(@modes),pop(@modes))));
 @modes = reverse(@modes);
 $op=~s/^0//;
 chomp($op);
 my $method = 'op_' . $op;

 $self->modes(\@modes);

Handling the input (01006) as an array is overly complex, but was the easiest way (for me) to "parse" the data...

Ops are cleaner

On the plus side, the implementation of the various op-codes now is much cleaner:

sub op_2 ($self) { # multiply
    $self->set($self->get * $self->get);
}
sub op_5 ($self) { # jump-if-true
    my $pos = $self->pos;
    my $check = $self->get;
    my $target = $self->get;
    if ($check != 0) {
        $self->pos($target);
    }
}

You can view the whole beast here