Browse Source

Day 9

master
Chris Smith 4 years ago
parent
commit
3f02f232b1
Signed by: Chris Smith <chris@chameth.com> GPG Key ID: 3A2D4BBDC4A3C9A9
6 changed files with 147 additions and 28 deletions
  1. 2
    0
      09/answers.txt
  2. 1
    0
      09/input.txt
  3. 37
    0
      09/main.go
  4. 21
    14
      intcode/ops.go
  5. 43
    13
      intcode/vm.go
  6. 43
    1
      intcode/vm_test.go

+ 2
- 0
09/answers.txt View File

@@ -0,0 +1,2 @@
1
+2714716640
2
+58879

+ 1
- 0
09/input.txt View File

@@ -0,0 +1 @@
1
+1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1102,1,3,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1102,521,1,1028,1101,0,33,1011,1101,0,22,1006,1101,28,0,1018,1102,37,1,1008,1102,1,20,1019,1101,0,405,1026,1101,25,0,1015,1101,330,0,1023,1101,0,29,1016,1101,0,560,1025,1101,24,0,1017,1102,516,1,1029,1102,333,1,1022,1102,1,34,1012,1101,0,402,1027,1101,0,1,1021,1102,36,1,1013,1102,30,1,1002,1101,21,0,1000,1102,1,23,1005,1102,39,1,1003,1102,1,32,1007,1102,26,1,1004,1101,565,0,1024,1101,0,0,1020,1101,0,31,1014,1101,27,0,1001,1101,0,38,1009,1101,0,35,1010,109,-3,2102,1,10,63,1008,63,32,63,1005,63,203,4,187,1106,0,207,1001,64,1,64,1002,64,2,64,109,26,21108,40,40,-4,1005,1019,229,4,213,1001,64,1,64,1105,1,229,1002,64,2,64,109,-20,2102,1,-3,63,1008,63,22,63,1005,63,253,1001,64,1,64,1105,1,255,4,235,1002,64,2,64,109,-10,1208,10,39,63,1005,63,277,4,261,1001,64,1,64,1106,0,277,1002,64,2,64,109,15,2107,20,-8,63,1005,63,299,4,283,1001,64,1,64,1106,0,299,1002,64,2,64,109,-8,1208,3,40,63,1005,63,315,1106,0,321,4,305,1001,64,1,64,1002,64,2,64,109,29,2105,1,-6,1106,0,339,4,327,1001,64,1,64,1002,64,2,64,109,-18,1205,10,353,4,345,1106,0,357,1001,64,1,64,1002,64,2,64,109,11,1206,-1,373,1001,64,1,64,1105,1,375,4,363,1002,64,2,64,109,-2,1205,0,391,1001,64,1,64,1106,0,393,4,381,1002,64,2,64,109,10,2106,0,-3,1106,0,411,4,399,1001,64,1,64,1002,64,2,64,109,-18,21108,41,39,3,1005,1015,427,1105,1,433,4,417,1001,64,1,64,1002,64,2,64,109,-7,21101,42,0,6,1008,1011,45,63,1005,63,457,1001,64,1,64,1106,0,459,4,439,1002,64,2,64,109,-14,2101,0,9,63,1008,63,21,63,1005,63,481,4,465,1105,1,485,1001,64,1,64,1002,64,2,64,109,22,1207,-7,21,63,1005,63,505,1001,64,1,64,1106,0,507,4,491,1002,64,2,64,109,15,2106,0,0,4,513,1106,0,525,1001,64,1,64,1002,64,2,64,109,-14,21101,43,0,-1,1008,1013,43,63,1005,63,551,4,531,1001,64,1,64,1106,0,551,1002,64,2,64,109,10,2105,1,0,4,557,1106,0,569,1001,64,1,64,1002,64,2,64,109,-12,21102,44,1,3,1008,1015,44,63,1005,63,595,4,575,1001,64,1,64,1105,1,595,1002,64,2,64,109,-4,1201,-8,0,63,1008,63,21,63,1005,63,621,4,601,1001,64,1,64,1106,0,621,1002,64,2,64,109,5,2108,37,-5,63,1005,63,639,4,627,1105,1,643,1001,64,1,64,1002,64,2,64,109,-14,1202,1,1,63,1008,63,21,63,1005,63,669,4,649,1001,64,1,64,1105,1,669,1002,64,2,64,109,-2,1207,7,27,63,1005,63,691,4,675,1001,64,1,64,1106,0,691,1002,64,2,64,109,13,2107,33,-3,63,1005,63,711,1001,64,1,64,1105,1,713,4,697,1002,64,2,64,109,19,1206,-9,727,4,719,1105,1,731,1001,64,1,64,1002,64,2,64,109,-24,1202,0,1,63,1008,63,20,63,1005,63,755,1001,64,1,64,1106,0,757,4,737,1002,64,2,64,109,8,21102,45,1,-3,1008,1010,46,63,1005,63,781,1001,64,1,64,1106,0,783,4,763,1002,64,2,64,109,-15,2108,40,10,63,1005,63,799,1105,1,805,4,789,1001,64,1,64,1002,64,2,64,109,20,21107,46,45,-1,1005,1017,821,1106,0,827,4,811,1001,64,1,64,1002,64,2,64,109,-23,1201,6,0,63,1008,63,29,63,1005,63,847,1106,0,853,4,833,1001,64,1,64,1002,64,2,64,109,17,21107,47,48,2,1005,1014,875,4,859,1001,64,1,64,1106,0,875,1002,64,2,64,109,-10,2101,0,-2,63,1008,63,20,63,1005,63,895,1105,1,901,4,881,1001,64,1,64,4,64,99,21102,27,1,1,21101,0,915,0,1105,1,922,21201,1,37574,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,942,1,0,1105,1,922,22102,1,1,-1,21201,-2,-3,1,21101,957,0,0,1105,1,922,22201,1,-1,-2,1105,1,968,21201,-2,0,-2,109,-3,2105,1,0

+ 37
- 0
09/main.go View File

@@ -0,0 +1,37 @@
1
+package main
2
+
3
+import (
4
+	"github.com/csmith/aoc-2019/common"
5
+	"github.com/csmith/aoc-2019/intcode"
6
+)
7
+
8
+func last(channel <-chan int) (res int) {
9
+	for {
10
+		o, more := <-channel
11
+		if more {
12
+			res = o
13
+		} else {
14
+			return
15
+		}
16
+	}
17
+}
18
+
19
+func main() {
20
+	input := common.ReadCsvAsInts("09/input.txt")
21
+	memory := make([]int, len(input))
22
+	copy(memory, input)
23
+
24
+	vm := intcode.NewVirtualMachine(memory)
25
+	vm.Input = make(chan int, 1)
26
+	vm.Output = make(chan int, 1)
27
+	vm.Input <- 1
28
+	go vm.Run()
29
+	println(last(vm.Output))
30
+
31
+	vm.Reset(input)
32
+	vm.Input = make(chan int, 1)
33
+	vm.Output = make(chan int, 1)
34
+	vm.Input <- 2
35
+	go vm.Run()
36
+	println(last(vm.Output))
37
+}

+ 21
- 14
intcode/ops.go View File

@@ -12,39 +12,40 @@ var opcodes = [100]opcodeFunc{
12 12
 	6:  jumpIfFalseOpCode,
13 13
 	7:  lessThanOpCode,
14 14
 	8:  equalsOpCode,
15
+	9:  relativeBaseOffsetOpCode,
15 16
 	99: haltOpcode,
16 17
 }
17 18
 
18 19
 // addOpcode takes the values specified by args 1 and 2, adds them together, and stores at the memory address given
19 20
 // by arg 3.
20 21
 func addOpcode(vm *VirtualMachine) {
21
-	vm.Memory[vm.Memory[vm.ip+3]] = vm.arg(0) + vm.arg(1)
22
+	*vm.arg(2) = *vm.arg(0) + *vm.arg(1)
22 23
 	vm.ip += 4
23 24
 }
24 25
 
25 26
 // mulOpcode takes the values specified by args 1 and 2, multiplies them together, and stores at the memory address
26 27
 // given by arg 3.
27 28
 func mulOpcode(vm *VirtualMachine) {
28
-	vm.Memory[vm.Memory[vm.ip+3]] = vm.arg(0) * vm.arg(1)
29
+	*vm.arg(2) = *vm.arg(0) * *vm.arg(1)
29 30
 	vm.ip += 4
30 31
 }
31 32
 
32 33
 // readOpCode reads a value from the input stream and stores it at the memory address given by arg 1.
33 34
 func readOpCode(vm *VirtualMachine) {
34
-	vm.Memory[vm.Memory[vm.ip+1]] = <-vm.Input
35
+	*vm.arg(0) = <-vm.Input
35 36
 	vm.ip += 2
36 37
 }
37 38
 
38 39
 // writeOpCode writes the value specified by the first argument to the output stream.
39 40
 func writeOpCode(vm *VirtualMachine) {
40
-	vm.Output <- vm.arg(0)
41
+	vm.Output <- *vm.arg(0)
41 42
 	vm.ip += 2
42 43
 }
43 44
 
44 45
 // jumpIfTrueOpCode checks if the first argument is not zero, and if so jumps to the second argument.
45 46
 func jumpIfTrueOpCode(vm *VirtualMachine) {
46
-	if vm.arg(0) != 0 {
47
-		vm.ip = vm.arg(1)
47
+	if *vm.arg(0) != 0 {
48
+		vm.ip = *vm.arg(1)
48 49
 	} else {
49 50
 		vm.ip += 3
50 51
 	}
@@ -52,8 +53,8 @@ func jumpIfTrueOpCode(vm *VirtualMachine) {
52 53
 
53 54
 // jumpIfFalseOpCode checks if the first argument is zero, and if so jumps to the second argument.
54 55
 func jumpIfFalseOpCode(vm *VirtualMachine) {
55
-	if vm.arg(0) == 0 {
56
-		vm.ip = vm.arg(1)
56
+	if *vm.arg(0) == 0 {
57
+		vm.ip = *vm.arg(1)
57 58
 	} else {
58 59
 		vm.ip += 3
59 60
 	}
@@ -62,10 +63,10 @@ func jumpIfFalseOpCode(vm *VirtualMachine) {
62 63
 // lessThanOpCode checks if the first argument is less than the second, and stores the result at the address given
63 64
 // by the third argument.
64 65
 func lessThanOpCode(vm *VirtualMachine) {
65
-	if vm.arg(0) < vm.arg(1) {
66
-		vm.Memory[vm.Memory[vm.ip+3]] = 1
66
+	if *vm.arg(0) < *vm.arg(1) {
67
+		*vm.arg(2) = 1
67 68
 	} else {
68
-		vm.Memory[vm.Memory[vm.ip+3]] = 0
69
+		*vm.arg(2) = 0
69 70
 	}
70 71
 	vm.ip += 4
71 72
 }
@@ -73,14 +74,20 @@ func lessThanOpCode(vm *VirtualMachine) {
73 74
 // equalsOpCode checks if the first argument is equal to the second, and stores the result at the address given
74 75
 // by the third argument.
75 76
 func equalsOpCode(vm *VirtualMachine) {
76
-	if vm.arg(0) == vm.arg(1) {
77
-		vm.Memory[vm.Memory[vm.ip+3]] = 1
77
+	if *vm.arg(0) == *vm.arg(1) {
78
+		*vm.arg(2) = 1
78 79
 	} else {
79
-		vm.Memory[vm.Memory[vm.ip+3]] = 0
80
+		*vm.arg(2) = 0
80 81
 	}
81 82
 	vm.ip += 4
82 83
 }
83 84
 
85
+// relativeBaseOffsetOpCode increases the relative base by the given argument.
86
+func relativeBaseOffsetOpCode(vm *VirtualMachine) {
87
+	vm.rb += *vm.arg(0)
88
+	vm.ip += 2
89
+}
90
+
84 91
 // haltOpcode halts the VM and takes no arguments.
85 92
 func haltOpcode(vm *VirtualMachine) {
86 93
 	vm.Halted = true

+ 43
- 13
intcode/vm.go View File

@@ -2,12 +2,14 @@ package intcode
2 2
 
3 3
 // VirtualMachine is an IntCode virtual machine.
4 4
 type VirtualMachine struct {
5
-	ip     int
6
-	modes  uint8
7
-	Memory []int
8
-	Halted bool
9
-	Input  chan int
10
-	Output chan int
5
+	ip             int
6
+	rb             int
7
+	parameterModes uint8
8
+	relativeModes  uint8
9
+	Memory         []int
10
+	Halted         bool
11
+	Input          chan int
12
+	Output         chan int
11 13
 }
12 14
 
13 15
 // NewVirtualMachine creates a new IntCode virtual machine, initialised to the given slice of memory.
@@ -23,12 +25,29 @@ func NewVirtualMachine(memory []int) *VirtualMachine {
23 25
 }
24 26
 
25 27
 // arg Returns the value of the given argument for the current instruction.
26
-func (vm *VirtualMachine) arg(pos int) int {
28
+func (vm *VirtualMachine) arg(pos int) *int {
27 29
 	mask := uint8(1) << uint8(pos)
28
-	if vm.modes&mask == mask {
29
-		return vm.Memory[vm.ip+1+pos]
30
+	if vm.parameterModes&mask == mask {
31
+		// Parameter mode - the value of the argument is just treated as an int
32
+		if vm.ip+1+pos > cap(vm.Memory) {
33
+			vm.Memory = append(vm.Memory, make([]int, 1024)...)
34
+		}
35
+
36
+		return &vm.Memory[vm.ip+1+pos]
37
+	} else if vm.relativeModes&mask == mask {
38
+		// Relative mode - the value of the argument is treated as a memory offset from the relative base
39
+		if vm.ip+1+pos > cap(vm.Memory) || vm.rb+vm.Memory[vm.ip+1+pos] > cap(vm.Memory) {
40
+			vm.Memory = append(vm.Memory, make([]int, 1024)...)
41
+		}
42
+
43
+		return &vm.Memory[vm.rb+vm.Memory[vm.ip+1+pos]]
30 44
 	} else {
31
-		return vm.Memory[vm.Memory[vm.ip+1+pos]]
45
+		// Position mode - the value of the argument is treated as a memory offset from the start of the memory
46
+		if vm.ip+1+pos > cap(vm.Memory) || vm.Memory[vm.ip+1+pos] > cap(vm.Memory) {
47
+			vm.Memory = append(vm.Memory, make([]int, 1024)...)
48
+		}
49
+
50
+		return &vm.Memory[vm.Memory[vm.ip+1+pos]]
32 51
 	}
33 52
 }
34 53
 
@@ -38,11 +57,15 @@ func (vm *VirtualMachine) Run() {
38 57
 		instruction := vm.Memory[vm.ip]
39 58
 		opcode := instruction % 100
40 59
 
41
-		vm.modes = 0
60
+		vm.parameterModes = 0
61
+		vm.relativeModes = 0
42 62
 		mask := uint8(1)
43 63
 		for i := instruction / 100; i > 0; i /= 10 {
44
-			if i%10 == 1 {
45
-				vm.modes = vm.modes | mask
64
+			mode := i % 10
65
+			if mode == 1 {
66
+				vm.parameterModes = vm.parameterModes | mask
67
+			} else if mode == 2 {
68
+				vm.relativeModes = vm.relativeModes | mask
46 69
 			}
47 70
 			mask = mask << 1
48 71
 		}
@@ -57,6 +80,13 @@ func (vm *VirtualMachine) Run() {
57 80
 // Reset resets the memory to the given slice, and all other state back to its original value.
58 81
 func (vm *VirtualMachine) Reset(memory []int) {
59 82
 	copy(vm.Memory, memory)
83
+
84
+	// We may previously have expanded our own memory, reset that to zero.
85
+	for i := len(memory); i < len(vm.Memory)-1; i++ {
86
+		vm.Memory[i] = 0
87
+	}
88
+
60 89
 	vm.ip = 0
90
+	vm.rb = 0
61 91
 	vm.Halted = false
62 92
 }

+ 43
- 1
intcode/vm_test.go View File

@@ -2,6 +2,7 @@ package intcode
2 2
 
3 3
 import (
4 4
 	"reflect"
5
+	"sync"
5 6
 	"testing"
6 7
 )
7 8
 
@@ -47,7 +48,7 @@ func TestDayFiveSamples(t *testing.T) {
47 48
 		{[]int{3, 3, 1107, -1, 8, 3, 4, 3, 99}, []int{10}, []int{0}},
48 49
 	}
49 50
 
50
-	for _, table := range tables {
51
+	for n, table := range tables {
51 52
 		vm := NewVirtualMachine(table.given)
52 53
 		vm.Input = make(chan int, 1)
53 54
 		vm.Output = make(chan int, 1)
@@ -58,6 +59,47 @@ func TestDayFiveSamples(t *testing.T) {
58 59
 
59 60
 		vm.Run()
60 61
 
62
+		for _, v := range table.output {
63
+			actual := <-vm.Output
64
+			if !reflect.DeepEqual(v, actual) {
65
+				t.Errorf("[%d] Wrong output value received for %v, got: %v, want: %v.", n, table.given, actual, v)
66
+			}
67
+		}
68
+	}
69
+}
70
+
71
+func TestDayNineSamples(t *testing.T) {
72
+	tables := []struct {
73
+		given  []int
74
+		input  []int
75
+		output []int
76
+	}{
77
+		// Takes no input and produces a copy of itself as output.
78
+		{[]int{109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99}, []int{}, []int{109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99}},
79
+		// Should output a 16 digit number.
80
+		{[]int{1102, 34915192, 34915192, 7, 4, 7, 99, 0}, []int{}, []int{1219070632396864}},
81
+		// Should output the large number in the middle.
82
+		{[]int{104, 1125899906842624, 99}, []int{}, []int{1125899906842624}},
83
+	}
84
+
85
+	for _, table := range tables {
86
+		vm := NewVirtualMachine(table.given)
87
+		vm.Input = make(chan int, 1)
88
+		vm.Output = make(chan int, 100)
89
+		wg := &sync.WaitGroup{}
90
+		wg.Add(1)
91
+
92
+		go func() {
93
+			vm.Run()
94
+			wg.Done()
95
+		}()
96
+
97
+		for _, v := range table.input {
98
+			vm.Input <- v
99
+		}
100
+
101
+		wg.Wait()
102
+
61 103
 		for _, v := range table.output {
62 104
 			actual := <-vm.Output
63 105
 			if !reflect.DeepEqual(v, actual) {

Loading…
Cancel
Save