mirror of
https://github.com/onyx-and-iris/aoc2024.git
synced 2026-04-09 02:23:36 +00:00
add day-17 + benchmarks
This commit is contained in:
6
day-17/internal/two/benchmark
Normal file
6
day-17/internal/two/benchmark
Normal file
@@ -0,0 +1,6 @@
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/onyx-and-iris/aoc2024/day-17/internal/two
|
||||
cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
|
||||
BenchmarkSolve-12 1000000000 0.001878 ns/op
|
||||
ok github.com/onyx-and-iris/aoc2024/day-17/internal/two 0.017s
|
||||
114
day-17/internal/two/computer.go
Normal file
114
day-17/internal/two/computer.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package two
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
type computer struct {
|
||||
registers map[string]int64
|
||||
instructionMap map[int64]instructionFn
|
||||
}
|
||||
|
||||
func newComputer(registers map[string]int64) *computer {
|
||||
c := &computer{
|
||||
registers: registers,
|
||||
}
|
||||
|
||||
c.instructionMap = map[int64]instructionFn{
|
||||
0: c.adv,
|
||||
1: c.bxl,
|
||||
2: c.bst,
|
||||
3: c.jnz,
|
||||
4: c.bxc,
|
||||
5: c.out,
|
||||
6: c.bdv,
|
||||
7: c.cdv,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *computer) bumpRegisters(next int64) {
|
||||
c.registers["A"] = next
|
||||
c.registers["B"] = 0
|
||||
c.registers["C"] = 0
|
||||
}
|
||||
|
||||
func (c *computer) run(program []int64) []int64 {
|
||||
results := []int64{}
|
||||
var ptr int
|
||||
for ptr < len(program) {
|
||||
opcode := program[ptr]
|
||||
jump, doJump, out, doStore := c.performOperation(c.instructionMap[opcode], operand(program[ptr+1]))
|
||||
if doJump {
|
||||
ptr = jump
|
||||
} else {
|
||||
ptr += 2
|
||||
}
|
||||
if doStore {
|
||||
results = append(results, out)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (c *computer) performOperation(fn instructionFn, operand operand) (int, bool, int64, bool) {
|
||||
return fn(operand)
|
||||
}
|
||||
|
||||
func (c *computer) adv(operand operand) (int, bool, int64, bool) {
|
||||
c.registers["A"] /= int64(math.Pow(2, float64(c.combo(operand))))
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) bxl(operand operand) (int, bool, int64, bool) {
|
||||
c.registers["B"] ^= int64(operand)
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) bst(operand operand) (int, bool, int64, bool) {
|
||||
c.registers["B"] = c.combo(operand) % 8
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) jnz(operand operand) (int, bool, int64, bool) {
|
||||
if c.registers["A"] == 0 {
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
return int(operand), true, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) bxc(operand operand) (int, bool, int64, bool) {
|
||||
c.registers["B"] ^= c.registers["C"]
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) out(operand operand) (int, bool, int64, bool) {
|
||||
return 0, false, c.combo(operand) % 8, true
|
||||
}
|
||||
|
||||
func (c *computer) bdv(operand operand) (int, bool, int64, bool) {
|
||||
c.registers["B"] = c.registers["A"] / int64(math.Pow(2, float64(c.combo(operand))))
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) cdv(operand operand) (int, bool, int64, bool) {
|
||||
c.registers["C"] = c.registers["A"] / int64(math.Pow(2, float64(c.combo(operand))))
|
||||
return 0, false, 0, false
|
||||
}
|
||||
|
||||
func (c *computer) combo(operand operand) int64 {
|
||||
switch operand {
|
||||
case 0, 1, 2, 3:
|
||||
return int64(operand)
|
||||
case 4:
|
||||
return int64(c.registers["A"])
|
||||
case 5:
|
||||
return int64(c.registers["B"])
|
||||
case 6:
|
||||
return int64(c.registers["C"])
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
33
day-17/internal/two/computer_internal_test.go
Normal file
33
day-17/internal/two/computer_internal_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package two
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComputerRunProgram1(t *testing.T) {
|
||||
tt := map[string]struct {
|
||||
registers map[string]int64
|
||||
program []int64
|
||||
}{
|
||||
"register C contains 9 with program 2,6, should set register B to 1": {
|
||||
registers: map[string]int64{
|
||||
"A": 117440,
|
||||
"B": 0,
|
||||
"C": 0,
|
||||
},
|
||||
program: []int64{0, 3, 5, 4, 3, 0},
|
||||
},
|
||||
}
|
||||
|
||||
c := newComputer(nil)
|
||||
for name, tc := range tt {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c.registers = tc.registers
|
||||
got := c.run(tc.program)
|
||||
if !slices.Equal(got, tc.program) {
|
||||
t.Errorf("output: %q, expected %q", got, tc.program)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
57
day-17/internal/two/solve.go
Normal file
57
day-17/internal/two/solve.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package two
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type operand int
|
||||
type instructionFn func(operand) (int, bool, int64, bool)
|
||||
|
||||
func Solve(buf []byte) (int64, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
_, program, err := parseLines(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
computer := newComputer(make(map[string]int64))
|
||||
a := lowestA(program, computer)
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func lowestA(program []int64, computer *computer) int64 {
|
||||
var a int64 = 1
|
||||
computer.bumpRegisters(a)
|
||||
var results []int64
|
||||
for {
|
||||
results = computer.run(program)
|
||||
|
||||
if slices.Equal(results, program) {
|
||||
return a
|
||||
}
|
||||
|
||||
if len(program) > len(results) {
|
||||
a *= 2
|
||||
computer.bumpRegisters(a)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(program) == len(results) {
|
||||
for j := len(program) - 1; j >= 0; j-- {
|
||||
if program[j] != results[j] {
|
||||
a += int64(math.Pow(8, float64(j)))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(program) < len(results) {
|
||||
a /= 2
|
||||
}
|
||||
|
||||
computer.bumpRegisters(a)
|
||||
}
|
||||
}
|
||||
15
day-17/internal/two/solve_internal_test.go
Normal file
15
day-17/internal/two/solve_internal_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package two
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:embed testdata/input.txt
|
||||
var data []byte
|
||||
|
||||
func BenchmarkSolve(b *testing.B) {
|
||||
os.Stdout, _ = os.Open(os.DevNull)
|
||||
Solve(data)
|
||||
}
|
||||
49
day-17/internal/two/util.go
Normal file
49
day-17/internal/two/util.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package two
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
reRegister = regexp.MustCompile(`Register (?P<identifier>[A-Z]): (?P<value>[0-9]+)`)
|
||||
reProgram = regexp.MustCompile(`Program: (?P<instructions>[0-9,]+)`)
|
||||
)
|
||||
|
||||
func parseLines(r io.Reader) (map[string]int64, []int64, error) {
|
||||
registers := make(map[string]int64)
|
||||
var instructions []int64
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
switch line := line; {
|
||||
case reRegister.MatchString(line):
|
||||
m := reRegister.FindStringSubmatch(line)
|
||||
registers[m[1]] = mustConv(m[2])
|
||||
case reProgram.MatchString(line):
|
||||
m := reProgram.FindStringSubmatch(line)
|
||||
for _, n := range strings.Split(m[1], ",") {
|
||||
instructions = append(instructions, mustConv(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return registers, instructions, nil
|
||||
}
|
||||
|
||||
func mustConv(s string) int64 {
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int64(n)
|
||||
}
|
||||
Reference in New Issue
Block a user