Skip to content
This repository has been archived by the owner on Dec 28, 2024. It is now read-only.

Commit

Permalink
Add solution day 17 part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
terminalnode committed Dec 17, 2024
1 parent fd4415d commit 6031974
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ automatically rebuilt and redeployed every time the `common` module or their own
| 01 | ⭐ ⭐ | 14 | ⭐ ⭐ |
| 02 | ⭐ ⭐ | 15 | ⭐ ⭐ |
| 03 | ⭐ ⭐ | 16 | ⭐ 🥸 |
| 04 | ⭐ ⭐ | 17 | |
| 04 | ⭐ ⭐ | 17 | |
| 05 | ⭐ ⭐ | 18 | |
| 06 | ⭐ ⭐ | 19 | |
| 07 | ⭐ ⭐ | 20 | |
Expand Down
117 changes: 117 additions & 0 deletions solutions/day17/machine.go
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)
}
85 changes: 85 additions & 0 deletions solutions/day17/machine_test.go
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)
}
})
}
}
14 changes: 13 additions & 1 deletion solutions/day17/main.go
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()
}
57 changes: 57 additions & 0 deletions solutions/day17/main_test.go
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)
}
}
62 changes: 62 additions & 0 deletions solutions/day17/parse.go
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
}

0 comments on commit 6031974

Please sign in to comment.