# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

from aocd.models import Puzzle

puzzle = Puzzle(2019, 9)
puzzle_input = [int(x) for x in puzzle.input_data.split(",")]


# +
# intcode computer from day 7

def get_opcode(value):
    # the opcode is the rightmost two digits of the first value in an instruction
    if len(str(value)) == 1: return value
    if len(str(value)) == 2: return int(str(value[1])) if str(value).startswith("0") else value
    else: return int(str(value)[len(str(value))-2:])
    

def determine_values_from_parameter_mode(parameter_mode, program, ip, ip_offset, relative_base_offset):
    parameter_mode = int(parameter_mode)
    
    if parameter_mode == 2: # relative mode
        return program[program[ip+ip_offset]+relative_base_offset]
    
    elif parameter_mode == 1: # immediate mode
        return program[ip+ip_offset]
    
    elif parameter_mode == 0: # position mode
        return program[program[ip+ip_offset]]
    
    else:
        raise ValueError("I can not understand the parameter mode for this value: {}".format(parameter_mode))
    
    
def intcode_computer(program, input_values, instruction_pointer_location):
    
    # map opcodes to parameter count
    opcode_parameter_map = dict()
    opcode_parameter_map["1"] = 3 # addition: summand1, summand2, adress of sum
    opcode_parameter_map["2"] = 3 # multiplication: factor1, factor2, adress of product
    opcode_parameter_map["3"] = 1 # input: where to store input address
    opcode_parameter_map["4"] = 1 # output: where to read output address
    opcode_parameter_map["5"] = 2 # jump-if-true: compare boolean of 2 parameters
    opcode_parameter_map["6"] = 2 # jump-if-false: compare boolean of 2 parameters
    opcode_parameter_map["7"] = 3 # less-than: compare values from 2 parameters, store 1 in position of third
    opcode_parameter_map["8"] = 3 # equals: compare values from 2 parameters, store 1 in position of third
    opcode_parameter_map["9"] = 1 # adjusts the relative base by the value of its only parameter
    
    output_value = None
        
    ip = instruction_pointer_location # instruction pointer
    relative_base_offset = 0
    
    # initialize program with a LOT more memory
    program.extend(0 for _ in range(100000))
    
    while True:
        # program[ip] is always the first value of the instruction. It contains the opcode and the parameter modes.
        # it is an integer.
        
        opcode = get_opcode(program[ip])
        
        first_instruction_value_string = str(program[ip]) 
        
        # halt
        if opcode == 99: break
            
        # addition 
        elif opcode == 1:
            while len(first_instruction_value_string) < 5: # 3 digits of parameter modes + 2 digits of opcode
                first_instruction_value_string = "0" + first_instruction_value_string
            
            # determine parameter modes
            first_instruction_value_string = first_instruction_value_string[:-2] # we dont need opcode anymore
            
            val1 = determine_values_from_parameter_mode(first_instruction_value_string[-1], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            val2 = determine_values_from_parameter_mode(first_instruction_value_string[-2], 
                                                         program, 
                                                         ip,
                                                         2,
                                                         relative_base_offset)
            
            
            if int(first_instruction_value_string[-3]) == 2: # relative mode
                program[program[ip+3]+relative_base_offset] = val1 + val2
            
            else: # position mode
                program[program[ip+3]] = val1 + val2
            
            ip += opcode_parameter_map["1"] + 1

            
        # multiplication   
        elif opcode == 2:
            while len(first_instruction_value_string) < 5: # 3 digits of parameter modes + 2 digits of opcode
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter modes
            first_instruction_value_string = first_instruction_value_string[:-2] # we dont need opcode anymore
            
            val1 = determine_values_from_parameter_mode(first_instruction_value_string[-1], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            val2 = determine_values_from_parameter_mode(first_instruction_value_string[-2], 
                                                         program, 
                                                         ip,
                                                         2,
                                                         relative_base_offset)
            
            
            if int(first_instruction_value_string[-3]) == 2: # relative mode
                program[program[ip+3]+relative_base_offset] = val1 * val2
            
            else: # position mode
                program[program[ip+3]] = val1 * val2
            
            
            ip += opcode_parameter_map["2"] + 1

            
        # store value
        elif opcode == 3:
            # Parameters that an instruction writes to will never be in immediate mode
            # so the input is always in position mode (.... well, this comment aged well)
            
            while len(first_instruction_value_string) < 3: # 1 digits of parameter modes + 2 digits of opcode
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter mode
            first_instruction_value_string = first_instruction_value_string[0] # we dont need opcode anymore
            
            if int(first_instruction_value_string) == 2: # relative mode
                program[program[ip+1]+relative_base_offset] = input_values.pop(0)
            
            else: # position mode
                program[program[ip+1]] = input_values.pop(0)

                
            ip += opcode_parameter_map["3"] + 1

        
        # output value
        elif opcode == 4:
            # i believe it is either possible to output the value at an address, or to output an immediate
            # value. Hence, the differentiation is needed here (even though it might be unnecessary)
            
            while len(first_instruction_value_string) < 3: # 1 digits of parameter modes + 2 digits of opcode
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter mode
            first_instruction_value_string = first_instruction_value_string[0] # we dont need opcode anymore
            
            output_value = determine_values_from_parameter_mode(first_instruction_value_string[0], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            ip += opcode_parameter_map["4"] + 1
            
            # return program, ip, output_value
            print("Output: {}".format(output_value))
            
            
        # jump if true
        elif opcode == 5:
            while len(first_instruction_value_string) < 4: # 2 digits of parameter modes + 2 digits of opcode
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter modes
            first_instruction_value_string = first_instruction_value_string[:-2]
            
            condition = determine_values_from_parameter_mode(first_instruction_value_string[-1], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            new_ip_position = determine_values_from_parameter_mode(first_instruction_value_string[-2], 
                                                         program, 
                                                         ip,
                                                         2,
                                                         relative_base_offset)
            
            if condition:
                ip = new_ip_position
            else:
                ip += opcode_parameter_map["5"] + 1
                
        
        # jump if false
        elif opcode == 6:
            while len(first_instruction_value_string) < 4:
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter modes
            first_instruction_value_string = first_instruction_value_string[:-2]
            
            condition = determine_values_from_parameter_mode(first_instruction_value_string[-1], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            new_ip_position = determine_values_from_parameter_mode(first_instruction_value_string[-2], 
                                                         program, 
                                                         ip,
                                                         2,
                                                         relative_base_offset)
            
            
            if not condition:
                ip = new_ip_position
            else:
                ip += opcode_parameter_map["6"] + 1
        
        # less than
        elif opcode == 7:
            while len(first_instruction_value_string) < 5:
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter modes
            first_instruction_value_string = first_instruction_value_string[:-2]
            
            val1 = determine_values_from_parameter_mode(first_instruction_value_string[-1], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            val2 = determine_values_from_parameter_mode(first_instruction_value_string[-2], 
                                                         program, 
                                                         ip,
                                                         2,
                                                         relative_base_offset)
            
            if val1 < val2:
                
                if int(first_instruction_value_string[-3]) == 2: # relative mode
                    program[program[ip+3]+relative_base_offset] = 1
            
                else: # position mode
                    program[program[ip+3]] = 1
                          
                
            else:
                if int(first_instruction_value_string[-3]) == 2: # relative mode
                    program[program[ip+3]+relative_base_offset] = 0
            
                else: # position mode
                    program[program[ip+3]] = 0
                
            ip += opcode_parameter_map["7"] + 1
                
        
        # equals
        elif opcode == 8:
            while len(first_instruction_value_string) < 5:
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter modes
            first_instruction_value_string = first_instruction_value_string[:-2]
            
            val1 = determine_values_from_parameter_mode(first_instruction_value_string[-1], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            val2 = determine_values_from_parameter_mode(first_instruction_value_string[-2], 
                                                         program, 
                                                         ip,
                                                         2,
                                                         relative_base_offset)
            
            
            
            if val1 == val2:
                
                if int(first_instruction_value_string[-3]) == 2: # relative mode
                    program[program[ip+3]+relative_base_offset] = 1
            
                else: # position mode
                    program[program[ip+3]] = 1
                          
                
            else:
                if int(first_instruction_value_string[-3]) == 2: # relative mode
                    program[program[ip+3]+relative_base_offset] = 0
            
                else: # position mode
                    program[program[ip+3]] = 0
                
            ip += opcode_parameter_map["8"] + 1
            
            
            
        # adjust relative base
        elif opcode == 9:
            while len(first_instruction_value_string) < 3: # 1 digits of parameter modes + 2 digits of opcode
                first_instruction_value_string = "0" + first_instruction_value_string
                
            # determine parameter mode            
            value = determine_values_from_parameter_mode(first_instruction_value_string[0], 
                                                         program, 
                                                         ip,
                                                         1,
                                                         relative_base_offset)
            
            
            relative_base_offset += value
            ip += opcode_parameter_map["9"] + 1
            
            
            
        
            

    # return program, ip, output_value
    return program, output_value
# -

intcode_computer(puzzle_input, [1], 0)





StarcoderdataJupyterScriptsDedupFiltered