diff --git a/day17/computer.go b/day17/computer.go new file mode 100644 index 0000000..deeab8c --- /dev/null +++ b/day17/computer.go @@ -0,0 +1,125 @@ +package main + +import ("math"; "errors"; "strconv"; "strings"; "fmt"; "os") + +//--------------------------------------------------------- +func check(err error) { if err != nil { panic(err) } } + +//--------------------------------------------------------- +type Computer struct { + pc uint + ra uint + rb uint + rc uint + mem []byte +} + +func NewComputer(input string) *Computer { + registers := strings.Split(input, "\n\n")[0] + memory := strings.Split(strings.Split(input, "\n\n")[1], " ")[1] + + lines := strings.Split(registers, "\n") + ra, err := strconv.Atoi(strings.TrimSpace(strings.Split(lines[0], ":")[1])) + check(err) + rb, err := strconv.Atoi(strings.TrimSpace(strings.Split(lines[1], ":")[1])) + check(err) + rc, err := strconv.Atoi(strings.TrimSpace(strings.Split(lines[2], ":")[1])) + check(err) + + mem := make([]byte, 0) + for _, str := range strings.Split(memory, ",") { + num, err := strconv.Atoi(str) + check(err) + mem = append(mem, byte(num)) + } + + return &Computer{ pc: 0, ra: uint(ra), rb: uint(rb), rc: uint(rc), mem: mem } +} + +func (c *Computer) literalop() uint { + // current literal operator + return uint(c.mem[c.pc + 1]) +} + +func (c *Computer) comboop() uint { + // current combo operator + val := uint(c.mem[c.pc + 1]) + + switch { + case val == 4: val = c.ra + case val == 5: val = c.rb + case val == 6: val = c.rc + case val == 7: panic("invalid program") + } + + return val +} + +func (c *Computer) adv(op uint) uint { + // division + numerator := c.ra + denominator := math.Pow(2, float64(op)) + return uint(float64(numerator) / float64(denominator)) +} + +func (c *Computer) bxl(op uint) uint { + // bitwise xor + return c.rb ^ op +} + +func (c *Computer) bst(op uint) uint { + // mod 8 + return op % 8 +} + +func (c *Computer) jnz(op uint) { + // jump if non zero + if c.ra == 0 { + return + } else { + c.pc = op - 2 + return + } +} + +func (c *Computer) bxc() uint { + // bitwise xor + return c.rb ^ c.rc +} + +func (c *Computer) out(op uint) { + fmt.Print(op % 8, ",") +} + + +func (c *Computer) tick() error { + // returns an error if the program halts + if c.pc >= uint(len(c.mem)) { return errors.New("halted") } + + instr := c.mem[c.pc] + switch { + case instr == 0: c.ra = c.adv(c.comboop()) + case instr == 1: c.rb = c.bxl(c.literalop()) + case instr == 2: c.rb = c.bst(c.comboop()) + case instr == 3: c.jnz(c.literalop()) + case instr == 4: c.rb = c.bxc() + case instr == 5: c.out(c.comboop()) + case instr == 6: c.rb = c.adv(c.comboop()) + case instr == 7: c.rc = c.adv(c.comboop()) + } + + c.pc += 2 + return nil +} + + +//--------------------------------------------------------- +func main() { + dat, err := os.ReadFile("data.txt") + check(err) + input := strings.TrimSpace(string(dat)) + + comp := NewComputer(input) + for comp.tick() == nil {} +} + diff --git a/day17/copy.go b/day17/copy.go new file mode 100644 index 0000000..f55d6fd --- /dev/null +++ b/day17/copy.go @@ -0,0 +1,157 @@ +package main + +import ("math"; "errors"; "strconv"; "strings"; "fmt"; "os") + +//--------------------------------------------------------- +func check(err error) { if err != nil { panic(err) } } + +//--------------------------------------------------------- +type Computer struct { + pc uint + ra uint + rb uint + rc uint + mem []byte +} + +func NewComputer(input string) *Computer { + registers := strings.Split(input, "\n\n")[0] + memory := strings.Split(strings.Split(input, "\n\n")[1], " ")[1] + + lines := strings.Split(registers, "\n") + ra, err := strconv.Atoi(strings.TrimSpace(strings.Split(lines[0], ":")[1])) + check(err) + rb, err := strconv.Atoi(strings.TrimSpace(strings.Split(lines[1], ":")[1])) + check(err) + rc, err := strconv.Atoi(strings.TrimSpace(strings.Split(lines[2], ":")[1])) + check(err) + + mem := make([]byte, 0) + for _, str := range strings.Split(memory, ",") { + num, err := strconv.Atoi(str) + check(err) + mem = append(mem, byte(num)) + } + + return &Computer{ + pc: 0, + ra: uint(ra), + rb: uint(rb), + rc: uint(rc), + mem: mem, + } +} + +func (c *Computer) literalop() uint { + // current literal operator + return uint(c.mem[c.pc + 1]) +} + +func (c *Computer) comboop() uint { + // current combo operator + val := uint(c.mem[c.pc + 1]) + + switch { + case val == 4: val = c.ra + case val == 5: val = c.rb + case val == 6: val = c.rc + case val == 7: panic("invalid program") + } + + return val +} + +func (c *Computer) adv(op uint) uint { + // division + numerator := c.ra + denominator := math.Pow(2, float64(op)) + return uint(float64(numerator) / float64(denominator)) +} + +func (c *Computer) bxl(op uint) uint { + // bitwise xor + return c.rb ^ op +} + +func (c *Computer) bst(op uint) uint { + // mod 8 + return op % 8 +} + +func (c *Computer) jnz(op uint) { + // jump if non zero + if c.ra == 0 { + return + } else { + c.pc = op - 2 + return + } +} + +func (c *Computer) bxc() uint { + // bitwise xor + return c.rb ^ c.rc +} + +func (c *Computer) tick() (int, error) { + // returns an error if the program halts + if c.pc >= uint(len(c.mem)) { return -1, errors.New("halted") } + + instr := c.mem[c.pc] + out := -1 + switch { + case instr == 0: c.ra = c.adv(c.comboop()) + case instr == 1: c.rb = c.bxl(c.literalop()) + case instr == 2: c.rb = c.bst(c.comboop()) + case instr == 3: c.jnz(c.literalop()) + case instr == 4: c.rb = c.bxc() + case instr == 5: out = int(c.comboop() % 8) + case instr == 6: c.rb = c.adv(c.comboop()) + case instr == 7: c.rc = c.adv(c.comboop()) + } + + c.pc += 2 + return out, nil +} + +func (c *Computer) returns_its_source() bool { + i := 0 + for { + out, err := c.tick() + + if err != nil { return false } + if out != -1 { + if byte(out) != c.mem[i] { return false } + i++ + if i == len(c.mem) { return true } + } + } + // never reached + return false +} + +func (c *Computer) restore(ra uint) { + c.pc = 0 + c.ra = ra + c.rb = 0 + c.rc = 0 +} + + +//--------------------------------------------------------- +func main() { + dat, err := os.ReadFile("data.txt") + check(err) + input := strings.TrimSpace(string(dat)) + + comp := NewComputer(input) + + for i := range 1000000000 { + comp.restore(uint(i)) + if comp.returns_its_source() { + fmt.Println(i) + break + } + } +} + diff --git a/day17/data.txt b/day17/data.txt new file mode 100644 index 0000000..3fb516f --- /dev/null +++ b/day17/data.txt @@ -0,0 +1,5 @@ +Register A: 41644071 +Register B: 0 +Register C: 0 + +Program: 2,4,1,2,7,5,1,7,4,4,0,3,5,5,3,0