7 votes

Day 5: Sunny with a Chance of Asteroids

Today's problem description: https://adventofcode.com/2019/day/5


Join the Tildes private leaderboard! You can do that on this page, by entering join code 730956-de85ce0c.

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):

<details>
<summary>Part 1</summary>

```python
Your code here.
```

</details>

10 comments

  1. [2]
    markh
    (edited )
    Link
    Haven't finished yet, but wanted to comment and say woof, this one's a doozy. If anyone else out there is struggling, you aren't alone. Edit: Finished it last night. In retrospect, it wasn't...

    Haven't finished yet, but wanted to comment and say woof, this one's a doozy. If anyone else out there is struggling, you aren't alone.


    Edit: Finished it last night. In retrospect, it wasn't actually that difficult to code, but the instructions were pretty difficult to grok.

    Part 1
    const fs = require('fs');
    const m = fs.readFileSync('input.txt').toString().split(',').map(Number);
    
    let opcode = m[0], ip = 1, x = 1, result;
    
    for (let i = 0; i < m.length; i += ip) {
        if (opcode === 1) {
            m[m[i + 3]] = m[m[i + 1]] + m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 101) {
            m[m[i + 3]] = m[i + 1] + m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1001) {
            m[m[i + 3]] = m[m[i + 1]] + m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1101) {
            m[m[i + 3]] = m[i + 1] + m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 2) {
            m[m[i + 3]] = m[m[i + 1]] * m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 102) {
            m[m[i + 3]] = m[i + 1] * m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1002) {
            m[m[i + 3]] = m[m[i + 1]] * m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1102) {
            m[m[i + 3]] = m[i + 1] * m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 3) {
            m[m[i + 1]] = x;
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 103) {
            m[i + 1] = x;
            opcode = m[i + 2];
            incrementer = 2;
        } else if (opcode === 4) {
            result = m[m[i + 1]];
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 104) {
            result = m[i + 1];
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 99) {
            break;
        }
    }
    
    console.log(`Diagnostic Code: ${result}`);
    
    Part 2
    const fs = require('fs');
    const m = fs.readFileSync('input.txt').toString().split(',').map(Number);
    
    let opcode = m[0], ip = 1, x = 5, result;
    
    for (let i = 0; i < m.length; i += ip) {
        if (opcode === 1) {
            m[m[i + 3]] = m[m[i + 1]] + m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 101) {
            m[m[i + 3]] = m[i + 1] + m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1001) {
            m[m[i + 3]] = m[m[i + 1]] + m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1101) {
            m[m[i + 3]] = m[i + 1] + m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 2) {
            m[m[i + 3]] = m[m[i + 1]] * m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 102) {
            m[m[i + 3]] = m[i + 1] * m[m[i + 2]];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1002) {
            m[m[i + 3]] = m[m[i + 1]] * m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1102) {
            m[m[i + 3]] = m[i + 1] * m[i + 2];
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 3) {
            m[m[i + 1]] = x;
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 103) {
            m[i + 1] = x;
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 4) {
            result = m[m[i + 1]];
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 104) {
            result = m[i + 1];
            opcode = m[i + 2];
            ip = 2;
        } else if (opcode === 5) {
            if (m[m[i + 1]] !== 0) {
                i = m[m[i + 2]];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 105) {
            if (m[i + 1] !== 0) {
                i = m[m[i + 2]];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 1005) {
            if (m[m[i + 1]] !== 0) {
                i = m[i + 2];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 1105) {
            if (m[i + 1] !== 0) {
                i = m[i + 2];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 6) {
            if (m[m[i + 1]] === 0) {
                i = m[m[i + 2]];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 106) {
            if (m[i + 1] === 0) {
                i = m[m[i + 2]];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 1006) {
            if (m[m[i + 1]] === 0) {
                i = m[i + 2];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 1106) {
            if (m[i + 1] === 0) {
                i = m[i + 2];
                opcode = m[i];
                ip = 0;
            } else {
                opcode = m[i + 3];
                ip = 3;
            }
        } else if (opcode === 7) { 
            m[m[i + 3]] = m[m[i + 1]] < m[m[i + 2]] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 107) {
            m[m[i + 3]] = m[i + 1] < m[m[i + 2]] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1007) {
            m[m[i + 3]] = m[m[i + 1]] < m[i + 2] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1107) {
            m[m[i + 3]] = m[i + 1] < m[i + 2] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 8) { 
            m[m[i + 3]] = m[m[i + 1]] === m[m[i + 2]] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 108) {
            m[m[i + 3]] = m[i + 1] === m[m[i + 2]] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1008) {
            m[m[i + 3]] = m[m[i + 1]] === m[i + 2] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 1108) {
            m[m[i + 3]] = m[i + 1] === m[i + 2] ? 1 : 0;
            opcode = m[i + 4];
            ip = 4;
        } else if (opcode === 99) {
            break;
        }
    }
    
    console.log(`Diagnostic Code: ${result}`);
    

    This was a very mutation-heavy problem, which went against my functional sensibilities. I'm not happy with the result, but it got the correct answer. If it's stupid but it works, then it ain't stupid.

    4 votes
    1. nothis
      (edited )
      Link Parent
      No shit, this one was exhausting, you're basically programming a compiler, now? Had to read the instructions like 5 times to even make sense of them and they're long (not to mention build upon day...

      No shit, this one was exhausting, you're basically programming a compiler, now? Had to read the instructions like 5 times to even make sense of them and they're long (not to mention build upon day 02, so if you hadn't done that one yet, it's double the work). I spent way too long on the second star's final test sample before I remembered to check outputs for "immediate mode".

      Part 2
      const readline = require("readline-sync");
      
      const ops = input.split(",").map(numberString => parseInt(numberString));
      
      function runOps(ops) {
        let pos = 0;
        while (ops[pos] != 99) {
          // get opcode digits
          const op = getDigits(ops[pos]);
          const currentOp = op.pop();
          console.log(`### op "${currentOp}" at pos ${pos}`);
      
          // add
          if (currentOp === 1) {
            let [p1, p2] = getParameters(ops, op, pos);
            ops[ops[pos + 3]] = p1 + p2;
            pos += 4;
          }
      
          // multiply
          if (currentOp === 2) {
            let [p1, p2] = getParameters(ops, op, pos);
            ops[ops[pos + 3]] = p1 * p2;
            pos += 4;
          }
      
          // input
          if (currentOp === 3) {
            const consoleInput = parseInt(readline.question("Input: "));
            ops[ops[pos + 1]] = consoleInput;
            pos += 2;
          }
      
          // output
          if (currentOp === 4) {
            if (op.pop() === 0) {
              console.log(ops[ops[pos + 1]]);
            } else {
              console.log(ops[pos + 1]);
            }
            pos += 2;
          }
      
          // jump if not zero
          if (currentOp === 5) {
            let [p1, p2] = getParameters(ops, op, pos);
            if (p1 !== 0) {
              pos = p2;
            } else {
              pos += 3;
            }
          }
      
          // jump if zero
          if (currentOp === 6) {
            let [p1, p2] = getParameters(ops, op, pos);
            if (p1 === 0) {
              pos = p2;
            } else {
              pos += 3;
            }
          }
      
          // jump if less
          if (currentOp === 7) {
            let [p1, p2] = getParameters(ops, op, pos);
            if (p1 < p2) {
              ops[ops[pos + 3]] = 1;
            } else {
              ops[ops[pos + 3]] = 0;
            }
            pos += 4;
          }
      
          // jump if equal
          if (currentOp === 8) {
            let [p1, p2] = getParameters(ops, op, pos);
            if (p1 === p2) {
              ops[ops[pos + 3]] = 1;
            } else {
              ops[ops[pos + 3]] = 0;
            }
            pos += 4;
          }
        }
        return ops;
      }
      
      const getParameters = (ops, op, pos) => {
        let p1, p2;
        if (op.pop() === 0) {
          p1 = ops[ops[pos + 1]];
        } else {
          p1 = ops[pos + 1];
        }
        if (op.pop() === 0) {
          p2 = ops[ops[pos + 2]];
        } else {
          p2 = ops[pos + 2];
        }
      
        return [p1, p2];
      };
      
      const getDigits = number => {
        let digits = [];
      
        for (let d = 10000; d >= 10; d = d / 10) {
          if (d === 10) {
            digits.push(number);
            break;
          }
          let digit = Math.floor(number / d);
          digits.push(digit);
          number -= digit * d;
        }
      
        return digits;
      };
      
      runOps(ops);
      
      2 votes
  2. [3]
    mrnd
    Link
    And my adventure with Elixir continues! My architecture from day 2 held up, and adding new opcodes was straightforward. The hardest part was parsing the opcode and mode arguments, but in the end I...

    And my adventure with Elixir continues!

    My architecture from day 2 held up, and adding new opcodes was straightforward. The hardest part was parsing the opcode and mode arguments, but in the end I found a rather simple solution.

    Intcode module
    defmodule Intcode do
      @moduledoc "Intcode interpreter."
    
      @doc """
      Run a program with input and return the output.
      """
      def run(program, input) do
        {_, io} = execute(0, program, in: input, out: [])
        out = io[:out]
        out
      end
    
      @doc """
      Loads a comma separated program from a given path.
      """
      def loadinput(file) do
        File.read!(file)
        |> String.trim()
        |> String.split(",")
        |> Enum.map(&String.to_integer/1)
      end
    
      # Private methods
    
      defp execute(pointer, memory, io) do
        if pointer < length(memory) do
          {new_pointer, new_memory, new_io} = parse_instruction(memory, pointer, io)
          execute(new_pointer, new_memory, new_io)
        else
          {memory, io}
        end
      end
    
      defp parse_instruction(memory, pointer, io) do
        digits =
          Enum.at(memory, pointer)
          |> Integer.digits()
          |> Enum.reverse()
    
        opcode_list = digits |> Enum.slice(0, 2)
        opcode = Enum.at(opcode_list, 0) + Enum.at(opcode_list, 1, 0) * 10
    
        modes = digits |> Enum.slice(2..-1)
    
        case opcode do
          # add
          1 ->
            {pointer + 4,
             change(
               memory,
               pointer + 3,
               reference(memory, pointer + 1, Enum.at(modes, 0, 0)) +
                 reference(memory, pointer + 2, Enum.at(modes, 1, 0))
             ), io}
    
          # multiply
          2 ->
            {pointer + 4,
             change(
               memory,
               pointer + 3,
               reference(memory, pointer + 1, Enum.at(modes, 0, 0)) *
                 reference(memory, pointer + 2, Enum.at(modes, 1, 0))
             ), io}
    
          # read input
          3 ->
            stdin = io[:in]
            stdout = io[:out]
    
            {pointer + 2, change(memory, pointer + 1, hd(stdin)), [in: tl(stdin), out: stdout]}
    
          # write output
          4 ->
            stdin = io[:in]
            stdout = io[:out]
    
            {pointer + 2, memory,
             [in: stdin, out: [reference(memory, pointer + 1, Enum.at(modes, 0, 0)) | stdout]]}
    
          # jump-if-true
          5 ->
            new_pointer =
              if reference(memory, pointer + 1, Enum.at(modes, 0, 0)) != 0 do
                reference(memory, pointer + 2, Enum.at(modes, 1, 0))
              else
                pointer + 3
              end
    
            {new_pointer, memory, io}
    
          # jump-if-false
          6 ->
            new_pointer =
              if reference(memory, pointer + 1, Enum.at(modes, 0, 0)) == 0 do
                reference(memory, pointer + 2, Enum.at(modes, 1, 0))
              else
                pointer + 3
              end
    
            {new_pointer, memory, io}
    
          # less than
          7 ->
            a = reference(memory, pointer + 1, Enum.at(modes, 0, 0))
            b = reference(memory, pointer + 2, Enum.at(modes, 1, 0))
    
            value =
              if a < b do
                1
              else
                0
              end
    
            {pointer + 4, change(memory, pointer + 3, value), io}
    
          # equals
          8 ->
            a = reference(memory, pointer + 1, Enum.at(modes, 0, 0))
            b = reference(memory, pointer + 2, Enum.at(modes, 1, 0))
    
            value =
              if a == b do
                1
              else
                0
              end
    
            {pointer + 4, change(memory, pointer + 3, value), io}
    
          # halt
          99 ->
            {length(memory), memory, io}
    
          _ ->
            raise "Invalid instruction!"
        end
      end
    
      defp reference(memory, pointer, immediate_mode) do
        if immediate_mode == 1 do
          Enum.at(memory, pointer)
        else
          Enum.at(memory, Enum.at(memory, pointer))
        end
      end
    
      defp change(memory, pointer, value) do
        List.replace_at(memory, Enum.at(memory, pointer), value)
      end
    end
    
    Part 1
        input = Intcode.loadinput(filename)
        hd(Intcode.run(input, [1]))
    
    Part 2
        input = Intcode.loadinput(filename)
        hd(Intcode.run(input, [5]))
    
    4 votes
    1. markh
      Link Parent
      Love this and love Elixir. Great work!

      Love this and love Elixir. Great work!

      3 votes
    2. mrnd
      Link Parent
      Also, because I like wasting time, I later added an additional detail: if opcode 3 (input read), can't find input, it produces error code -999. With this, I created the following intcode program:...

      Also, because I like wasting time, I later added an additional detail: if opcode 3 (input read), can't find input, it produces error code -999. With this, I created the following intcode program:

      "3,17,1008,17,-999,19,1005,19,20,1,17,18,18,1105,1,0,0,0,0,0,4,18"

      Given a list of numbers as input, it loops through them and sums them together!

      3 votes
  3. Crespyl
    (edited )
    Link
    I just spent an unreasonable amount of time tracking down an issue in my addressing mode decoder, good unit testing is important here. Something I need to work on myself :S. Day 5 Intcode...

    I just spent an unreasonable amount of time tracking down an issue in my addressing mode decoder, good unit testing is important here. Something I need to work on myself :S.

    Day 5 Intcode Interpreter
    # This module define the "Intcode" interpreter, operating on arrays of Integer
    require "io/console"
    
    module Intcode
      @@DEBUG = false
    
      # This class is just a holder for information about an Opcode, including Proc
      # objects for the implementation and a debug/display function
      class Opcode
        property sym : Symbol
        property size : Int32
        property impl : Proc(VM, Int32, Int32)
        property disasm : Proc(VM, Int32, String)
    
        def initialize(@sym, @size, @impl, @disasm)
        end
    
        # Execute this opcode inside this VM (we pass the actual instruction as well
        # so that the opcode can parse out its addressing modes)
        def exec(vm, instr)
          impl.call(vm, instr)
        end
    
        # Attempt to produce a debug string for this opcode
        def debug(vm, instr)
          disasm.call(vm, instr)
        end
    
        # Lookup the correct registered Opcode from an instruction
        def self.from_instruction(instr)
          OPCODES[instr % 100]
        end
    
        # Get the addressing mode for the nth parameter from an instruction (2+nth digit)
        #
        # Mode 0 is "position", should treat the parameter as an address
        # Mode 1 is immediate, should treat the parameter as a literal
        def self.get_addressing_mode_for_param(instr, param_idx)
          instr // 10**(param_idx+1) % 10
        end
    
        # Given a VM and instruction, decode the given value
        def self.parameter_value(vm, instr, param)
          case Opcode.get_addressing_mode_for_param(instr, param)
          when 0 # position mode
                 vm.mem[vm.mem[vm.pc + param]]
          when 1 # literal mode
                 vm.mem[vm.pc + param]
          else
            raise "Unsupported addressing mode #{instr}, #{param}"
          end
        end
    
        def self.debug_parameter_value(vm, instr, param)
          case Opcode.get_addressing_mode_for_param(instr, param)
          when 0 # position mode
                 "@#{vm.mem[vm.pc + param]}"
          when 1 # literal mode
                 "#{vm.mem[vm.pc + param]}"
          else
            raise "Unsupported addressing mode #{instr}, #{param}"
          end
        end
    
      end
    
      # This class holds the current state of a running interpreter: the memory
      # (Array(Int32)), the program counter, and a "halted/running" flag
      class VM
        property mem : Array(Int32)
        property pc : Int32
        property halted : Bool
    
        def initialize(mem)
          @pc = 0
          @halted = false
          @mem = Array.new(4096, 0)
          mem.each_with_index do |v, i|
            @mem[i] = v
          end
    
        end
    
        def self.from_file(filename)
          VM.new(Intcode.load_file(filename))
        end
    
        def self.from_string(str)
          VM.new(Intcode.read_intcode(str))
        end
    
        def run
          while !@halted && @pc < mem.size
            instr = mem[pc]
            opcode = Opcode.from_instruction(instr)
            if opcode == nil
              raise "INVALID OPCODE AT #{pc}: #{mem[pc]}"
            end
    
            Intcode.log("%4i:%04i: %s" % [pc, mem[pc], opcode.debug(self, instr)])
            opcode.exec(self, instr)
          end
        end
    
        def get_input
          input = STDIN.read_line.to_i
          puts "GOT INPUT #{input}"
          input
        end
    
        def write_output(val)
          puts "                                     GOT OUTPUT #{val}"
        end
      end
    
      # Here we define the mapping from integer to opcode, along with the actual
      # implementation of each, as Proc objects that get bound to the Opcode
      # instance during execution
      OPCODES = {
        1 => Opcode.new(:add,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          y = Opcode.parameter_value(vm, instr, 2)
                          dest = vm.mem[vm.pc+3]
                          vm.mem[dest] = x + y
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "ADD %4s, %4s -> %4s" % [Opcode.debug_parameter_value(vm, instr, 1),
                                                   Opcode.debug_parameter_value(vm, instr, 2),
                                                   vm.mem[vm.pc+3]]
                        }),
        2 => Opcode.new(:mul,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          y = Opcode.parameter_value(vm, instr, 2)
                          dest = vm.mem[vm.pc+3]
                          vm.mem[dest] = x * y
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "MUL %4s, %4s -> %4s" % [Opcode.debug_parameter_value(vm, instr, 1),
                                                   Opcode.debug_parameter_value(vm, instr, 2),
                                                   vm.mem[vm.pc+3]]
                        }),
        3 => Opcode.new(:input,
                        2,
                        ->(vm: VM, instr: Int32) {
                          dest = vm.mem[vm.pc+1]
                          vm.mem[dest] = vm.get_input
                          vm.pc += 2
                        },
                        ->(vm: VM, instr: Int32) {
                          "INPUT -> %4s" % [vm.mem[vm.pc+1]]
                        }),
        4 => Opcode.new(:output,
                        2,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          vm.write_output(x)
                          vm.pc += 2
                        },
                        ->(vm: VM, instr: Int32) {
                          "OUTPUT %4s" % [Opcode.debug_parameter_value(vm, instr, 1)]
                        }),
        5 => Opcode.new(:jt,
                        3,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          dest = Opcode.parameter_value(vm, instr, 2)
                          if x != 0
                            vm.pc = dest
                          else
                            vm.pc += 3
                          end
                        },
                        ->(vm: VM, instr: Int32) {
                          "JT %4s %4s" % [Opcode.debug_parameter_value(vm, instr, 1),
                                          Opcode.debug_parameter_value(vm, instr, 2)]
                        }),
        6 => Opcode.new(:jf,
                        3,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          dest = Opcode.parameter_value(vm, instr, 2)
                          if x == 0
                            vm.pc = dest
                          else
                            vm.pc += 3
                          end
                        },
                        ->(vm: VM, instr: Int32) {
                          "JF %4s %4s" % [Opcode.debug_parameter_value(vm, instr, 1),
                                          Opcode.debug_parameter_value(vm, instr, 2)]
                        }),
        7 => Opcode.new(:lt,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          y = Opcode.parameter_value(vm, instr, 2)
                          dest = vm.mem[vm.pc+3]
                          if x < y
                            vm.mem[dest] = 1
                          else
                            vm.mem[dest] = 0
                          end
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "LT %4s %4s -> %4s" % [Opcode.debug_parameter_value(vm, instr, 1),
                                                 Opcode.debug_parameter_value(vm, instr, 2),
                                                 vm.mem[vm.pc+3]]
                        }),
        8 => Opcode.new(:eq,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.parameter_value(vm, instr, 1)
                          y = Opcode.parameter_value(vm, instr, 2)
                          dest = vm.mem[vm.pc+3]
                          if x == y
                            vm.mem[dest] = 1
                          else
                            vm.mem[dest] = 0
                          end
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "EQ %4s %4s -> %4s" % [Opcode.debug_parameter_value(vm, instr, 1),
                                                 Opcode.debug_parameter_value(vm, instr, 2),
                                                 vm.mem[vm.pc+3]]
                        }),
        99 => Opcode.new(:halt,
                         1,
                         ->(vm: VM, instr: Int32) {
                           vm.halted = true
                           1
                         },
                         ->(vm: VM, instr: Int32) {
                           "HALT"
                         }),
      }
    
      # Parse the input string into an array of integers
      def self.read_intcode(str)
        str.split(',')
          .map { |s| s.to_i }
          .reject { |x| !x.is_a? Int32 }
      end
    
      # Load an Intcode program from a filename, returned as an array of Integers
      def self.load_file(filename)
        read_intcode(File.read(filename))
      end
    
      # Load an Intcode VM from a string
      def self.load_vm_from_string(str)
        mem = read_intcode(str)
        VM.new(mem)
      end
    
      # Load an Intcode VM from a file
      def self.load_vm_from_file(filename)
        mem = load_file(filename)
        VM.new(mem)
      end
    
      # Enable or disable verbose debug logging during execution
      def self.set_debug(enable_debug)
        @@DEBUG = enable_debug
      end
    
      def self.log(msg)
        puts msg if @@DEBUG
      end
    end
    

    I'm not entirely satisfied with how I'm handling the addressing modes, writing to a parameter is always encoded as a "position"-mode parameter (an address), but the actual value of that parameter should be interpreted literally. I.e. you don't see 0003 4 and go find the value at 4 then write your input to that address (indirect), you always write directly to 4.

    I think I want to lift out the concept of a "parameter" into its own object that tracks its value as encoded and the addressing mode, and let the VM operate on that to distinguish between the read and write behaviors.

    Day 5 (with nicer parameter handling)
    # This module define the "Intcode" interpreter, operating on arrays of Integer
    require "io/console"
    
    module Intcode
      @@DEBUG = false
    
      # This class is just a holder for information about an Opcode, including Proc
      # objects for the implementation and a debug/display function
      class Opcode
        property sym : Symbol
        property size : Int32
        property impl : Proc(VM, Int32, Int32)
        property disasm : Proc(VM, Int32, String)
    
        def initialize(@sym, @size, @impl, @disasm)
        end
    
        # Execute this opcode inside this VM (we pass the actual instruction as well
        # so that the opcode can parse out its addressing modes)
        def exec(vm, instr)
          impl.call(vm, instr)
        end
    
        # Attempt to produce a debug string for this opcode
        def debug(vm, instr)
          disasm.call(vm, instr)
        end
    
        # Lookup the correct registered Opcode from an instruction
        def self.from_instruction(instr)
          OPCODES[instr % 100]
        end
    
        # Get the addressing mode for the nth parameter from an instruction (2+nth digit)
        #
        # Mode 0 is "position", should treat the parameter as an address
        # Mode 1 is immediate, should treat the parameter as a literal
        def self.get_addressing_mode_for_param(instr, param_idx)
          case instr // 10**(param_idx+1) % 10
          when 0 then :position
          when 1 then :literal
          else
            raise "Unsupported addressing mod #{instr}: #{param_idx}"
          end
        end
    
        # Given a VM and instruction, decode the given parameter
        def self.get_parameter(vm, instr, param)
          mode = get_addressing_mode_for_param(instr, param)
          val = vm.mem[vm.pc + param]
          Parameter.new(mode, val)
        end
    
        def self.get_parameters(vm, instr, n_params)
          (1..n_params).map { |i| get_parameter(vm, instr, i) }
        end
        def self.debug_parameters(vm, instr, n_params)
          get_parameters(vm, instr, n_params).map { |p| p.debug }
        end
      end
    
      # Represents an encoded parameter and its addressing mode
      class Parameter
        property val : Int32 # the original value in memory
        property mode : Symbol
    
        def initialize(@mode, @val) end
    
        def debug
          case mode
          when :position then "@#{val}"
          when :literal then "val"
          else
            "??"
          end
        end
      end
    
      # This class holds the current state of a running interpreter: the memory
      # (Array(Int32)), the program counter, and a "halted/running" flag
      class VM
        property mem : Array(Int32)
        property pc : Int32
        property halted : Bool
    
        def initialize(mem)
          @pc = 0
          @halted = false
          @mem = Array.new(4096, 0)
          mem.each_with_index do |v, i|
            @mem[i] = v
          end
        end
    
        def self.from_file(filename)
          VM.new(Intcode.load_file(filename))
        end
    
        def self.from_string(str)
          VM.new(Intcode.read_intcode(str))
        end
    
        # Get the value of the provided parameter
        def read_param(p : Parameter)
          case p.mode
          when :position then mem[p.val]
          when :literal then p.val
          else
            raise "Unsupported addressing mode for #{p}"
          end
        end
    
        # Set the address indicated by the parameter to the given value
        def write_param_value(p : Parameter, val : Int32)
          case p.mode
          when :position then mem[p.val] = val
          when :literal then raise "Cannot write to literal"
          else
            raise "Unsupported addressing mode for #{p}"
          end
        end
    
        def run
          while !@halted && @pc < mem.size
            instr = mem[pc]
            begin
            opcode = Opcode.from_instruction(instr)
            rescue
              raise "INVALID OPCODE AT #{pc}: #{mem[pc]}"
            end
            if opcode == nil
            end
    
            Intcode.log("%4i:%04i: %s" % [pc, mem[pc], opcode.debug(self, instr)])
            opcode.exec(self, instr)
          end
        end
    
        def get_input
          input = STDIN.read_line.to_i
          puts "GOT INPUT #{input}"
          input
        end
    
        def write_output(val)
          puts "                                     GOT OUTPUT #{val}"
        end
      end
    
      # Here we define the mapping from integer to opcode, along with the actual
      # implementation of each, as Proc objects that get bound to the Opcode
      # instance during execution
      OPCODES = {
        1 => Opcode.new(:add,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x, y, dest = Opcode.get_parameters(vm, instr, 3)
                          vm.write_param_value(dest, vm.read_param(x) + vm.read_param(y))
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "ADD %4s, %4s -> %4s" % Opcode.debug_parameters(vm, instr, 3)
                        }),
        2 => Opcode.new(:mul,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x, y, dest = Opcode.get_parameters(vm, instr, 3)
                          vm.write_param_value(dest, vm.read_param(x) * vm.read_param(y))
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "MUL %4s, %4s -> %4s" % Opcode.debug_parameters(vm, instr, 3)
                        }),
        3 => Opcode.new(:input,
                        2,
                        ->(vm: VM, instr: Int32) {
                          dest = Opcode.get_parameter(vm, instr, 1)
                          vm.write_param_value(dest, vm.get_input)
                          vm.pc += 2
                        },
                        ->(vm: VM, instr: Int32) {
                          "INPUT -> %4s" % Opcode.debug_parameters(vm, instr, 1)
                        }),
        4 => Opcode.new(:output,
                        2,
                        ->(vm: VM, instr: Int32) {
                          x = Opcode.get_parameter(vm, instr, 1)
                          vm.write_output(vm.read_param(x))
                          vm.pc += 2
                        },
                        ->(vm: VM, instr: Int32) {
                          "OUTPUT %4s" % Opcode.debug_parameters(vm, instr, 1)
                        }),
        5 => Opcode.new(:jt,
                        3,
                        ->(vm: VM, instr: Int32) {
                          x, dest = Opcode.get_parameters(vm, instr, 2)
                          if vm.read_param(x) != 0
                            vm.pc = vm.read_param(dest)
                          else
                            vm.pc += 3
                          end
                        },
                        ->(vm: VM, instr: Int32) {
                          "JT  %4s, %4s" % Opcode.debug_parameters(vm, instr, 2)
                        }),
        6 => Opcode.new(:jf,
                        3,
                        ->(vm: VM, instr: Int32) {
                          x, dest = Opcode.get_parameters(vm, instr, 2)
                          if vm.read_param(x) == 0
                            vm.pc = vm.read_param(dest)
                          else
                            vm.pc += 3
                          end
                        },
                        ->(vm: VM, instr: Int32) {
                          "JF  %4s, %4s" % Opcode.debug_parameters(vm, instr, 2)
                        }),
        7 => Opcode.new(:lt,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x, y, dest = Opcode.get_parameters(vm, instr, 3)
                          if vm.read_param(x) < vm.read_param(y)
                            vm.write_param_value(dest, 1)
                          else
                            vm.write_param_value(dest, 0)
                          end
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "LT  %4s, %4s -> %4s" % Opcode.debug_parameters(vm, instr, 3)
                        }),
        8 => Opcode.new(:eq,
                        4,
                        ->(vm: VM, instr: Int32) {
                          x, y, dest = Opcode.get_parameters(vm, instr, 3)
                          if vm.read_param(x) == vm.read_param(y)
                            vm.write_param_value(dest, 1)
                          else
                            vm.write_param_value(dest, 0)
                          end
                          vm.pc += 4
                        },
                        ->(vm: VM, instr: Int32) {
                          "EQ  %4s, %4s -> %4s" % Opcode.debug_parameters(vm, instr, 3)
                        }),
        99 => Opcode.new(:halt,
                         1,
                         ->(vm: VM, instr: Int32) {
                           vm.halted = true
                           1
                         },
                         ->(vm: VM, instr: Int32) {
                           "HALT"
                         }),
      }
    end
    

    I went back and cleaned up the handling of read/write parameter and addressing modes. Now I'm parsing out the parameter explicitly, and the VM object knows how to read and write from a given parameter. The parameter objects know their own addressing mode, and the VM can use that to understand the difference between writing to a position-mode parameter or the (inevitable) register-mode parameter.

    I've also noticed that the test binary includes a good deal of code that we haven't gotten to yet, there may be some hints in there...

    3 votes
  4. Crestwave
    (edited )
    Link
    This language is looking more and more like Brainfuck. I got stuck for a bit because I had the order of the modes reversed, then tried to solve it cleanly. Then I got impatient and just reversed...

    This language is looking more and more like Brainfuck. I got stuck for a bit because I had the order of the modes reversed, then tried to solve it cleanly. Then I got impatient and just reversed the string; I'll try to clean it up later.

    Part 1
    #!/usr/bin/awk -f
    { prog = prog $0 }
    
    END {
        split(prog, init, ",")
        for (i = 1; i <= length(init); ++i)
            mem[i-1] = init[i]
    
        for (ptr = 0; ptr < length(mem);) {
            split(substr(mem[ptr], 1, length(mem[ptr])-2), modes, "")
            c = int(substr(mem[ptr], length(mem[ptr])-1))
    
            for (i = 1; i <= 2; ++i)
                params[i] = modes[length(modes)-i+1] ? \
                    mem[ptr+i] : \
                    mem[mem[ptr+i]]
    
            if (c == 1) {
                mem[mem[ptr+3]] = params[1] + params[2]
                ptr += 4
            } else if (c == 2) {
                mem[mem[ptr+3]] = params[1] * params[2]
                ptr += 4
            } else if (c == 3) {
                getline input < "-"
                mem[mem[ptr+1]] = int(input)
                ptr += 2
            } else if (c == 4) {
                print params[1]
                ptr += 2
            } else if (c == 99) {
                break
            } else {
                exit(1)
            }
        }
    }
    
    Part 2
    #!/usr/bin/awk -f
    { prog = prog $0 }
    
    END {
        split(prog, init, ",")
        for (i = 1; i <= length(init); ++i)
            mem[i-1] = init[i]
    
        for (ptr = 0; ptr < length(mem);) {
            split(substr(mem[ptr], 1, length(mem[ptr])-2), modes, "")
            c = int(substr(mem[ptr], length(mem[ptr])-1))
    
            for (i = 1; i <= 2; ++i)
                params[i] = modes[length(modes)-i+1] ? \
                    mem[ptr+i] : \
                    mem[mem[ptr+i]]
    
            if (c == 1) {
                mem[mem[ptr+3]] = params[1] + params[2]
                ptr += 4
            } else if (c == 2) {
                mem[mem[ptr+3]] = params[1] * params[2]
                ptr += 4
            } else if (c == 3) {
                getline input < "-"
                mem[mem[ptr+1]] = int(input)
                ptr += 2
            } else if (c == 4) {
                print params[1]
                ptr += 2
            } else if (c == 5) {
                ptr = params[1] ? params[2] : ptr+3
            } else if (c == 6) {
                ptr = params[1] ? ptr+3 : params[2]
            } else if (c == 7) {
                mem[mem[ptr+3]] = params[1] < params[2]
                ptr += 4
            } else if (c == 8) {
                mem[mem[ptr+3]] = params[1] == params[2]
                ptr += 4
            } else if (c == 99) {
                break
            } else {
                exit(1)
            }
        }
    }
    

    EDIT: Okay, cleaned up; it's amazing what a short water break can do. I'm not sure if the method to read a line of input is POSIX, but it's compatible with gawk, lok, mawk, and nawk, so I think it's fine even if it isn't.

    3 votes
  5. Gyrfalcon
    (edited )
    Link
    I had a rough day today so I only got to the first part. I will probably work on adding things for the second part tomorrow before starting on the new challenges. I had a little trouble with...

    I had a rough day today so I only got to the first part. I will probably work on adding things for the second part tomorrow before starting on the new challenges. I had a little trouble with getting the immediate vs. position mode right on the I/O instructions, but other than that I think I was able to fit it into the format I started with on day 2 fairly well.

    EDIT: Part 2 complete!

    Parts 1 and 2, Python

    EDIT: Part 2 was not as hard as I thought it would be. My only hiccup was moving the pointer on the jump instructions when there was not a jump.

    def get_operands(instructions, curr_instruction, pointer):
    
        for i in range(5 - len(curr_instruction)):
            curr_instruction.insert(0,0) # Pad to get the right number of 0s
    
        out_location = instructions[pointer + 3]
    
        if curr_instruction[1] == 0:
            op_2_location = instructions[pointer+2]
        else:
            op_2_location = pointer + 2
    
        if curr_instruction[2] == 0:
            op_1_location = instructions[pointer+1]
        else:
            op_1_location = pointer + 1
    
        op_1 = instructions[op_1_location]
        op_2 = instructions[op_2_location]
    
        return [op_1, op_2, out_location]
    
    def get_io_operand(instructions, curr_instruction, pointer):
    
        for i in range(3 - len(curr_instruction)):
            curr_instruction.insert(0,0) # Pad to get the right number of 0s
    
        if curr_instruction[-1] == 4 and curr_instruction[0] == 1:
            op = pointer + 1
        else:
            op = instructions[pointer + 1]
    
        return op
    
    def write_result(instructions, result, location):
    
        instructions[location] = result
        return instructions
    
    def add_instruction(instructions, curr_instruction, pointer):
        operands = get_operands(instructions, curr_instruction, pointer)
    
        result = operands[0] + operands[1]
        return write_result(instructions, result, operands[2])
    
    def mult_instruction(instructions, curr_instruction, pointer):
        operands = get_operands(instructions, curr_instruction, pointer)
    
        result = operands[0] * operands[1]
        return write_result(instructions, result, operands[2])
    
    def output_instruction(instructions, curr_instruction, pointer):
    
        operand = get_io_operand(instructions, curr_instruction, pointer)
    
        print("Output is {}".format(instructions[operand]))
    
    def input_instruction(instructions, curr_instruction, pointer):
    
        operand = get_io_operand(instructions, curr_instruction, pointer)
    
        user_input = int(input("Please enter input: "))
    
        return write_result(instructions, user_input, operand)
    
    def jump_instruction(instructions, curr_instruction, pointer, jump_type):
    
        operands = get_operands(instructions, curr_instruction, pointer)
    
        if ((operands[0] and jump_type) or (not operands[0] and not jump_type)):
            return operands[1]
        else:
            return (pointer + 3)
    
    def less_than_instruction(instructions, curr_instruction, pointer):
    
        operands = get_operands(instructions, curr_instruction, pointer)
    
        if operands[0] < operands[1]:
            return write_result(instructions, 1, operands[2])
        else:
            return write_result(instructions, 0, operands[2])
    
    def equals_instruction(instructions, curr_instruction, pointer):
    
        operands = get_operands(instructions, curr_instruction, pointer)
    
        if operands[0] == operands[1]:
            return write_result(instructions, 1, operands[2])
        else:
            return write_result(instructions, 0, operands[2])
    
    def process_instructions(instructions):
    
        pointer = 0
    
        while True:
    
            if pointer > (len(instructions) - 1):
                break
            elif instructions[pointer] == 99:
                break
    
            curr_instruction = [int(num) for num in str(instructions[pointer])]
    
            if curr_instruction[-1] == 1:
                instructions = add_instruction(instructions, curr_instruction, pointer)
                pointer += 4
            elif curr_instruction[-1] == 2:
                instructions = mult_instruction(instructions, curr_instruction, pointer)
                pointer += 4
            elif curr_instruction[-1] == 3:
                instructions = input_instruction(instructions, curr_instruction, pointer)
                pointer += 2
            elif curr_instruction[-1] == 4:
                output_instruction(instructions, curr_instruction, pointer)
                pointer += 2
            elif curr_instruction[-1] == 5:
                pointer = jump_instruction(instructions, curr_instruction, pointer, True)
            elif curr_instruction[-1] == 6:
                pointer = jump_instruction(instructions, curr_instruction, pointer, False)
            elif curr_instruction[-1] == 7:
                instructions = less_than_instruction(instructions, curr_instruction, pointer)
                pointer += 4
            elif curr_instruction[-1] == 8:
                instructions = equals_instruction(instructions, curr_instruction, pointer)
                pointer += 4
    
        print("PROGRAM TERMINATED")
    
        return instructions
    
    input_file = "PuzzleInput.txt"
    
    with open(input_file, 'r') as fid:
        data = fid.readline()
    
    instructions = data.strip().split(',')
    
    for idx in range(len(instructions)):
        instructions[idx] = int(instructions[idx])
    
    process_instructions(instructions)
    
    2 votes
  6. PapaNachos
    Link
    I had to do moderately heavy modifications from day 2, but mostly it was just converting stuff to functions that I had previously hard-coded. Overall my structure worked pretty well. It would...

    I had to do moderately heavy modifications from day 2, but mostly it was just converting stuff to functions that I had previously hard-coded. Overall my structure worked pretty well.

    It would definitely be shorter if I didn't include verbose and super_verbose modes. Still plenty of fun, I'm hoping we do more with the int-puter. It's getting closer to a processor

    Day 5 - Python
    import math
    verbose_mode = False
    super_verbose_mode = False
    
    def read_instruction(instruction):
        #In order to deal with shorter length instructions we're going to 0-pad everything out to len==5
        #and then divide it back up
        if verbose_mode:
            print(f"Reading Instruction: {instruction}")
        instruction_components = [int(d) for d in str(instruction)]
        if len(instruction_components) == 1:
            #Pads opcode with non-functional 0, now len==2
            instruction_components.insert(0,0)
        if len(instruction_components) == 2:
            #Brings parameter_1 from null to 0, now len==3
            instruction_components.insert(0,0)
        if len(instruction_components) == 3:
            #Brings parameter_2 from null to 0, now len==4
            instruction_components.insert(0,0)
        if len(instruction_components) == 4:
            #Brings parameter_3 from null to 0, now len==5
            instruction_components.insert(0,0)
        opcode_portion = instruction_components[-2:]
        parameters_reversed = instruction_components[0:3]
        
        
        
        parameters = [int(parameters_reversed[2]), int(parameters_reversed[1]), int(parameters_reversed[0])]
        
        opcode = int((10 * opcode_portion[0]) + opcode_portion[1])
        
        if verbose_mode:
            print(f"Op Code {opcode}")
            print(f"Parameters {parameters}")
        
        return opcode, parameters
    
    def execute_instruction(op_code, parameter_codes, instruction_pointer):
        address_0 = instruction_pointer + 1
        address_1 = instruction_pointer + 2
        address_2 = instruction_pointer + 3
        
        if super_verbose_mode:
            print(memory)
        
        if op_code == 1:
            val = read_value(address_0, parameter_codes[0]) + read_value(address_1, parameter_codes[1])
            write_value(address_2, val)
            instruction_pointer = instruction_pointer + 4
        elif op_code == 2:
            val = read_value(address_0, parameter_codes[0]) * read_value(address_1, parameter_codes[1])
            write_value(address_2, val)
            instruction_pointer = instruction_pointer + 4
        elif op_code == 3:
            val = input("Input Requested: ")
            write_value(address_0, val)
            instruction_pointer = instruction_pointer + 2
        elif op_code == 4:
            print(read_value(address_0, parameter_codes[0]))
            instruction_pointer = instruction_pointer + 2
        elif op_code == 5:
            val = read_value(address_0, parameter_codes[0])
            if val != 0:
                instruction_pointer = read_value(address_1, parameter_codes[1])
            else:
                instruction_pointer = instruction_pointer + 3
        elif op_code == 6:
            val = read_value(address_0, parameter_codes[0])
            if val == 0:
                instruction_pointer = read_value(address_1, parameter_codes[1])
            else:
                instruction_pointer = instruction_pointer + 3
        elif op_code == 7:
            val_0 = read_value(address_0, parameter_codes[0])
            val_1 = read_value(address_1, parameter_codes[1])
            
            if val_0 < val_1:
                write_value(address_2, 1)
            else:
                write_value(address_2, 0)
            instruction_pointer = instruction_pointer + 4
        elif op_code == 8:
            val_0 = read_value(address_0, parameter_codes[0])
            val_1 = read_value(address_1, parameter_codes[1])
            
            if val_0 == val_1:
                write_value(address_2, 1)
            else:
                write_value(address_2, 0)
            instruction_pointer = instruction_pointer + 4
        else:
            print('Something fucking broke')
            
        return instruction_pointer
    
    def read_value(address, parameter):
        if verbose_mode:
            print("Reading memory")
            print(f"Address: {address}")
            print(f"Parameter: {parameter}")
        if super_verbose_mode:
            print(memory)
        #If parameter is 0, use the address stored in address
        if parameter == 0:
            new_address = memory[address]
            value = memory[new_address]
        #If parameter is 1, us the value stored in address
        elif parameter == 1:
            value = memory[address]
        return int(value)
    
    def write_value(address, value):
        new_address = memory[address]
        memory[new_address] = value
        
    def run_computer(initial_memory):
        #input_string = "1,9,10,3,2,3,11,0,99,30,40,50"
        global memory 
        
        memory = [int(x) for x in initial_memory.split(",")]
    
        instruction_pointer = 0
        op_code, parameters = read_instruction(memory[instruction_pointer])
    
    
        while op_code != 99:
            instruction_pointer = execute_instruction(op_code, parameters, instruction_pointer)
            op_code, parameters = read_instruction(memory[instruction_pointer])
        return memory
    initial_mem = "MEMORY STRING GOES HERE"
    
    results = run_computer(initial_mem)
    
    print(results)
    
    2 votes
  7. asterisk
    Link
    I got sick, but now I kinda fine, so I return. It was really hard to read and to understand the task for me too. PHP functions.php <?php class instructions { public $input; public $point; public...

    I got sick, but now I kinda fine, so I return.

    It was really hard to read and to understand the task for me too.

    PHP

    functions.php

    <?php
    
    class instructions
    {
        public $input;
        public $point;
        public $enter;
        public $result;
    
        function __construct($input, $enter)
        {
            $this->input = $input;
            $this->enter = $enter;
            $this->point = 0;
        }
    
        function size()
        {
            return count($this->input);
        }
    
        function imm($add = 0, $point = null) // immediate
        {
            return is_null($point) ? $this->point + $add : $point;
        }
    
        function pos($add = 0, $point = null) // possition
        {
            return $this->input[$this->imm($add, $point)];
        }
    
        function mode($pos)
        {
            $modes = array_reverse(str_split(substr($this->pos(), 0, -2)));
    
            return isset($modes[$pos - 1]) ? $modes[$pos - 1] ? $this->imm($pos) : $this->pos($pos) : $this->pos($pos);
        }
    
        function sum($augend, $addent, $sum)
        {
    
            $this->input[$sum] = $this->input[$augend] + $this->input[$addent];
            $this->point += 4;
        }
    
        function multi($multiplier, $multiplicand, $product)
        {
    
            $this->input[$product] = $this->input[$multiplier] * $this->input[$multiplicand];
            $this->point += 4;
        }
    
        function input($where)
        {
    
            $this->input[$where] = $this->enter;
            $this->point += 2;
        }
    
        function output($where)
        {
    
            $this->result = $this->input[$where];
            $this->point += 2;
        }
    
        function compare($first, $second, $where = null)
        {
            switch (substr($this->pos(), -2)) {
                case 5:
                    $this->point = $this->input[$first] ? $this->input[$second] : $this->point + 3;
                    break;
                case 6:
                    $this->point = $this->input[$first] ? $this->point + 3 : $this->input[$second];
                    break;
                case 7:
                    $this->input[$where] = $this->input[$first] < $this->input[$second] ? 1 : 0;
                    $this->point += 4;
                    break;
                case 8:
                    $this->input[$where] = $this->input[$first] == $this->input[$second] ? 1 : 0;
                    $this->point += 4;
                    break;
            }
        }
    }
    

    part-2.php; For part-1 just remove cases. But here is no different exept value of $enter (sysmtem ID), even with these cases it works same.

    <?php include('functions.php');
    
    function run($input, $enter = 1)
    {
        $op = new instructions($input, $enter);
    
        while ($op->point < $op->size()) {
            switch (substr($op->pos(), -2)) {
                case 1:
                    $op->sum($op->mode(1), $op->mode(2), $op->mode(3));
                    break;
                case 2:
                    $op->multi($op->mode(1), $op->mode(2), $op->mode(3));
                    break;
                case 3:
                    $op->input($op->mode(1));
                    break;
                case 4:
                    $op->output($op->mode(1));
                    break;
                case 5:
                    $op->compare($op->mode(1), $op->mode(2));
                    break;
                case 6:
                    $op->compare($op->mode(1), $op->mode(2));
                    break;
                case 7:
                    $op->compare($op->mode(1), $op->mode(2), $op->mode(3));
                    break;
                case 8:
                    $op->compare($op->mode(1), $op->mode(2), $op->mode(3));
                    break;
                case 99:
                    break 2;
                default:
                    echo sprintf("Point %s, Op %s\n", $op->imm(), $op->pos());
                    break 2;
            };
        }
    
        echo "Result: $op->result\n";
    }
    
    run(explode(',', file_get_contents(__DIR__ . '/input.txt')), 5); // p1 7566643, p2 9265694
    
    1 vote