package main import ( "fmt" "io/ioutil" "strconv" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) var incache []int type Computer struct { Address int Program []int Position int Offset int Channel chan int } func (c Computer) String() string { status := "○" if c.Position%2 == 0 { status = "●" } return fmt.Sprintf("[%2d] %s {%d}", c.Address, status, c.Offset) } func (c *Computer) parseInstruction() (int, []int) { inst := c.read(c.Position) opcode := inst % 100 modes := make([]int, 4) inst /= 100 for i := 0; inst > 0; i++ { modes[i] = inst % 10 inst /= 10 } return opcode, modes } func (c *Computer) parseParameters(modes []int, num int) []int { params := make([]int, num) for i := 0; i < num; i++ { if i >= len(modes) || modes[i] == 0 { params[i] = c.read(c.read(c.Position + i + 1)) } else if modes[i] == 1 { params[i] = c.read(c.Position + i + 1) } else { params[i] = c.read(c.read(c.Position+i+1) + c.Offset) } } return params } func (c *Computer) read(pos int) int { if pos >= len(c.Program) { return 0 } return c.Program[pos] } func (c *Computer) write(pos, val, mode int) { if mode == 2 { pos += c.Offset } if pos >= len(c.Program) { prog := make([]int, pos*2) copy(prog, c.Program) c.Program = prog } c.Program[pos] = val } func (c *Computer) run() { for c.read(c.Position) != 99 { inst, modes := c.parseInstruction() switch inst { case 1: params := c.parseParameters(modes, 2) c.write(c.read(c.Position+3), params[0]+params[1], modes[2]) c.Position += 4 case 2: params := c.parseParameters(modes, 2) c.write(c.read(c.Position+3), params[0]*params[1], modes[2]) c.Position += 4 case 3: c.write(c.read(c.Position+1), <-c.Channel, modes[0]) c.Position += 2 case 4: params := c.parseParameters(modes, 1) c.Channel <- params[0] c.Position += 2 case 5: params := c.parseParameters(modes, 2) if params[0] != 0 { c.Position = params[1] } else { c.Position += 3 } case 6: params := c.parseParameters(modes, 2) if params[0] == 0 { c.Position = params[1] } else { c.Position += 3 } case 7: params := c.parseParameters(modes, 2) if params[0] < params[1] { c.write(c.read(c.Position+3), 1, modes[2]) } else { c.write(c.read(c.Position+3), 0, modes[2]) } c.Position += 4 case 8: params := c.parseParameters(modes, 2) if params[0] == params[1] { c.write(c.read(c.Position+3), 1, modes[2]) } else { c.write(c.read(c.Position+3), 0, modes[2]) } c.Position += 4 case 9: params := c.parseParameters(modes, 1) c.Offset += params[0] c.Position += 2 } } close(c.Channel) } func NewComputer() Computer { var ints []int if incache == nil { input, _ := ioutil.ReadFile("input") ints = make([]int, 0) for _, v := range strings.Split(string(input), ",") { i, _ := strconv.Atoi(v) ints = append(ints, i) } incache = make([]int, len(ints)) copy(incache, ints) } else { ints = make([]int, len(incache)) copy(ints, incache) } c := Computer{Program: ints, Channel: make(chan int)} go c.run() return c } func NewNetwork() ([]Computer, [][]h.Coordinate) { devices := make([]Computer, 50) queue := make([][]h.Coordinate, len(devices)) for i := 0; i < len(devices); i++ { devices[i] = NewComputer() devices[i].Address = i devices[i].Channel <- i queue[i] = make([]h.Coordinate, 0) } return devices, queue }