158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
|
|
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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|