This repository has been archived by the owner on Dec 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fd4415d
commit 6031974
Showing
6 changed files
with
335 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
) | ||
|
||
type machine struct { | ||
a int64 | ||
b int64 | ||
c int64 | ||
seq []int64 | ||
out []int64 | ||
pos int | ||
count int | ||
} | ||
|
||
func (m *machine) run(maxItt int) { | ||
for m.pos < len(m.seq) && (maxItt <= 0 || m.count < maxItt) { | ||
m.next() | ||
m.count++ | ||
} | ||
} | ||
|
||
func (m *machine) next() { | ||
switch m.seq[m.pos] { | ||
// Division operators | ||
case 0: | ||
// log.Println("ADV") | ||
m.a = m.a / (1 << m.comboOperand()) | ||
m.incrementPos(2) | ||
case 6: | ||
// log.Println("BDV") | ||
m.b = m.a / (1 << m.comboOperand()) | ||
m.incrementPos(2) | ||
case 7: | ||
// log.Println("CDV") | ||
m.c = m.a / (1 << m.comboOperand()) | ||
m.incrementPos(2) | ||
|
||
// Bitwise operators | ||
case 1: | ||
// log.Println("BXL") | ||
m.b = m.b ^ m.literalOperand() | ||
m.incrementPos(2) | ||
case 4: | ||
// log.Println("BXC") | ||
m.b = m.b ^ m.c | ||
m.incrementPos(2) | ||
|
||
// Modulo operators | ||
case 5: | ||
// log.Println("OUT") | ||
m.out = append(m.out, m.comboOperand()%8) | ||
m.incrementPos(2) | ||
case 2: | ||
// log.Println("BST") | ||
m.b = m.comboOperand() % 8 | ||
m.incrementPos(2) | ||
|
||
// Other | ||
case 3: | ||
// log.Println("JNZ") | ||
if m.a == 0 { | ||
m.incrementPos(2) | ||
} else { | ||
m.pos = int(m.seq[m.pos+1]) | ||
} | ||
} | ||
} | ||
|
||
func (m *machine) incrementPos(n int) { | ||
m.pos += n | ||
} | ||
|
||
func (m *machine) comboOperand() int64 { | ||
raw := m.literalOperand() | ||
switch { | ||
case raw < 0: | ||
panic("operand can not be < 0") | ||
case raw <= 3: | ||
return raw | ||
case raw == 4: | ||
return m.a | ||
case raw == 5: | ||
return m.b | ||
case raw == 6: | ||
return m.c | ||
default: | ||
log.Println(m) | ||
panic("failed to get operand (can not be >= 7)") | ||
} | ||
} | ||
|
||
func (m *machine) literalOperand() int64 { | ||
return m.seq[m.pos+1] | ||
} | ||
|
||
func (m *machine) strOut() string { | ||
if len(m.out) == 0 { | ||
return "" | ||
} | ||
|
||
s := fmt.Sprintf("%d", m.out[0]) | ||
for _, o := range m.out[1:] { | ||
s += fmt.Sprintf(",%d", o) | ||
} | ||
return s | ||
} | ||
|
||
func (m *machine) String() string { | ||
curr := "out of bounds" | ||
if m.pos > 0 && m.pos < len(m.seq) { | ||
curr = fmt.Sprintf("%d", m.seq[m.pos]) | ||
} | ||
return fmt.Sprintf("machine{a:%d, b:%d, c:%d, seq:%v, out:%v, pos:%d (%s)}", m.a, m.b, m.c, m.seq, m.out, m.pos, curr) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"testing" | ||
) | ||
|
||
func TestMachineRun(t *testing.T) { | ||
tests := []struct { | ||
n string | ||
m machine | ||
f func(machine) bool | ||
}{ | ||
{ | ||
n: "#1 If register C contains 9, the program 2,6 would set register B to 1.", | ||
m: machine{c: 9, seq: []int64{2, 6}}, | ||
f: func(m machine) bool { return m.b == 1 }, | ||
}, { | ||
n: "#2 If register A contains 10, the program 5,0,5,1,5,4 would output 0,1,2.", | ||
m: machine{a: 10, seq: []int64{5, 0, 5, 1, 5, 4}}, | ||
f: func(m machine) bool { return m.strOut() == "0,1,2" }, | ||
}, { | ||
n: "#3 If register A contains 2024, the program 0,1,5,4,3,0 would output 4,2,5,6,7,7,7,7,3,1,0 and leave 0 in register A.", | ||
m: machine{a: 2024, seq: []int64{0, 1, 5, 4, 3, 0}}, | ||
f: func(m machine) bool { | ||
return m.a == 0 && m.strOut() == "4,2,5,6,7,7,7,7,3,1,0" | ||
}, | ||
}, { | ||
n: "#4 If register B contains 29, the program 1,7 would set register B to 26.", | ||
m: machine{b: 29, seq: []int64{1, 7}}, | ||
f: func(m machine) bool { return m.b == 26 }, | ||
}, { | ||
n: "#5 If register B contains 2024 and register C contains 43690, the program 4,0 would set register B to 44354.", | ||
m: machine{b: 2024, c: 43690, seq: []int64{4, 0}}, | ||
f: func(m machine) bool { return m.b == 44354 }, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.n, func(t *testing.T) { | ||
test.m.run(100) | ||
if !test.f(test.m) { | ||
t.Errorf("Test validation failed with %s", test.m.String()) | ||
fmt.Println(test.m) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestMachine_String(t *testing.T) { | ||
min64I := int64(math.MinInt64) // -9223372036854775808 | ||
max64I := int64(math.MaxInt64) // 9223372036854775807 | ||
|
||
tests := []struct { | ||
n string | ||
m machine | ||
e string | ||
}{ | ||
{ | ||
n: "machine with boundary int64 values", | ||
m: machine{a: min64I, b: 0, c: max64I, seq: []int64{min64I, 0, max64I}, pos: 2}, | ||
e: "machine{a:-9223372036854775808, b:0, c:9223372036854775807, seq:[-9223372036854775808 0 9223372036854775807], out:[], pos:2 (9223372036854775807)}", | ||
}, | ||
{ | ||
n: "machine with positive out of bounds index", | ||
m: machine{a: 0, b: 1, c: 2, seq: []int64{1, 2, 3, 4, 5}, out: []int64{1, 2, 3}, pos: 5}, | ||
e: "machine{a:0, b:1, c:2, seq:[1 2 3 4 5], out:[1 2 3], pos:5 (out of bounds)}", | ||
}, | ||
{ | ||
n: "machine with negative out of bounds index", | ||
m: machine{a: 0, b: 1, c: 2, seq: []int64{1, 2, 3, 4, 5}, pos: -1}, | ||
e: "machine{a:0, b:1, c:2, seq:[1 2 3 4 5], out:[], pos:-1 (out of bounds)}", | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.n, func(t *testing.T) { | ||
actual := test.m.String() | ||
if actual != test.e { | ||
t.Errorf("expected: '%s'\nbut got: '%s'", test.e, actual) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,21 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/terminalnode/adventofcode2024/common" | ||
) | ||
|
||
func main() { | ||
common.Setup(17, nil, nil) | ||
common.Setup(17, part1, nil) | ||
} | ||
|
||
func part1( | ||
input string, | ||
) string { | ||
m, err := parseMachine(input) | ||
if err != nil { | ||
return fmt.Sprintf("Failed to parse machine: %v", err) | ||
} | ||
m.run(-1) | ||
return m.strOut() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package main | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
const ex1Out = "4,6,3,5,6,3,5,2,1,0" | ||
const ex1 = `Register A: 729 | ||
Register B: 0 | ||
Register C: 0 | ||
Program: 0,1,5,4,3,0` | ||
|
||
func TestParseMachine(t *testing.T) { | ||
testInput := `Register A: -1337 | ||
Register B: 0 | ||
Register C: 666 | ||
Program: 0,-1,5,-4,3,0` | ||
testProgram := []int64{0, -1, 5, -4, 3, 0} | ||
|
||
m, err := parseMachine(testInput) | ||
|
||
if err != nil { | ||
t.Errorf("Failed to parse machine: %v", err) | ||
} | ||
|
||
if m.a != -1337 { | ||
t.Errorf("expected m.a == -1337, but was %d", m.c) | ||
} | ||
|
||
if m.b != 0 { | ||
t.Errorf("expected m.b == 0, but was %d", m.c) | ||
} | ||
|
||
if m.c != 666 { | ||
t.Errorf("expected m.c == 666, but was %d", m.c) | ||
} | ||
|
||
if len(m.seq) != len(testProgram) { | ||
t.Errorf("Expected len(m.seq) == %d, but was %d", len(testProgram), len(m.seq)) | ||
} | ||
|
||
for i, e := range testProgram { | ||
a := m.seq[i] | ||
if e != a { | ||
t.Errorf("Expected m.seq[%d] == %d, but was %d", i, e, a) | ||
} | ||
} | ||
} | ||
|
||
func TestPart1_Example1(t *testing.T) { | ||
out := part1(ex1) | ||
if out != ex1Out { | ||
t.Errorf("expected '%s' but got '%s'", ex1Out, out) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
var r = regexp.MustCompile(`Register A: (-?\d+)\nRegister B: (-?\d+)\nRegister C: (-?\d+)\n\nProgram: ((?:-?\d,)+)`) | ||
|
||
func parseMachine( | ||
input string, | ||
) (machine, error) { | ||
out := machine{} | ||
matches := r.FindStringSubmatch(input) | ||
if len(matches) != 5 { | ||
return out, fmt.Errorf("expected four fields but got %d", len(matches)) | ||
} | ||
|
||
digits, err := parseRegisters(matches) | ||
if err != nil { | ||
return out, err | ||
} | ||
out.a = digits[0] | ||
out.b = digits[1] | ||
out.c = digits[2] | ||
|
||
seq, err := parseProgram(matches[4]) | ||
out.seq = seq | ||
|
||
return out, nil | ||
} | ||
|
||
func parseRegisters( | ||
matches []string, | ||
) ([]int64, error) { | ||
out := make([]int64, 3) | ||
for i, m := range matches[1:4] { | ||
mAsI64, err := strconv.ParseInt(m, 10, 64) | ||
if err != nil { | ||
return out, err | ||
} | ||
out[i] = mAsI64 | ||
} | ||
return out, nil | ||
} | ||
|
||
func parseProgram( | ||
program string, | ||
) ([]int64, error) { | ||
parts := strings.Split(program, ",") | ||
out := make([]int64, len(parts)) | ||
for i, part := range parts { | ||
partAsI64, err := strconv.ParseInt(part, 10, 64) | ||
if err != nil { | ||
return out, err | ||
} | ||
out[i] = partAsI64 | ||
} | ||
return out, nil | ||
} |