Advent of Code 2019 馃く (讬讜诐 7)

讘讛爪诇讞讛 :slight_smile:
讬讜诐 7

2 诇讬讬拽讬诐
讞诇拽 1
import operator


def int_input(inputs=None):
    if inputs == None or inputs == []:
        return int(input())
    else:
        return inputs.pop(0)


def intcode_computer(instructions, user_inputs=None, pass_print=False):
    temp = instructions.copy()
    STATES = {
        1: {'func': operator.add, 'inputs': 3, 'index': 4, 'type': 0},
        2: {'func': operator.mul, 'inputs': 3, 'index': 4, 'type': 0},
        3: {'func': int_input, 'inputs': 1, 'index': 2, 'type': 1},
        4: {'func': print, 'inputs': 1, 'index': 2, 'type': 2},
        5: {'func': operator.truth, 'inputs': 2, 'index': 3, 'type': 3},
        6: {'func': operator.not_, 'inputs': 2, 'index': 3, 'type': 3},
        7: {'func': operator.lt, 'inputs': 3, 'index': 4, 'type': 4},
        8: {'func': operator.eq, 'inputs': 3, 'index': 4, 'type': 4},
             }

    outputs = []
    index = 0
    while True:
        opcode = str(temp[index]).zfill(5)
        state = int(opcode[-2:])
        if state != 99:
            inputs = [temp[index + i + 1] for i in range(STATES[state]['inputs'])]
            for pos in range(len(inputs) - 1):
                if int(opcode[-3 - pos]) == 0:
                    inputs[pos] = temp[inputs[pos]]
            if STATES[state]['type'] == 0:
                temp[inputs[-1]] = STATES[state]['func'](*inputs[:-1])
            elif STATES[state]['type'] == 1:
                if user_inputs is not None:
                    inputs = user_inputs + inputs
                temp[inputs[-1]] = STATES[state]['func'](inputs[:-1])
                if user_inputs is not None and user_inputs != []: 
                    user_inputs.pop(0)
            elif STATES[state]['type'] == 2:
                if int(opcode[-3]) == 0:
                    inputs[0] = temp[inputs[0]]
                if not pass_print:
                    STATES[state]['func'](inputs[0])
                outputs.append(inputs[0])
            elif STATES[state]['type'] == 4:
                if STATES[state]['func'](*inputs[:-1]):
                    temp[inputs[-1]] = 1
                else:
                    temp[inputs[-1]] = 0
            index += STATES[state]['index']
            if STATES[state]['type'] == 3:
                if STATES[state]['func'](*inputs[:-1]):
                    if int(opcode[-4]) == 0:
                        index = temp[inputs[-1]]
                    else:
                        index = inputs[-1]
        else:
            if outputs:
                return outputs
            else:
                return temp


def calc_signal(software_input, sequence_list, pass_print=False):
    output = 0
    for digit in sequence_list:
        inputs = [digit, output]
        output = intcode_computer(software_input, inputs, pass_print)[0]
    return output


def find_highest_signal(software_input, range_start, range_end, pass_print=False):
    all_sequences = [[d0, d1, d2, d3, d4]
                     for d0 in range(range_start, range_end)
                     for d1 in range(range_start, range_end)
                     if d1 != d0
                     for d2 in range(range_start, range_end)
                     if d0 != d2 != d1
                     for d3 in range(range_start, range_end)
                     if d0 != d3 != d1 and d3 != d2
                     for d4 in range(range_start, range_end)
                     if d0 != d4 != d1 and d2 != d4 != d3
                    ]

    max_signal = 0
    for sequence in all_sequences:
        signal = calc_signal(software_input, sequence, pass_print)
        if signal > max_signal:
            max_signal = signal

    return max_signal


with open('Day 7.txt', 'r') as file_handler:
    challenge_input = list(map(int, file_handler.read().split(',')))


find_highest_signal(challenge_input, 0, 5, pass_print=True)

讛驻注诐 讛诐 谞讬爪讞讜 讗讜转讬. 讗谞讬 驻砖讜讟 诇讗 诪讘讬谉 诪讛 专讜爪讬诐 讘讞诇拽 2 讜讜讬转专转讬 诇讘谞转讬讬诐.
讗讞讝讜专 诇讝讛 讘讛诪砖讱 注诐 专讗砖 谞拽讬.

诇讬讬拽 1

:star2:

讞诇拽 1
#day7
#part 1
import itertools
import operator

PRO_CODE = list(range(1, 9))


def get_intcode_program(intcode):
    with open(intcode, "r") as intcode_program:
        intcode_program_read = intcode_program.read()
    intcode_program_read = list(map(int, intcode_program_read.split(",")))
    return intcode_program_read


def opcode_3_4(intcode_pro, code, signal):
    if intcode_pro[code] % 100 == 3:
        intcode_pro[intcode_pro[code + 1]] = signal
        return True
    elif intcode_pro[code] % 1000 == 4:
        return False
    elif intcode_pro[code] % 1000 == 104:
        return False
    return True


def opcode_1_2(int_code_pro, code, a, b):
    if code == 1:
        return operator.add(int_code_pro[a], int_code_pro[b])
    elif code == 2:
        return operator.mul(int_code_pro[a], int_code_pro[b])


def opcode_5_6(int_code_pro, code, a, b):
    if int_code_pro[code] % 100 == 5:
        if int_code_pro[a] != 0:
            return int_code_pro[b]
    elif int_code_pro[code] % 100 == 6:
        if int_code_pro[a] == 0:
            return int_code_pro[b]
    return code + 3


def opcode_7_8(int_code_pro, code, a, b):
    if int_code_pro[code] % 100 == 7:
        if int_code_pro[a] < int_code_pro[b]:
            return 1
    elif int_code_pro[code] % 100 == 8:
        if int_code_pro[a] == int_code_pro[b]:
            return 1
    return 0


def retrieve_parameters_mode_and_run(int_code_pro, pos, number):
    number = str(number).zfill(5)
    if number[2] == "0":
        a = int_code_pro[pos + 1]
    else:
        a = pos + 1
    if number[1] == "0":
        b = int_code_pro[pos + 2]
    else:
        b = pos + 2
    return a, b


def run_code(intcode_pro, signal, signal2):
    code = 0
    counti = 0
    while code < len(intcode_pro):
        if intcode_pro[code] % 100 in PRO_CODE[0:2]:
            a, b = retrieve_parameters_mode_and_run(intcode_pro, code, intcode_pro[code])
            intcode_pro[intcode_pro[code + 3]] = opcode_1_2(intcode_pro, intcode_pro[code] % 100, a, b)
            code += 4
        elif intcode_pro[code] % 100 in PRO_CODE[2:4]:
            if True:
                if counti == 1:
                    signal = signal2
                counti = 1
            if not opcode_3_4(intcode_pro, code, signal):
                if intcode_pro[code + 2] == 99:
                    if intcode_pro[code] == 4:
                        return (intcode_pro[intcode_pro[code + 1]], intcode_pro)
                    return (intcode_pro[code + 1], intcode_pro)
                print("Error")
                return False
            code += 2
        elif intcode_pro[code] % 100 in PRO_CODE[4:6]:
            a, b = retrieve_parameters_mode_and_run(intcode_pro, code, intcode_pro[code])
            code = opcode_5_6(intcode_pro, code, a, b)
        elif intcode_pro[code] % 100 in PRO_CODE[6:8]:
            a, b = retrieve_parameters_mode_and_run(intcode_pro, code, intcode_pro[code])
            value = opcode_7_8(intcode_pro, code, a, b)
            intcode_pro[intcode_pro[code + 3]] = value
            code += 4


intcode_prog = get_intcode_program("resources//day7.txt")
all_possibilites = itertools.permutations("01234", 5)
maximum = 0
for possible in all_possibilites:
    output = 0
    for digit in possible:
        output, intcode_prog = run_code(intcode_prog, int(digit), output)
    if output > maximum:
        maximum = output
print(f"answer part 1 : {maximum}")
诇讬讬拽 1

讞诇拽 2 讬讞讻讛 诇诪讞专 讻谞专讗讛. 讘讬谞转讬讬诐 讝讛 谞讞诪讚 诪讗讜讚 :slight_smile:

讞诇拽 1
from itertools import permutations 


# *************************************** IntCode Computer ************************************************
def get_input(path):
    with open(path, 'r') as puzzle_input:
        return puzzle_input.read().split(',')


def run_input(puzzle_input, op_dict, inputs):
    op_code = 0
    i = 0
    while i < len(puzzle_input) and puzzle_input[i] != '99':
        op_code = puzzle_input[i]
        puzzle_input, to_increase, to_set = handle_op_code(op_code, i, puzzle_input, op_dict, inputs)
        
        if to_increase:
            i += int(to_increase)
        else:
            i = int(to_set)
        if puzzle_input == -1:
            return -1
        
    return puzzle_input


def handle_op_code(op_code, op_code_idx, puzzle_input, op_dict, inputs):
    op = get_operation(op_code)
    op_code = op_code.zfill(5)
    mode1 = op_code[2]
    mode2 = op_code[1]
    mode3 = op_code[0]
    
    if op == 99:
        return puzzle_input, _, 0
    elif op == -1:
        return puzzle_input, _, 0
    
    elif op == 3:
        return handle_three(op_code_idx, puzzle_input, inputs), 2, 0
    
    elif op == 4:
        if int(op_code.zfill(5)[2]):
            mode = 1
        else:
            mode = 0
        return handle_four(op_code_idx, puzzle_input, mode), 2, 0
    
    elif op == 5:
        to_set = handle_five(op_code_idx, puzzle_input, mode1, mode2, mode3)
        return puzzle_input, 0, to_set
            
    
    elif op == 6:
        to_set = handle_six(op_code_idx, puzzle_input, mode1, mode2, mode3)
        return puzzle_input, 0, to_set
    
    elif op == 7:
        return handle_seven(op_code_idx, puzzle_input, mode1, mode2, mode3), 4, 0
    
    elif op == 8:
        return handle_eight(op_code_idx, puzzle_input, mode1, mode2, mode3), 4, 0
        
    else:
        return handle_math_ops(op_dict, op, op_code_idx, puzzle_input, mode1, mode2, mode3), 4,0



def get_operation(op_code):
    if op_code[-1] == '1':
        return '+'
    elif op_code[-1] == '2':
        return '*'
    elif op_code[-1] == '3':
        return 3
    elif op_code[-1] == '4':
        return 4
    elif op_code[-1] == '5':
        return 5
    elif op_code[-1] == '6':
        return 6
    elif op_code[-1] == '7':
        return 7
    elif op_code[-1] == '8':
        return 8
    elif op_code[-1] == '9':
        return 99
    else:
        return -1


def get_args(op_code_idx, puzzle_input, modes, num_args):
    args = []
    if num_args == 2:
        for i in range(len(modes)):
            if not int(modes[i]):
                args.append(int(puzzle_input[int(puzzle_input[op_code_idx + i + 1])]))
            else:
                args.append(int(puzzle_input[op_code_idx + i + 1]))
        args.append(-1)

    else:
        for i in range(len(modes) - 1):
            if not int(modes[i]):
                args.append(int(puzzle_input[int(puzzle_input[op_code_idx + i + 1])]))
            else:
                args.append(int(puzzle_input[op_code_idx + i + 1]))
        args.append(op_code_idx + 3)
    
    return args


def handle_three(op_code_idx, puzzle_input, inputs):
    integer = inputs[0]
    inputs.pop(0)
    puzzle_input[int(puzzle_input[op_code_idx + 1])] = integer
    return puzzle_input


def handle_four(op_code_idx, puzzle_input, mode):
    if mode:
        return puzzle_input[op_code_idx + 1]
    else:
        return puzzle_input[int(puzzle_input[op_code_idx + 1])]


def handle_five(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2]
    arg1, arg2, _ = get_args(op_code_idx, puzzle_input, modes, len(modes))    
    if arg1:
        return arg2
    else:
        return op_code_idx + 3


def handle_six(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2]
    arg1, arg2, _ = get_args(op_code_idx, puzzle_input, modes, len(modes))
    if not arg1:
        return arg2
    else:
        return op_code_idx + 3


def handle_seven(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2, mode3]
    arg1, arg2, arg3 = get_args(op_code_idx, puzzle_input, modes, len(modes))    
    if arg1 < arg2:
        puzzle_input[int(puzzle_input[arg3])] = '1'
    else:
        puzzle_input[int(puzzle_input[arg3])] = '0'
    
    return puzzle_input


def handle_eight(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2, mode3]
    arg1, arg2, arg3 = get_args(op_code_idx, puzzle_input, modes, len(modes))
    if arg1 == arg2:
        puzzle_input[int(puzzle_input[arg3])] = '1'
    else:        
        puzzle_input[int(puzzle_input[arg3])] = '0'
        
    return puzzle_input


def handle_math_ops(op_dict, op, op_code_idx, puzzle_input, mode1='0', mode2='0', mode3='0'):
    arg3 = int(puzzle_input[op_code_idx + 3])
    arg2 = set_arg(op_code_idx + 2, puzzle_input, mode2)
    arg1 = set_arg(op_code_idx + 1, puzzle_input, mode1)
    
    
    operation_result = op_dict.get(op)(arg1, arg2)
    puzzle_input[arg3] = str(op_dict.get(op)(arg1, arg2))
    return puzzle_input


def set_arg(idx, puzzle_input, mode):
    if mode == '0':
        first_input_idx = int(puzzle_input[idx])
        arg = int(puzzle_input[first_input_idx])
    else:
        arg = int(puzzle_input[idx])
    return arg


def add(num1, num2):
    return num1 + num2


def multiply(num1, num2):
    return num1 * num2
# *************************************** IntCode Computer ************************************************


def get_permutations(numbers):
    perm = permutations(numbers) 
    for i in list(perm): 
        yield i 


def get_best(puzzle_input, op_dict):
    results = []
    perms = get_permutations(range(0, 5))
    for perm in perms:
        res = '0'
        for i in range(5):
            inputs = [str(perm[i]), res]
            res = run_input(puzzle_input, op_dict, inputs)
        results.append(res)
    
    results = [int(result) for result in results]
    return max(results)
 

def main():
    puzzle_input = get_input('input.txt')
    op_dict = op_dict = {'+': add, '*': multiply}
    first_part_result = get_best(puzzle_input, op_dict)
    print(f'First part result: {first_part_result}')


main()
诇讬讬拽 1

讻讘专 讻诪注讟 讛砖讗专转讬 讗转 讛住讜祝 砖诇 讝讛 诇诪讞专 讜讬爪讗转讬 诇专讜抓鈥 讜讗讝 讘讝诪谉 讛专讬爪讛 讗讬讻砖讛讜 注诇讛 诇讬 专注讬讜谉 讜讝讛 注讘讚!

讞诇拽 2
from itertools import permutations 


# *************************************** IntCode Computer ************************************************
def get_input(path):
    with open(path, 'r') as puzzle_input:
        return puzzle_input.read().split(',')


def run_input(puzzle_input, op_dict, inputs, idx=0):
    op_code = 0
    i = idx
    while i < len(puzzle_input) and puzzle_input[i] != '99':
        op_code = puzzle_input[i]
        puzzle_input, to_increase, to_set = handle_op_code(op_code, i, puzzle_input, op_dict, inputs)

        if to_increase:
            i += int(to_increase)
        else:
            i = int(to_set)
        if puzzle_input == -1:
            return -1

    if type(puzzle_input) == str:
        return puzzle_input, i
    
    if puzzle_input[i] == '99':
        return 99, i


def handle_op_code(op_code, op_code_idx, puzzle_input, op_dict, inputs):
    op = get_operation(op_code)
    op_code = op_code.zfill(5)
    mode1 = op_code[2]
    mode2 = op_code[1]
    mode3 = op_code[0]

    if op == 99:
        return puzzle_input, _, 0
    elif op == -1:
        return puzzle_input, _, 0

    elif op == 3:
        return handle_three(op_code_idx, puzzle_input, inputs), 2, 0

    elif op == 4:
        if int(op_code.zfill(5)[2]):
            mode = 1
        else:
            mode = 0
        return handle_four(op_code_idx, puzzle_input, mode), 2, 0

    elif op == 5:
        to_set = handle_five(op_code_idx, puzzle_input, mode1, mode2, mode3)
        return puzzle_input, 0, to_set


    elif op == 6:
        to_set = handle_six(op_code_idx, puzzle_input, mode1, mode2, mode3)
        return puzzle_input, 0, to_set

    elif op == 7:
        return handle_seven(op_code_idx, puzzle_input, mode1, mode2, mode3), 4, 0

    elif op == 8:
        return handle_eight(op_code_idx, puzzle_input, mode1, mode2, mode3), 4, 0

    else:
        return handle_math_ops(op_dict, op, op_code_idx, puzzle_input, mode1, mode2, mode3), 4,0



def get_operation(op_code):
    if op_code[-1] == '1':
        return '+'
    elif op_code[-1] == '2':
        return '*'
    elif op_code[-1] == '3':
        return 3
    elif op_code[-1] == '4':
        return 4
    elif op_code[-1] == '5':
        return 5
    elif op_code[-1] == '6':
        return 6
    elif op_code[-1] == '7':
        return 7
    elif op_code[-1] == '8':
        return 8
    elif op_code[-1] == '9':
        return 99
    else:
        return -1


def get_args(op_code_idx, puzzle_input, modes, num_args):
    args = []
    if num_args == 2:
        for i in range(len(modes)):
            if not int(modes[i]):
                args.append(int(puzzle_input[int(puzzle_input[op_code_idx + i + 1])]))
            else:
                args.append(int(puzzle_input[op_code_idx + i + 1]))
        args.append(-1)

    else:
        for i in range(len(modes) - 1):
            if not int(modes[i]):
                args.append(int(puzzle_input[int(puzzle_input[op_code_idx + i + 1])]))
            else:
                args.append(int(puzzle_input[op_code_idx + i + 1]))
        args.append(op_code_idx + 3)

    return args


def handle_three(op_code_idx, puzzle_input, inputs):
    integer = inputs[0]
    inputs.pop(0)
    puzzle_input[int(puzzle_input[op_code_idx + 1])] = integer
    return puzzle_input


def handle_four(op_code_idx, puzzle_input, mode):
    if mode:
        return puzzle_input[op_code_idx + 1]
    else:
        return puzzle_input[int(puzzle_input[op_code_idx + 1])]


def handle_five(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2]
    arg1, arg2, _ = get_args(op_code_idx, puzzle_input, modes, len(modes))    
    if arg1:
        return arg2
    else:
        return op_code_idx + 3


def handle_six(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2]
    arg1, arg2, _ = get_args(op_code_idx, puzzle_input, modes, len(modes))
    if not arg1:
        return arg2
    else:
        return op_code_idx + 3


def handle_seven(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2, mode3]
    arg1, arg2, arg3 = get_args(op_code_idx, puzzle_input, modes, len(modes))    
    if arg1 < arg2:
        puzzle_input[int(puzzle_input[arg3])] = '1'
    else:
        puzzle_input[int(puzzle_input[arg3])] = '0'
    
    return puzzle_input


def handle_eight(op_code_idx, puzzle_input, mode1, mode2, mode3):
    modes = [mode1, mode2, mode3]
    arg1, arg2, arg3 = get_args(op_code_idx, puzzle_input, modes, len(modes))
    if arg1 == arg2:
        puzzle_input[int(puzzle_input[arg3])] = '1'
    else:        
        puzzle_input[int(puzzle_input[arg3])] = '0'
        
    return puzzle_input


def handle_math_ops(op_dict, op, op_code_idx, puzzle_input, mode1='0', mode2='0', mode3='0'):
    arg3 = int(puzzle_input[op_code_idx + 3])
    arg2 = set_arg(op_code_idx + 2, puzzle_input, mode2)
    arg1 = set_arg(op_code_idx + 1, puzzle_input, mode1)
    
    
    operation_result = op_dict.get(op)(arg1, arg2)
    puzzle_input[arg3] = str(op_dict.get(op)(arg1, arg2))
    return puzzle_input


def set_arg(idx, puzzle_input, mode):
    if mode == '0':
        first_input_idx = int(puzzle_input[idx])
        arg = int(puzzle_input[first_input_idx])
    else:
        arg = int(puzzle_input[idx])
    return arg


def add(num1, num2):
    return num1 + num2


def multiply(num1, num2):
    return num1 * num2
# *************************************** IntCode Computer ************************************************


def get_permutations(numbers):
    perm = permutations(numbers) 
    for i in list(perm): 
        yield i 


def part_two(puzzle_input, op_dict):
    results = []
    perms = get_permutations(range(5, 10))
    for perm in perms:
        res = '0'
        i = 0
        j = 0
        flag = False
        amplifiers_pos = [0, 0, 0, 0, 0]
        puzzle_input_copies = []
        for k in range(5):
            puzzle_input_copies.append(puzzle_input.copy())
        
        while res != 99:
            if not flag:
                inputs = [str(perm[i]), res]
            else:
                inputs = [res]
            prev_res = res
            res, j = run_input(puzzle_input_copies[i], op_dict, inputs, amplifiers_pos[i])
            amplifiers_pos[i] = j
            if (i + 1) % 5 == 0:
                flag = True
                
            i = (i + 1) % 5
            
        results.append(prev_res)

    results = [int(result) for result in results]
    return max(results)


def main():
    puzzle_input = get_input('input.txt')
    op_dict = op_dict = {'+': add, '*': multiply}    
    second_part_result = part_two(puzzle_input, op_dict)
    print(second_part_result)


main()
2 诇讬讬拽讬诐

讗住讙讜专 讘专讘讬注讬 :slight_smile:

诇讬讬拽 1

讜讜讗讬 讜讜讗讬, 讞诇砖 驻讛 讛驻注诐 :slight_smile:
诇讗 诪转 注诇 讛驻转专讜谉 砖诇讬, 讗讘诇 注讜讘讚.
诪住讬讘讜转 砖诇 讞讜住专 讝诪谉 诇讗 爪谞讝专转讬 讚讘专讬诐 砖讗转诐 诇讗 讬讜讚注讬诐, 讗讘诇 讗讬谉 驻讛 讗讬讝讜 讟讻谞讬拽讛 诪讟讜专驻转 砖讙讜专诪转 诇讛讻讜诇 诇讛讬讜转 讬讜转专 拽诇.

import itertools
import operator
from copy import copy
from typing import (
    Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union,
)

from typing_extensions import TypedDict


Command = TypedDict(
    'Command',
    {
        'size': int, 'function': Callable[..., Any],
        'jump': bool, 'void': int, 'method': bool,
    },
    total=False,
)
ParsedCmds = Iterator[int]
vmethod = {'void': 1, 'method': True}


class IntcodeMachine:
    OPCODES: Dict[int, Command] = {
        1: {'size': 4, 'function': operator.add},
        2: {'size': 4, 'function': operator.mul},
        3: {'size': 2, 'function': lambda s: s.get_input(), 'method': True},
        4: {'size': 2, 'function': lambda s, v: s.add_output(v), **vmethod},
        5: {'size': 3, 'function': operator.truth, 'jump': True},
        6: {'size': 3, 'function': operator.not_, 'jump': True},
        7: {'size': 4, 'function': lambda x, y: bool(operator.lt(x, y))},
        8: {'size': 4, 'function': lambda x, y: bool(operator.eq(x, y))},
    }

    def __init__(self, code: List[int], inputs: Iterable[int]) -> None:
        self.eip = 0  # Instructions pointer
        self.code = code
        self._inputs = iter(inputs)
        self._outputs_index = 0
        self._outputs: List[int] = []

    @property
    def opcode(self) -> int:
        return self.code[self.eip] % 100

    @property
    def is_halt(self) -> bool:
        return self.opcode == 99

    @property
    def is_jump(self) -> bool:
        return self.opcode_get('jump', False)

    def add_inputs(self, inputs: Union[int, List[int]]) -> None:
        if not isinstance(inputs, (list, tuple)):
            inputs = [inputs]
        self._inputs = itertools.chain(self._inputs, inputs)

    def get_input(self) -> Optional[int]:
        try:
            return next(self._inputs)
        except StopIteration:
            return None

    def add_output(self, outputs: Union[int, List[int]]) -> None:
        if not isinstance(outputs, (list, tuple)):
            outputs = [outputs]
        self._outputs.extend(outputs)

    def get_outputs(self) -> List[int]:
        outputs = self._outputs[self._outputs_index:]
        self._outputs_index = len(self._outputs)
        return outputs

    @property
    def modes(self) -> Tuple[int, int, int]:
        modes_part = self.code[self.eip] // 100
        return modes_part % 10, modes_part // 10 % 10, modes_part // 100

    def opcode_get(self, prop: str, default: Any = None) -> Any:
        return self.OPCODES[self.opcode].get(prop, default)

    @property
    def argc(self) -> int:
        return self.opcode_get('size', 2) - 2

    @property
    def args(self) -> List[int]:
        offset = self.argc + self.opcode_get('void', 0)
        params = self.code[self.eip + 1:self.eip + 1 + offset]
        out = []
        for param, mode in zip(params, self.modes):
            out.append(param if mode else self.code[param])
        if self.opcode_get('method'):
            out.insert(0, self)
        return out

    @property
    def write_address(self) -> Optional[int]:
        if self.opcode_get('void'):
            return None

        write_to = self.code[self.eip + 1 + self.argc]
        is_relative_jump = self.is_jump and self.modes[self.argc] == 0
        return self.code[write_to] if is_relative_jump else write_to

    def __call__(self) -> List[int]:
        while self.opcode != 99:
            op_size = self.opcode_get('size')
            args, write_to = self.args, self.write_address
            returns = self.opcode_get('function')(*args)
            if returns is None and self.opcode == 3:
                return self.get_outputs()  # Halt until you get input
            if write_to is not None and not self.is_jump:
                self.code[write_to] = returns
            should_jump = self.is_jump and returns
            self.eip = write_to if should_jump else (self.eip + op_size)
        return self.get_outputs()


def get_input() -> List[int]:
    with open('input.txt', 'r') as challenge_input:
        return list(map(int, challenge_input.read().strip().split(',')))


# Part 1
program = get_input()
outputs = []
for phases in itertools.permutations(list(range(5))):
    current_input = 0
    for phase in phases:
        current_machine = IntcodeMachine(program, [phase, current_input])
        current_input = current_machine()[-1]
        program = current_machine.code
    outputs.append(current_input)
print(max(outputs))


# Part 2
program = get_input()
max_output = 0
for phases in itertools.permutations(list(range(5, 10))):
    machines = [IntcodeMachine(copy(get_input()), [phase]) for phase in phases]
    next_input = 0
    i = 0
    while not machines[-1].is_halt:
        machines[i].add_inputs(next_input)
        next_input = machines[i]()[-1]
        i = (i + 1) % len(machines)
    max_output = max(max_output, next_input)
print(max_output)
诇讬讬拽 1

转讗诪讬谉 讗讜 诇讗 - 诪住转讘专 砖讛拽讟注 注诐 讛专讬爪讛 拽专讛 讙诐 诇讬!
拽爪转 讘讗讬讞讜专, 讗讘诇 驻转专转讬. 注讜讘专 诇-8 :star_struck:

Advent of Code, Day 7
# ADVENT OF CODE, DAY 7
from itertools import permutations

def get_intcode():
    with open('input.txt', 'r') as file:
        intcode = file.read().split(',')
    intcode = intcode_items_to_int(intcode)
    return intcode


def intcode_items_to_int(intcode):
    for i, num in enumerate(intcode):
        intcode[i] = int(intcode[i])
    return intcode


def read_instruction(code):
    opcode = int(code) % 100
    parameters = str(int(code) // 100).zfill(3)
    return opcode, parameters


def run_intcode(amp_memory, phase, intcode):
    i = 0
    j = 0
    is_phase = True
    is_ended = False
    current_output = 0
    while i < len(intcode):
        opcode, parameters = read_instruction(intcode[i])
        if opcode == 1 or opcode == 2:
            if parameters[2] == '0':
                num1 = intcode[intcode[i + 1]]
            elif parameters[2] == '1':
                num1 = intcode[i + 1]
            if parameters[1] == '0':
                num2 = intcode[intcode[i + 2]]
            elif parameters[1] == '1':
                num2 = intcode[i + 2]
            if opcode == 1:
                intcode[intcode[i + 3]] = num1 + num2
            else:
                intcode[intcode[i + 3]] = num1 * num2
            i += 4
        if opcode == 3:
            if is_phase:
                intcode[intcode[i + 1]] = phase
                is_phase = False
            else:
                intcode[intcode[i + 1]] = amp_memory[j]
                j += 1
            i += 2
        if opcode == 4:
            if parameters[2] == '0':
                current_output = intcode[intcode[i + 1]]
            elif parameters[2] == '1':
                current_output = intcode[i + 1]
            if j == len(amp_memory):
                return intcode, current_output, is_ended
            i += 2
        if opcode == 5 or opcode == 6:
            if parameters[2] == '0':
                num1 = intcode[intcode[i + 1]]
            elif parameters[2] == '1':
                num1 = intcode[i + 1]
            if opcode == 5:
                if num1 != 0:
                    if parameters[1] == '0':
                        i = intcode[intcode[i + 2]]
                    if parameters[1] == '1':
                        i = intcode[i + 2]
                else:
                    i += 3
            if opcode == 6:
                if num1 == 0:
                    if parameters[1] == '0':
                        i = intcode[intcode[i + 2]]
                    if parameters[1] == '1':
                        i = intcode[i + 2]
                else:
                    i += 3
        if opcode == 7 or opcode ==8:
            if parameters[2] == '0':
                num1 = intcode[intcode[i + 1]]
            elif parameters[2] == '1':
                num1 = intcode[i + 1]
            if parameters[1] == '0':
                num2 = intcode[intcode[i + 2]]
            elif parameters[1] == '1':
                num2 = intcode[i + 2]
            if opcode == 7:
                if num1 < num2:
                    intcode[intcode[i + 3]] = 1
                else:
                    intcode[intcode[i + 3]] = 0
            if opcode == 8:
                if num1 == num2:
                    intcode[intcode[i + 3]] = 1
                else:
                    intcode[intcode[i + 3]] = 0
            i += 4
        if opcode == 99:
            is_ended = True
            return intcode, current_output, is_ended


def feedback_loop(phases):
    options = list(permutations(phases))
    max_thrusters = 0
    correct_phases = []
    current_amp = 'a'
    for option in options:
        amp_a = [0]
        amp_b = []
        amp_c = []
        amp_d = []
        amp_e = []
        is_complete = False
        j = 0
        while not is_complete:
            for phase in option:
                intcode = get_intcode()
                if current_amp == 'a':
                    intcode, output_value, _ = run_intcode(amp_a, phase, intcode)
                    amp_b.append(output_value)
                    current_amp = 'b'
                elif current_amp == 'b':
                    intcode, output_value, _ = run_intcode(amp_b, phase, intcode)
                    amp_c.append(output_value)
                    current_amp = 'c'
                elif current_amp == 'c':
                    intcode, output_value, _ = run_intcode(amp_c, phase, intcode)
                    amp_d.append(output_value)
                    current_amp = 'd'
                elif current_amp == 'd':
                    intcode, output_value, _ = run_intcode(amp_d, phase, intcode)
                    amp_e.append(output_value)
                    current_amp = 'e'
                elif current_amp == 'e':
                    intcode, output_value, is_complete = run_intcode(amp_e, phase, intcode)
                    amp_a.append(output_value)
                    current_amp = 'a'
        if amp_a[-1] > max_thrusters:
            max_thrusters = amp_a[-1]
            correct_phases = option
    return (max_thrusters, correct_phases) 


# PART 1
print(feedback_loop([0, 1, 2, 3, 4]))
# PART 2
print(feedback_loop([5, 6, 7, 8, 9]))
2 诇讬讬拽讬诐