13 votes

Day 2: 1202 Program Alarm

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


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>

21 comments

  1. [7]
    tesseractcat
    (edited )
    Link
    Day 2 in Befunge Day 2 was definitely significantly more challenging, but the puzzle worked well with the put and get instructions in Befunge. Part 1 This snippet of code might not work in all...

    Day 2 in Befunge

    Day 2 was definitely significantly more challenging, but the puzzle worked well with the put and get instructions in Befunge.

    Part 1

    This snippet of code might not work in all Befunge interpreters/compilers. I saw nothing in the spec that said I couldn't use the put instruction to store arbitrarily large numbers, but it doesn't work consistently. I actually created my own interpreter for this to run more smoothly (also so I could enter all the inputs as one array, rather than individually at each &). For the input I needed to add a -1 at the end to signify the end.

    v                                                             
    
    1      >v
    >: &:1+|>68*    + \0p 1+ v  <-- Encoder
    ^                        < 
           >$$$ 1v
               > >:0g 68*-    v
                
                 v            <   <-- Opcode processing 
    
                 > :1-#v_$ 1+:0g68*- 1+0 g68*-               v
                 v     <v g10 +*86+p10\  -*86g 0+1 -*86g0:+1\<
                        > 1+0g68*- 1+0 p  01g 1+              v
                 v                                      
    
    
                 > :2-#v_$ 1+:0g68*- 1+0 g68*-               v
                 v     <v g10 +*86*p10\  -*86g 0+1 -*86g0:+1\<
                        > 1+0g68*- 1+0 p  01g 1+              v
                 v                                      
    
                 >:25*9*9+-#v_$$0>1+:0g68*-:44*+#v_@
               + v          <    ^          ," ".<
               1 $
               ^ <                                            <
    
    Part 2

    I just brute forced this section by manually trying a lot of different values.

    5 votes
    1. [5]
      nothis
      Link Parent
      wtf is Befunge? And wow.

      Befunge

      wtf is Befunge? And wow.

      1 vote
      1. Deimos
        (edited )
        Link Parent
        I know nothing about Befunge other than what people said in the comments here (that it's some kind of weird grid-based language), but I was looking at these solutions last night and had a...

        I know nothing about Befunge other than what people said in the comments here (that it's some kind of weird grid-based language), but I was looking at these solutions last night and had a realization: it basically turns any real-world programming problem into something a lot like a text-based Zachtronics game.

        Just looking at the solutions without reading anything about the language, I can tell that it's doing something with the ^ > v < characters (as arrows that point up/right/down/left) to direct the execution of the program, and that you can put some kind of instructions along the path to do things, change direction conditionally, etc. So it's similar in a lot of ways to the Zachtronic games like SpaceChem, where you've got objects physically moving around and you process them on the way and move them into different sections of your machine.

        I think that's fascinating—of course it's not very practical, but it's like making a Zachtronics-style puzzle out of every programming task you use it for.

        5 votes
      2. tesseractcat
        Link Parent
        Befunge is a 2d grid based programming language. Here's a good wiki page on it: https://esolangs.org/wiki/Befunge.

        Befunge is a 2d grid based programming language. Here's a good wiki page on it: https://esolangs.org/wiki/Befunge.

        1 vote
      3. Crestwave
        Link Parent
        Befunge is an esolang that was designed to be as difficult to compile as possible: https://esolangs.org/wiki/Befunge

        Befunge is an esolang that was designed to be as difficult to compile as possible: https://esolangs.org/wiki/Befunge

        1 vote
    2. markh
      Link Parent
      What am I reading and how did you write this?

      What am I reading and how did you write this?

  2. [3]
    Crespyl
    (edited )
    Link
    Day 2 in Crystal I did a quick pass in ruby first to get the solutions, then went back and refactored a bit to make the interpreter more extensible, in the hope that we come back to it later. I...

    Day 2 in Crystal

    I did a quick pass in ruby first to get the solutions, then went back and refactored a bit to make the interpreter more extensible, in the hope that we come back to it later. I really enjoy working with bytecode interpreters/VMs (the Synacor Challenge was one of my favorite puzzles), so this was really fun.

    Intcode Module
    # This module define the "Intcode" interpreter, operating on arrays of Integer
    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)
        property disasm : Proc(VM, String)
    
        def initialize(@sym, @size, @impl, @disasm)
        end
    
        # Execute this opcode inside this VM
        def exec(vm)
          impl.call(vm)
        end
    
        # Attempt to produce a debug string for this opcode
        def debug(vm)
          disasm.call(vm)
        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
        end
    
        def self.from_file(filename)
          VM.new(load_file(filename))
        end
    
        def self.from_string(str)
          VM.new(read_intcode(str))
        end
    
        def run
          while !@halted && @pc < mem.size
            opcode = OPCODES[mem[pc]]
            if opcode == nil
              raise "INVALID OPCODE AT #{pc}: #{mem[pc]}"
            end
    
            Intcode.log("%4i: %s" % [pc, opcode.debug(self)])
            opcode.exec(self)
          end
        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) {
                          x = vm.mem[vm.pc+1]
                          y = vm.mem[vm.pc+2]
                          dest = vm.mem[vm.pc+3]
                          vm.mem[dest] = vm.mem[x] + vm.mem[y]
                          vm.pc += 4
                        },
                        ->(vm: VM) {
                          "ADD %i %i %i" % [vm.mem[vm.pc+1], vm.mem[vm.pc+2], vm.mem[vm.pc+3]]
                        }),
        2 => Opcode.new(:mul,
                        4,
                        ->(vm: VM) {
                          x = vm.mem[vm.pc+1]
                          y = vm.mem[vm.pc+2]
                          dest = vm.mem[vm.pc+3]
                          vm.mem[dest] = vm.mem[x] * vm.mem[y]
                          vm.pc += 4
                        },
                        ->(vm: VM) {
                          "MUL %i %i %i" % [vm.mem[vm.pc+1], vm.mem[vm.pc+2], vm.mem[vm.pc+3]]
                        }),
        99 => Opcode.new(:halt,
                         1,
                         ->(vm: VM) {
                           vm.halted = true
                           1
                         },
                         ->(vm: VM) {
                           "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? Integer }
      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
    

    This should make it easy to add things like a registers and a stack, along with the corresponding opcodes if/when we get to that point, as well as providing hooks for debugging/disassembly (something that I didn't do early enough in my first attempts at the Synacor puzzle, and made later bits very challenging).

    As an aside, apparently the markdown/highlighter doesn't support Crystal syntax specifically, I've used Ruby here and it looks fine though. I guess I was just doing the preview wrong? It works fine now.

    3 votes
    1. [2]
      krg
      Link Parent
      Well, I've read the problem for part 1 and thought it'd be a fairly straight-forward process..but, hot damn, your solution is robust! Pretty nice.

      Well, I've read the problem for part 1 and thought it'd be a fairly straight-forward process..but, hot damn, your solution is robust! Pretty nice.

      1. Crespyl
        Link Parent
        Haha, yeah it can be solved with little more than a switch/case, and doing that will have better performance than mine (there's a lot of indirection). At this point it's entirely unnecessary to...

        Haha, yeah it can be solved with little more than a switch/case, and doing that will have better performance than mine (there's a lot of indirection). At this point it's entirely unnecessary to create the VM struct or reify the opcodes the way I have, but I'm hoping that I'll have an excuse to use it in a future puzzle.

        Also, I just now realized that the Eric Wastl behind Advent of Code is, in fact, the same Eric Wastl behind the Synacor puzzle, which explains a few things. :P

        1 vote
  3. Crestwave
    (edited )
    Link
    Using awk again. Part 2 #!/usr/bin/awk -f { prog = prog $0 } END { split(prog, init, ",") for (i = 0; i < 100; ++i) { for (j = 0; j < 100; ++j) { for (k = 1; k <= length(init); ++k) mem[k-1] =...

    Using awk again.

    Part 2
    #!/usr/bin/awk -f
    { prog = prog $0 }
    
    END {
        split(prog, init, ",")
        for (i = 0; i < 100; ++i) {
            for (j = 0; j < 100; ++j) {
                for (k = 1; k <= length(init); ++k)
                    mem[k-1] = init[k]
    
                mem[1] = i
                mem[2] = j
    
                for (ptr = 0; ptr < length(mem); ptr += 4) {
                    if (mem[ptr] == 1) {
                        mem[mem[ptr+3]] = mem[mem[ptr+1]] + mem[mem[ptr+2]]
                    } else if (mem[ptr] == 2) {
                        mem[mem[ptr+3]] = mem[mem[ptr+1]] * mem[mem[ptr+2]]
                    } else if (mem[ptr] == 99) {
                        break
                    } else {
                        exit(1)
                    }
                }
    
                if (mem[0] == 19690720) {
                    print 100 * i + j
                    exit
                }
            }
        }
    }
    
    3 votes
  4. [7]
    Gyrfalcon
    (edited )
    Link
    My solution to both parts using Python. I had some trouble with my lists not behaving as expected, and then learned that deep copy is a good idea. Parts 1 and 2 import copy def...

    My solution to both parts using Python. I had some trouble with my lists not behaving as expected, and then learned that deep copy is a good idea.

    Parts 1 and 2
    import copy
    
    def get_operands(instructions, pointer):
        op_1_location = instructions[pointer+1]
        op_2_location = instructions[pointer+2]
        out_location = instructions[pointer+3]
    
        op_1 = instructions[op_1_location]
        op_2 = instructions[op_2_location]
    
        return [op_1, op_2, out_location]
    
    def write_result(instructions, result, location):
    
        instructions[location] = result
        return instructions
    
    def add_instruction(instructions, pointer):
        operands = get_operands(instructions, pointer)
    
        result = operands[0] + operands[1]
        return write_result(instructions, result, operands[2])
    
    def mult_instruction(instructions, pointer):
        operands = get_operands(instructions, pointer)
    
        result = operands[0] * operands[1]
        return write_result(instructions, result, operands[2])
    
    def process_instructions(instructions):
    
        pointer = 0
    
        while True:
    
            if pointer > (len(instructions) - 1):
                break
            elif instructions[pointer] == 99:
                break
            elif instructions[pointer] == 1:
                instructions = add_instruction(instructions, pointer)
            elif instructions[pointer] == 2:
                instructions = mult_instruction(instructions, pointer)
    
            pointer += 4
    
        return instructions
    
    input_file = "IntcodeInput.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])
    
    original_instructions = copy.deepcopy(instructions)
    
    output = process_instructions(instructions)
    
    
    print(output)
    
    results = []
    
    for noun in range(100):
        for verb in range(100):
            test_instructions = copy.deepcopy(original_instructions)
            test_instructions[1] = noun
            test_instructions[2] = verb
            output = process_instructions(test_instructions)
    
            if output[0] == 19690720:
                print(100 * noun + verb)
    
    3 votes
    1. [2]
      cfabbro
      Link Parent
      I think you need an extra line in between the </summary> and your codeblock.

      I think you need an extra line in between the </summary> and your codeblock.

      2 votes
      1. Gyrfalcon
        Link Parent
        Thanks for pointing that out. Fixed!

        Thanks for pointing that out. Fixed!

        1 vote
    2. [2]
      hhh
      Link Parent
      while trying to solve the same issue I also tried that, but some more googling showed that deep copy is better reserved for more complex objects. for lists it's an order of magnitude or two slower...

      deep copy is a good idea

      while trying to solve the same issue I also tried that, but some more googling showed that deep copy is better reserved for more complex objects. for lists it's an order of magnitude or two slower than just copying the contents of the list. Deimos's comment here is a good explanation of one way to do that.

      2 votes
      1. Gyrfalcon
        Link Parent
        Ah okay! I wasn't sure reading things online if a regular copy was going to be enough, but I will try to keep in mind that it is for the future.

        Ah okay! I wasn't sure reading things online if a regular copy was going to be enough, but I will try to keep in mind that it is for the future.

        1 vote
    3. [2]
      psi
      Link Parent
      Deepcopy isn't necessary, but maybe it's preferable for some reason unbeknownst to me? Here's my python solution. Part 1 intcode = [...] def parser(arr, index): if arr[index] == 1:...

      Deepcopy isn't necessary, but maybe it's preferable for some reason unbeknownst to me? Here's my python solution.

      Part 1
      intcode = [...]
      
      def parser(arr, index):
          if arr[index] == 1:
              arr[arr[index+3]] = arr[arr[index+1]] + arr[arr[index+2]]
          elif arr[index] == 2:
              arr[arr[index+3]] = arr[arr[index+1]] *arr[arr[index+2]]
          return arr
      
      j = 0
      while True:
          if intcode[j] != 99:
              parser(intcode, j)
          else:
              break
          j = j+ 4
          
      print(intcode[0])
      
      1 vote
      1. Gyrfalcon
        Link Parent
        My need for copying came in in the second part, as I needed to iterate and change the initial conditions. I didn't realize that my list would be modified inside the function, I got it into my head...

        My need for copying came in in the second part, as I needed to iterate and change the initial conditions. I didn't realize that my list would be modified inside the function, I got it into my head that Python passes by value but of course a list is an object. Strictly speaking I don't need deepcopy as per the discussion with @hhh, but it's what I found that worked.

        2 votes
  5. unknown user
    Link
    Copy pasted from my original comment in the original thread Day 2: 1202 Program Alarm I really liked this one. It was much harder to wrap my head around how the intcode actually worked than to...

    Copy pasted from my original comment in the original thread

    Day 2: 1202 Program Alarm

    I really liked this one. It was much harder to wrap my head around how the intcode actually worked than to program it.

    Part 1
    data = [] #The input was given in something very similar to a python array so I just copy pasted it in here. To save space I haven't included it in the post.
    
    
    ipointer = 0
    ipointermax = (len(data) - 1) / 4#-1 because of 99 at the end.
    #print(ipointermax)
    
    
    for i in range(int(ipointermax)):#Probably could have just done a while loop that went until it found 99.
        #print('ipointer', ipointer)
        #print(data)
        val1 = data[data[ipointer + 1]]
        val2 = data[data[ipointer + 2]]
        #print('op', data[ipointer])
        #print('v1', val1)
        #print('v2', val2)
        if data[ipointer] == 1:
            data[data[ipointer + 3]] = val1 + val2
            #print(val1 + val2)
        elif data[ipointer] == 2:
            data[data[ipointer + 3]] = val1 * val2
            #print(val1 * val2)
        elif data[ipointer] == 99:
            print('STOP')
            print('Answer is', data[0])
            break
        else:
            print('Something has gone horribly and terribly wrong')
            break
        ipointer = ipointer + 4
    

    I didn't include the actual input data in this post. I really liked this challenge.

    One thing that tripped me up though. The challenge said:

    To do this, before running the program, replace position 1 with the value 12 and replace position 2 with the value 2.

    I didn't read the question properly, never changed that second value and spent a good 10 minutes manually checking that it was properly processing the intcode, before reading the question again and facepalming at my own negligence.

    Part 2
    def calculate(noun,verb):
        data = [] #Again, not including my actual input data, it's long
        ipointer = 0
        ipointermax = (len(data) - 1) / 4#-1 because of 99 at the end.
        #print(ipointermax)
        data[1] = noun
        data[2] = verb
    
        for i in range(int(ipointermax)):
            #print('ipointer', ipointer)
            #print(data)
            val1 = data[data[ipointer + 1]]
            val2 = data[data[ipointer + 2]]
            #print('op', data[ipointer])
            #print('v1', val1)
            #print('v2', val2)
            if data[ipointer] == 1:
                data[data[ipointer + 3]] = val1 + val2
                #print(val1 + val2)
            elif data[ipointer] == 2:
                data[data[ipointer + 3]] = val1 * val2
                #print(val1 * val2)
            elif data[ipointer] == 99:
                #print('STOP')
                #print('Answer is', data[0])
                return data[0]
            else:
                print('Something has gone horribly and terribly wrong')
                break
            ipointer = ipointer + 4
            
    
    
    verb = 0
    noun = 0
    
    for verb in range(100):
        for noun in range(100):
            if calculate(noun,verb) == 19690720:
                print('Answer is', 100 * noun + verb)
    

    The first part is the same as part 1, but now it is part of a function that gets run over and over by a double for loop. I'm sure if you were mathematically talented you could probably use clever maths to solve this with a method that isn't brute force. But I'm not that smart.

    3 votes
  6. 9000
    (edited )
    Link
    Language: Rust I just refactored and extended my initial solution to give me the answer to both parts, so i don't have a Part 1/2 distinction. I used this as an opportunity to get a little more...

    Language: Rust

    I just refactored and extended my initial solution to give me the answer to both parts, so i don't have a Part 1/2 distinction.

    I used this as an opportunity to get a little more used to streams! I haven't really used them at all before, but structured file parsing is a good use case where streams prevent me from allocating unnecessary Vecs.

    Solution
    /* day_2
     *
     * USAGE
     *     day_2 [filename]
     * 
     * Runs the Intcode program at `filename`.
     */
    use std::env;
    use std::fs;
    use std::vec::Vec;
    
    const GOAL: usize = 19690720;
    
    fn main() {
        // Get filename
        let args: Vec<String> = env::args().collect();
        let filename = "input".to_string();
        let filename = args.get(1).unwrap_or(&filename);
    
        // Parse instructions
        let instructions = fs::read_to_string(filename).unwrap();
        let instructions: Vec<usize> = instructions.split(',')
            .map(|x| x.trim().parse().unwrap())
            .collect();
    
        // Constants given in problem for part 1
        println!("Part 1: {}", run_intcode(&instructions, 12, 2));
    
        for noun in 0..100 {
            for verb in 0..100 {
                if run_intcode(&instructions, noun, verb) == GOAL {
                    println!("Part 2: noun = {}, verb = {}, answer = {}", noun, verb, 100 * noun + verb);
                    return;
                }
            }
        }
    }
    
    fn run_intcode(instructions: &Vec<usize>, noun: usize, verb: usize) -> usize {
        let mut instructions = instructions.clone();
        instructions[1] = noun;
        instructions[2] = verb; 
    
        let mut index = 0;
        while index < instructions.len() {
            match instructions[index] {
                1 => { // Add
                    let num_1 = instructions[instructions[index + 1]];
                    let num_2 = instructions[instructions[index + 2]];
                    let dest = instructions[index + 3];
                    instructions[dest] = num_1 + num_2;
                }
                2 => { // Multiply
                    let num_1 = instructions[instructions[index + 1]];
                    let num_2 = instructions[instructions[index + 2]];
                    let dest = instructions[index + 3];
                    instructions[dest] = num_1 * num_2;
                }
                99 => { // End
                    return instructions[0];
                }
                _ => { // Error
                    eprintln!("ERROR: Received instruction {} at position {}", instructions[index], index);
                }
            }
            index += 4;
        }
    
        eprintln!("WARNING: End of memory! Did not end at opcode 99.");
        instructions[0]
    }
    
    2 votes
  7. heady
    Link
    Part 1 import csv with open('input', 'r') as csvfile: int_codes = {} csv_codes = csv.reader(csvfile, delimiter=',') i = 0 for row in csv_codes: for code in row: int_codes[i] = int(row[i]) i += 1...
    Part 1
    import csv
    
    with open('input', 'r') as csvfile:
        int_codes = {}
        csv_codes = csv.reader(csvfile, delimiter=',')
        i = 0
        for row in csv_codes:
            for code in row:
                int_codes[i] = int(row[i])
                i += 1
    
    ##  before running the program, replace position 1 with the value 12 and replace position 2 with the value 2
    int_codes[1] = 12
    int_codes[2] = 2
    ##
    
    def op_code_1(code1, code2, code3):
        int_codes[code3] = int_codes[code1] + int_codes[code2]
        return False
        
    def op_code_2(code1, code2, code3):
        int_codes[code3] = int_codes[code1] * int_codes[code2]
        return False
    
    def op_code_99():
        return True
    
    def op_codes(opcode, code1, code2, code3):
        if opcode == 1:
            return op_code_1(code1, code2, code3)
        elif opcode == 2:
            return op_code_2(code1, code2, code3)
        elif opcode == 99:
            return op_code_99()
        else:
            print("ruh roh " + str(opcode))
            return True
            
    i = 0
    execute_order_99 = False
    while execute_order_99 == False and int_codes[i]:
            execute_order_99 = op_codes(int_codes[i], int_codes[i+1], int_codes[i+2], int_codes[i+3])
            i += 4
    
    print(int_codes[0])
    
    On part 2 I struggled as my design from part 1 was unwieldy to adapt.

    For the life of me I'm still not sure why I needed a iterating copy loop to reset the data.

    In my first attempt I passed the original memory state into functions to be modified locally but it would adopt false values for reasons unknown.

    Part 2
    import csv
    import sys
    
    with open('input', 'r') as csvfile:
        original_state = {}
        csv_codes = csv.reader(csvfile, delimiter=',')
        i = 0
        for row in csv_codes:
            for code in row:
                original_state[i] = int(row[i])
                i += 1
        inputs = i
        
    def op_code_1(int_codes, code1, code2, code3):
        int_codes[code3] = int_codes[code1] + int_codes[code2]
        return int_codes
        
    def op_code_2(int_codes, code1, code2, code3):
        int_codes[code3] = int_codes[code1] * int_codes[code2]
        return int_codes
    
    for noun in range(100):
        for verb in range(100):
            i = 0
            int_codes = {}
            while i < inputs:
                int_codes[i] = original_state[i]
                i += 1
            print(int_codes)
            int_codes[1] = noun
            int_codes[2] = verb
            i = 0
            execute_order_99 = False
            while execute_order_99 == False and int_codes[i]:
                if int_codes[i] == 1:
                    int_codes = op_code_1(int_codes, int_codes[i+1], int_codes[i+2], int_codes[i+3])
                elif int_codes[i] == 2:
                    int_codes = op_code_2(int_codes, int_codes[i+1], int_codes[i+2], int_codes[i+3])
                else:
                    execute_order_99 = True
                i += 4
            if int_codes[0] == 19690720:
                sys.exit("solution!: " + str(100 * noun + verb) + " noun: " + str(noun) + " verb:" + str(verb))
    
    2 votes