https://adventofcode.com/2019/day/2
달에서 스윙바이를 하려고 이동하던 중, 우주선의 컴퓨터가 "1202 Program Alarm"을 띄우며 시끄럽게 울리기 시작합니다. 무전을 통해 한 요정이 상황을 설명해주었습니다. "걱정마세요. 모든 것이 정상입..." 그 순간, 컴퓨터가 폭주하기 시작합니다.
당신은 요정들에게 컴퓨터에서 수상한 연기가 나고 있다는 사실을 알려줍니다. "그 컴퓨터는 중력 보조 프로그램과 같은 **정수 프로그램(IntCode Program)**를 실행하던 컴퓨터였어요. 새로운 정수 컴퓨터를 만들 여분 부품이 충분히 있을 거에요.
정수 프로그램은 쉼표로 구분된 정수의 리스트입니다. (1,0,0,3,99
처럼요) 이를 실행하려면, 우선 리스트의 첫번재 정수를 보면 됩니다.(0
번 위치(position)라고 부릅시다.) 이 값은 **연산 코드(opcode)**이며, 1
, 2
또는 99
중 하나입니다. 이 연산코드는 무엇을 해야할지 나타냅니다. 예를들어 99는 프로그램이 완료되었고 즉시 종료되어야 한다는 뜻입니다. 그 이외의 코드가 들어온다면, 무언가 잘못되고 있다는 것입니다.
연산 코드 1
은 두 개의 위치로부터 값을 읽어들여 그것을 서로 더한 뒤, 세 번째 위치에 저장합니다. 연산 코드 뒤에 바로 이어지는 세 개의 숫자가 그 위치를 나타냅니다. 첫 번째와 두 번째 값이 읽어들일 값이 있는 위치를, 세 번째 값이 저장할 장소의 위치를 나타냅니다.
예를 들어 정수 프로그램이 1,10,20,30
을 만나면, 10번째 위치와 20번째 위치에서 값을 읽어들여, 그 값들을 더하고, 30번째 위치에 그 결과를 저장합니다.
연산 코드 2
는 연산 코드 1
과 똑같이 동작하지만, 더하는 대신 두 숫자를 곱합니다. 다시 한번 강조하지만, 연산 코드 뒤의 숫자들은 값이 아니라 값이 어디에 있는지를 나타냅니다.
한 연산 코드에 대한 처리를 끝내면, 4
개의 숫자를 건너뛰어 다음 명령어를 실행하면 됩니다.
예를 들어, 다음과 같은 프로그램이 있다고 해봅시다.
1,9,10,3,2,3,11,0,99,30,40,50
알기 쉽게 여러 줄로 나누어 보겠습니다.
1,9,10,3,
2,3,11,0,
99,
30,40,50
처음 네 숫자(1,9,10,3
)의 위치는 차례대로 0
, 1
, 2
, 3
입니다. 그리고 차례대로 첫 번째 연산코드(1
), 입력 값의 위치(9
, 10
), 더한 값을 저장할 위치(3
)을 나타냅니다. 9
번째 위치에 있는 숫자는 30
이고 10
번째 위치에 있는 숫자는 40
이므로, 이를 더하면 70이 됩니다. 이 값을 3번째 위치에 저장하면 프로그램은 다음과 같아집니다.
1,9,10,70,
2,3,11,0,
99,
30,40,50
이후 네 칸을 이동하면 다음 연산 코드인 2
에 도달할 수 있습니다. 덧셈 대신 곱셈을 하는 것만 다르고, 나머지는 똑같습니다. 곱할 값은 3번째와 11번째 위치에 있으므로, 입력 값은 70과 50입니다. 곱한 결과인 3500은 0번째 위치에 저장됩니다.
3500,9,10,70,
2,3,11,0,
99,
30,40,50
네 칸을 더 이동하면 99가 나오므로, 프로그램이 종료됩니다.
다음은 몇 가지 작은 프로그램들의 초기 값과 최종 값입니다.
1,0,0,0,99
는2,0,0,0,99
가 됩니다. (1 + 1 = 2
)2,3,0,3,99
는2,3,0,6,99
가 됩니다. (3 * 2 = 6
)2,4,4,5,99,0
는2,4,4,5,99,9801
가 됩니다. (99 * 99 = 9801
)1,1,1,4,99,5,6,0,99
는30,1,1,4,2,5,6,0,99
가 됩니다.
정상 동작하는 컴퓨터를 만들고나서, 처음 해야할 일은 중력 보조 프로그램(퍼즐 입력)을 컴퓨터에 불이 나기 전 상태인 "1202 프로그램 알람" 상태로 복원하는 것입니다. 이걸 하기위해, 프로그램을 실행하기 전에, 1
번째 위치의 값을 12
로 바꾸고, 2
번째 위치의 값을 2
로 바꿔주어야 합니다. 이후 프로그램이 종료된 뒤, 0
번 위치에는 어떤 값이 들어가있습니까?
"좋아요, 새 컴퓨터는 잘 동작하는 것 같네요. 아마 다음에도 이걸 쓸 일이 있을테니, 소중히 다루어 주세요. 원래 정수 컴퓨터는 방금 당신이 만든 것보다 더 많은 기능을 지원하지만, 그건 필요할 때 다시 알려드릴게요."
"지금은 스윙바이를 하는 것에 집중하도록 하죠. 그러려면 우선 방금 만든 것에 대한 몇 가지 용어들을 정리해야할 것 같네요."
정수 프로그램은 정수들의 리스트로 주어지고, 그 값들은 컴퓨터 메모리의 초기 값으로 사용됩니다. 정수 프로그램을 실행하면, 메모리의 초기 값를 프로그램으로 세팅해야 합니다. 메모리상 위치(position)는 주소 혹은 번지(address)라고 부릅시다. 예를들어 메모리의 첫번째 값은 "0번지(address 0)"입니다.
연산 코드(1
, 2
, 99
)는 각 명령어(instruction)의 시작을 나타냅니다. 연산 코드 뒤에 있는 숫자들은 명령어의 파라미터(parameters)라고 합니다. 예를 들어 명령어 1,2,3,4
의 경우, 1
은 연산 코드, 2
, 3
, 4
는 파라미터입니다. 명령어 99는 연산 코드만을 가지고 파라미터는 가지지 않습니다.
현재 실행 중인 명령어의 주소를 명령 포인터(instruction pointer)라고 하고, 이는 0부터 시작합니다. 한 명령어의 실행이 끝나면, 명령 포인터의 값은 명령어의 값들의 개수만큼 증가합니다. 정수 컴퓨터에 다른 명령어가 추가되기 전까진 이 값은 항상 4
입니다. (종료 명령어는 명령 포인터를 1
만큼 증가시키겠지만, 그 대신 프로그램을 즉시 종료시킵니다.)
"이렇게 용어를 정리했으니 더 나아갈 수 있겠네요. 스윙바이를 성공시키기 위해서는, 프로그램이 19690720
이라는 출력을 내는 입력값을 찾아야 합니다."
방금 전에 했던 것처럼, 입력 값은 프로그램의 1
번지와 2
번지의 값으로 주어집니다. 이 프로그램에서는 1
번지의 값을 명사
, 2
번지의 값을 동사
라고 합시다. 각 입력 값은 0
이상 99
이하의 값 중 하나입니다.
프로그램이 종료되면, 출력 값은 방금 전처럼 0
번지의 값이 됩니다. 각 입력 값에 대해 프로그램을 실행한 후에는, 컴퓨터의 메모리를 초기값(퍼즐 입력)으로 다시 세팅해야 한다는 것을 잊지마세요. 달리 말하면, 이정 시도의 메모리를 재사용해서는 안됩니다.
19690720
이라는 출력을 만드는 명사와 동사의 입력 값을 찾으세요. 그 후, 100 * 명사 + 동사
의 값을 알려주세요. (예를 들어, 명사가 12
, 동사가 2
라면 정답은 1202
입니다.)