Advent of Code 2016 Complete
This commit is contained in:
		| @@ -1,412 +1,48 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"math" | 	"math" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"sort" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/fatih/color" | 	"github.com/fatih/color" | ||||||
| 	termbox "github.com/nsf/termbox-go" |  | ||||||
|  |  | ||||||
| 	"../../" | 	"../../" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var shortestSolutionDist int | var tWidth, tHeight int | ||||||
| var pois []string |  | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	playMode := aoc.ArgIsSet("-play") |  | ||||||
| 	fileNm := aoc.GetArgNumber(1) | 	fileNm := aoc.GetArgNumber(1) | ||||||
| 	txtMaze := aoc.FileToStringSlice(fileNm) | 	if len(os.Args) < 2 { | ||||||
| 	m := CreateMaze(txtMaze) | 		fmt.Println("Usage: ./day24 <mazefile>") | ||||||
| 	if playMode { |  | ||||||
| 		player := CreatePlayer(m) |  | ||||||
| 		err := termbox.Init() |  | ||||||
| 		//tWidth, tHeight := termbox.Size() |  | ||||||
| 		if err != nil { |  | ||||||
| 			fmt.Println("Error initializing termbox") |  | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
| 		defer termbox.Close() | 	var f *Floor | ||||||
| 		var done bool |  | ||||||
| 		for !done { |  | ||||||
| 			fmt.Println(aoc.ClearScreen) |  | ||||||
| 			player.PrintMaze() |  | ||||||
| 			if player.CheckIsDone() { |  | ||||||
| 				fmt.Println("Maze Completed in", player.dist, "steps!") |  | ||||||
| 				fmt.Println("Press any key to quit.") |  | ||||||
| 				done = true |  | ||||||
| 				termbox.PollEvent() |  | ||||||
| 			} else { |  | ||||||
| 				ev := termbox.PollEvent() |  | ||||||
| 				if ev.Type == termbox.EventKey { |  | ||||||
| 					switch { |  | ||||||
| 					case ev.Ch == 'q': |  | ||||||
| 						done = true |  | ||||||
| 					case ev.Key == termbox.KeyArrowUp: |  | ||||||
| 						player.MoveUp() |  | ||||||
| 					case ev.Key == termbox.KeyArrowDown: |  | ||||||
| 						player.MoveDown() |  | ||||||
| 					case ev.Key == termbox.KeyArrowLeft: |  | ||||||
| 						player.MoveLeft() |  | ||||||
| 					case ev.Key == termbox.KeyArrowRight: |  | ||||||
| 						player.MoveRight() |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		shortestSolutionDist = -1 |  | ||||||
| 		m.PrintMaze() |  | ||||||
| 		m.StartSolve() |  | ||||||
| 		//fmt.Println("Shortest Solution: ", shortestSolutionDist) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var numWorkers int |  | ||||||
|  |  | ||||||
| func PrintStatus() { |  | ||||||
| 	fmt.Println(aoc.ClearScreen) |  | ||||||
| 	fmt.Println("Workers: ", numWorkers) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Player struct { |  | ||||||
| 	pos     *Coord |  | ||||||
| 	m       *Maze |  | ||||||
| 	hitPois *CoordCollection |  | ||||||
| 	dist    int |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CreatePlayer(m *Maze) *Player { |  | ||||||
| 	p := new(Player) |  | ||||||
| 	p.pos = CreateCoord(m.start.x, m.start.y) |  | ||||||
| 	p.m = m |  | ||||||
| 	p.hitPois = CreateCoordCollection() |  | ||||||
| 	p.hitPois.Add(p.pos.x, p.pos.y, "0") |  | ||||||
| 	return p |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) CheckIsDone() bool { |  | ||||||
| 	for _, v := range p.m.pois.coords { |  | ||||||
| 		if !p.hitPois.Contains(v.x, v.y) { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) MoveUp() bool { |  | ||||||
| 	if !p.CanMoveTo(p.pos.x, p.pos.y-1) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	p.pos.y-- |  | ||||||
| 	p.dist++ |  | ||||||
| 	p.CheckMovedPos() |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) MoveDown() bool { |  | ||||||
| 	if !p.CanMoveTo(p.pos.x, p.pos.y+1) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	p.pos.y++ |  | ||||||
| 	p.dist++ |  | ||||||
| 	p.CheckMovedPos() |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) MoveRight() bool { |  | ||||||
| 	if !p.CanMoveTo(p.pos.x+1, p.pos.y) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	p.pos.x++ |  | ||||||
| 	p.dist++ |  | ||||||
| 	p.CheckMovedPos() |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) MoveLeft() bool { |  | ||||||
| 	if !p.CanMoveTo(p.pos.x-1, p.pos.y) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	p.pos.x-- |  | ||||||
| 	p.dist++ |  | ||||||
| 	p.CheckMovedPos() |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) CanMoveTo(x, y int) bool { |  | ||||||
| 	return !p.m.walls.Contains(x, y) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) CheckMovedPos() { |  | ||||||
| 	if c, err := p.m.pois.GetXY(p.pos.x, p.pos.y); err == nil { |  | ||||||
| 		p.hitPois.Add(c.x, c.y, c.label) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) PrintMaze() { |  | ||||||
| 	var err error |  | ||||||
| 	poiC := color.New(color.BgGreen) |  | ||||||
| 	hitPoiC := color.New(color.BgBlue) |  | ||||||
| 	playerC := color.New(color.BgYellow) |  | ||||||
| 	target := color.New(color.BgRed) |  | ||||||
| 	next := p.FindClosestNewPoi() |  | ||||||
| 	for y := 0; y < p.m.h; y++ { |  | ||||||
| 		for x := 0; x < p.m.w; x++ { |  | ||||||
| 			var c *Coord |  | ||||||
| 			if c, err = p.m.walls.GetXY(x, y); err == nil { |  | ||||||
| 				fmt.Print(c.label) |  | ||||||
| 			} else if p.pos.is(x, y) { |  | ||||||
| 				playerC.Print("@") |  | ||||||
| 			} else if p.m.start.is(x, y) { |  | ||||||
| 				hitPoiC.Print("0") |  | ||||||
| 			} else if c, err = p.m.pois.GetXY(x, y); err == nil { |  | ||||||
| 				if p.hitPois.Contains(x, y) { |  | ||||||
| 					hitPoiC.Print(c.label) |  | ||||||
| 				} else if next != nil && next.is(x, y) { |  | ||||||
| 					target.Print(c.label) |  | ||||||
| 				} else { |  | ||||||
| 					poiC.Print(c.label) |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				fmt.Print(".") |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		fmt.Println() |  | ||||||
| 	} |  | ||||||
| 	fmt.Printf("Next Closest POI (%s: %d,%d)\n", next.label, next.x, next.y) |  | ||||||
| 	fmt.Printf("Steps Taken: %d\n", p.dist) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Player) FindClosestNewPoi() *Coord { |  | ||||||
| 	var shortestPoi Coord |  | ||||||
| 	shortestDist := -1 |  | ||||||
| 	for _, v := range p.m.pois.coords { |  | ||||||
| 		if !p.hitPois.Contains(v.x, v.y) { |  | ||||||
| 			if t := FindSLDistance(p.pos, &v); t < shortestDist || shortestDist == -1 { |  | ||||||
| 				shortestDist = t |  | ||||||
| 				shortestPoi = v |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return &shortestPoi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Coord struct { |  | ||||||
| 	x, y  int |  | ||||||
| 	label string |  | ||||||
| 	dist  int |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CreateCoord(x, y int) *Coord { |  | ||||||
| 	return &Coord{x: x, y: y, label: ""} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Coord) is(x, y int) bool { |  | ||||||
| 	return c.x == x && c.y == y |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type CoordCollection struct { |  | ||||||
| 	coords []Coord |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CreateCoordCollection() *CoordCollection { |  | ||||||
| 	cc := new(CoordCollection) |  | ||||||
| 	return cc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CreateCCFromCoordSlice(c []Coord) *CoordCollection { |  | ||||||
| 	cc := new(CoordCollection) |  | ||||||
| 	for i := range c { |  | ||||||
| 		cc.Add(c[i].x, c[i].y, c[i].label) |  | ||||||
| 	} |  | ||||||
| 	return cc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (cc *CoordCollection) Add(x, y int, l string) { |  | ||||||
| 	if !cc.Contains(x, y) { |  | ||||||
| 		cc.coords = append(cc.coords, Coord{x: x, y: y, label: l}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (cc *CoordCollection) Contains(x, y int) bool { |  | ||||||
| 	if cc.coords != nil && len(cc.coords) > 0 { |  | ||||||
| 		for i := range cc.coords { |  | ||||||
| 			if cc.coords[i].x == x && cc.coords[i].y == y { |  | ||||||
| 				return true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (cc *CoordCollection) GetXY(x, y int) (*Coord, error) { |  | ||||||
| 	for i := range cc.coords { |  | ||||||
| 		if cc.coords[i].x == x && cc.coords[i].y == y { |  | ||||||
| 			return &cc.coords[i], nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil, errors.New("Collection Doesn't Contain Coord") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Path struct { |  | ||||||
| 	coords []Coord |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Path) Append(c Coord) { |  | ||||||
| 	p.coords = append(p.coords, c) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Path) Contains(x, y int) bool { |  | ||||||
| 	for i := range p.coords { |  | ||||||
| 		if p.coords[i].is(x, y) { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Path) GetCoordAt(x, y int) *Coord { |  | ||||||
| 	for i := range p.coords { |  | ||||||
| 		if p.coords[i].is(x, y) { |  | ||||||
| 			return &p.coords[i] |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Maze struct { |  | ||||||
| 	start, end *Coord |  | ||||||
| 	pois       *CoordCollection |  | ||||||
| 	walls      *CoordCollection |  | ||||||
| 	h, w       int |  | ||||||
| 	solvePath  Path |  | ||||||
| 	testedPath Path |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CreateMaze(inp []string) *Maze { |  | ||||||
| 	m := new(Maze) |  | ||||||
| 	m.pois = CreateCoordCollection() |  | ||||||
| 	m.walls = CreateCoordCollection() |  | ||||||
| 	for y := range inp { |  | ||||||
| 		for x := range inp[y] { |  | ||||||
| 			if inp[y][x] == '#' { |  | ||||||
| 				m.walls.Add(x, y, "#") //aoc.FillChar) |  | ||||||
| 			} else if inp[y][x] != '.' { |  | ||||||
| 				if inp[y][x] == '0' { |  | ||||||
| 					m.start = &Coord{x: x, y: y, label: "0"} |  | ||||||
| 				} else { |  | ||||||
| 					m.pois.Add(x, y, string(inp[y][x])) |  | ||||||
| 				} |  | ||||||
| 				newOne := true |  | ||||||
| 				for pi := range pois { |  | ||||||
| 					if pois[pi] == string(inp[y][x]) { |  | ||||||
| 						newOne = false |  | ||||||
| 						break |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				if newOne { |  | ||||||
| 					pois = append(pois, string(inp[y][x])) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if x > m.w { |  | ||||||
| 				m.w = x |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if y > m.h { |  | ||||||
| 			m.h = y |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	m.w++ |  | ||||||
| 	return m |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CopyMaze(walls, pois *CoordCollection) *Maze { |  | ||||||
| 	newM := new(Maze) |  | ||||||
| 	newM.pois = CreateCoordCollection() |  | ||||||
| 	newM.walls = CreateCoordCollection() |  | ||||||
| 	for _, v := range walls.coords { |  | ||||||
| 		newM.walls.Add(v.x, v.y, v.label) |  | ||||||
| 	} |  | ||||||
| 	for _, v := range pois.coords { |  | ||||||
| 		newM.pois.Add(v.x, v.y, v.label) |  | ||||||
| 	} |  | ||||||
| 	return newM |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Maze) PrintMaze() { |  | ||||||
| 	var err error |  | ||||||
| 	poiC := color.New(color.BgGreen) |  | ||||||
| 	for y := 0; y < m.h; y++ { |  | ||||||
| 		for x := 0; x < m.w; x++ { |  | ||||||
| 			var c *Coord |  | ||||||
| 			if c, err = m.walls.GetXY(x, y); err == nil { |  | ||||||
| 				fmt.Print(c.label) |  | ||||||
| 			} else if m.start.is(x, y) { |  | ||||||
| 				poiC.Print("0") |  | ||||||
| 			} else if c, err = m.pois.GetXY(x, y); err == nil { |  | ||||||
| 				poiC.Print(c.label) |  | ||||||
| 			} else { |  | ||||||
| 				fmt.Print(" ") |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		fmt.Println() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var shortestPoiDist map[string]int |  | ||||||
|  |  | ||||||
| // StartSolve finds the shortest distance between every poi and every other poi |  | ||||||
| // Then figures out the shortest one to hit them all |  | ||||||
| func (m *Maze) StartSolve() { |  | ||||||
| 	shortestPoiDist = make(map[string]int) |  | ||||||
| 	for _, i := range m.pois.coords { |  | ||||||
| 		if dist, gud := m.GetShortestPath(m.start.x, m.start.y, i.x, i.y, 0, *new(Path)); gud { |  | ||||||
| 			shortestPoiDist[m.start.label+";"+i.label] = dist |  | ||||||
| 		} |  | ||||||
| 		for _, j := range m.pois.coords { |  | ||||||
| 			if i.label != j.label { |  | ||||||
| 				fst, scd := i, j |  | ||||||
| 				if i.label[0] > j.label[0] { |  | ||||||
| 					fst, scd = j, i |  | ||||||
| 				} |  | ||||||
| 				if _, ok := shortestPoiDist[fst.label+";"+scd.label]; !ok { |  | ||||||
| 					if dist, gud := m.GetShortestPath(i.x, i.y, j.x, j.y, 0, *new(Path)); gud { |  | ||||||
| 						shortestPoiDist[fst.label+";"+scd.label] = dist |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var poiString string | 	var poiString string | ||||||
| 	fmt.Println("pois", pois) | 	f = CreateFloorFromFile(fileNm) | ||||||
| 	for i := range pois { | 	for i := 0; i < len(f.pois); i++ { | ||||||
| 		poiString += pois[i] | 		poiString += f.pois[i].Name() | ||||||
|  | 		if f.debug { | ||||||
|  | 			for j := i + 1; j < len(f.pois); j++ { | ||||||
|  | 				idx := f.pois[i].Name() + ";" + f.pois[j].Name() | ||||||
|  | 				fmt.Println(idx, f.shortestPaths[idx]) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	poiPerms := aoc.StringPermutations(poiString) | 	poiPerms := aoc.StringPermutations(poiString) | ||||||
| 	var wrk []string |  | ||||||
| 	for i := range poiPerms { |  | ||||||
| 		var found bool |  | ||||||
| 		for j := range wrk { |  | ||||||
| 			if wrk[j] == poiPerms[i] { |  | ||||||
| 				found = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !found { |  | ||||||
| 			wrk = append(wrk, poiPerms[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	poiPerms = wrk |  | ||||||
| 	shortest := -1 | 	shortest := -1 | ||||||
| 	var shortestPerm string | 	var shortestPerm string | ||||||
| 	for _, perm := range poiPerms { | 	for _, perm := range poiPerms { | ||||||
| 		if perm[0] != '0' { | 		if perm[0] != '0' { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  | 		if aoc.ArgIsSet("-2") { | ||||||
|  | 			// For part 2 we return to 0 | ||||||
|  | 			perm = perm + "0" | ||||||
|  | 		} | ||||||
| 		var permTtl int | 		var permTtl int | ||||||
| 		for i := range perm { | 		for i := range perm { | ||||||
| 			if i > 0 { | 			if i > 0 { | ||||||
| @@ -414,7 +50,7 @@ func (m *Maze) StartSolve() { | |||||||
| 				if beg[0] > end[0] { | 				if beg[0] > end[0] { | ||||||
| 					beg, end = end, beg | 					beg, end = end, beg | ||||||
| 				} | 				} | ||||||
| 				permTtl += shortestPoiDist[beg+";"+end] | 				permTtl += f.shortestPaths[beg+";"+end] | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if permTtl < shortest || shortest == -1 { | 		if permTtl < shortest || shortest == -1 { | ||||||
| @@ -425,95 +61,440 @@ func (m *Maze) StartSolve() { | |||||||
| 	fmt.Println(shortestPerm, ": ", shortest) | 	fmt.Println(shortestPerm, ": ", shortest) | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetShortestPath just finds the shortest path between two points | type Floor struct { | ||||||
| func (m *Maze) GetShortestPath(begX, begY, endX, endY, dist int, path Path) (int, bool) { | 	cells []string | ||||||
| 	if begX == endX && begY == endY { | 	start *Coord | ||||||
| 		return dist, true | 	end   *Coord | ||||||
| 	} | 	pois  []Coord | ||||||
| 	if path.Contains(begX, begY) { |  | ||||||
| 		return 0, false | 	shortestPaths map[string]int | ||||||
| 	} |  | ||||||
| 	// Figure out if there is a shorter path to this coordinate | 	testedPath Path | ||||||
| 	if !m.walls.Contains(begX-1, begY) { | 	solvePath  Path | ||||||
| 		if t := path.GetCoordAt(begX-1, begY); t != nil { |  | ||||||
| 			if t.dist+1 < dist { | 	cellCount int | ||||||
| 				return 0, false |  | ||||||
| 			} | 	debug bool | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !m.walls.Contains(begX+1, begY) { |  | ||||||
| 		if t := path.GetCoordAt(begX+1, begY); t != nil { |  | ||||||
| 			if t.dist+1 < dist { |  | ||||||
| 				return 0, false |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !m.walls.Contains(begX, begY-1) { |  | ||||||
| 		if t := path.GetCoordAt(begX, begY-1); t != nil { |  | ||||||
| 			if t.dist+1 < dist { |  | ||||||
| 				return 0, false |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !m.walls.Contains(begX, begY+1) { |  | ||||||
| 		if t := path.GetCoordAt(begX, begY+1); t != nil { |  | ||||||
| 			if t.dist+1 < dist { |  | ||||||
| 				return 0, false |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	path.Append(Coord{x: begX, y: begY, dist: dist}) |  | ||||||
| 	shortest := -1 |  | ||||||
| 	var foundSol bool |  | ||||||
| 	if path.GetCoordAt(begX-1, begY) == nil && !m.walls.Contains(begX-1, begY) { |  | ||||||
| 		if nDist, sol := m.GetShortestPath(begX-1, begY, endX, endY, dist+1, path); sol { |  | ||||||
| 			foundSol = true |  | ||||||
| 			if nDist < shortest || shortest == -1 { |  | ||||||
| 				shortest = nDist |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if path.GetCoordAt(begX, begY-1) == nil && !m.walls.Contains(begX, begY-1) { |  | ||||||
| 		if nDist, sol := m.GetShortestPath(begX, begY-1, endX, endY, dist+1, path); sol { |  | ||||||
| 			foundSol = true |  | ||||||
| 			if nDist < shortest || shortest == -1 { |  | ||||||
| 				shortest = nDist |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if path.GetCoordAt(begX+1, begY) == nil && !m.walls.Contains(begX+1, begY) { |  | ||||||
| 		if nDist, sol := m.GetShortestPath(begX+1, begY, endX, endY, dist+1, path); sol { |  | ||||||
| 			foundSol = true |  | ||||||
| 			if nDist < shortest || shortest == -1 { |  | ||||||
| 				shortest = nDist |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if path.GetCoordAt(begX, begY+1) == nil && !m.walls.Contains(begX, begY+1) { |  | ||||||
| 		if nDist, sol := m.GetShortestPath(begX, begY+1, endX, endY, dist+1, path); sol { |  | ||||||
| 			foundSol = true |  | ||||||
| 			if nDist < shortest || shortest == -1 { |  | ||||||
| 				shortest = nDist |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return shortest, foundSol |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *Maze) FindClosestPoi() *Coord { | func CreateFloorFromFile(fileNm string) *Floor { | ||||||
| 	var shortestPoi Coord | 	f := new(Floor) | ||||||
| 	shortestDist := -1 | 	f.debug = aoc.ArgIsSet("-d") | ||||||
| 	for _, v := range m.pois.coords { | 	f.cells = aoc.FileToStringSlice(fileNm) | ||||||
| 		if t := FindSLDistance(m.start, &v); t < shortestDist || shortestDist == -1 { | 	for y := range f.cells { | ||||||
| 			shortestDist = t | 		for x := range f.cells[y] { | ||||||
| 			shortestPoi = v | 			if f.cells[y][x] != '#' { | ||||||
|  | 				f.cellCount++ | ||||||
|  | 			} | ||||||
|  | 			if f.cells[y][x] != '#' && f.cells[y][x] != '.' { | ||||||
|  | 				// A point of interest | ||||||
|  | 				wrkCoord := NewCoord(x, y, f.cells[y][x]) | ||||||
|  | 				f.pois = append(f.pois, *wrkCoord) | ||||||
|  | 				if f.cells[y][x] == '0' { | ||||||
|  | 					f.start = wrkCoord | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 	return &shortestPoi | 		} | ||||||
|  | 	} | ||||||
|  | 	// Sort the pois | ||||||
|  | 	sort.Sort(CoordByLabel(f.pois)) | ||||||
|  |  | ||||||
|  | 	// Find the shortest paths between all points of interest | ||||||
|  | 	f.shortestPaths = make(map[string]int) | ||||||
|  | 	if aoc.ArgIsSet("-2") { | ||||||
|  |  | ||||||
|  | 		/* Output from running part 1 | ||||||
|  | 		   1;2 232 | ||||||
|  | 		   1;3 30 | ||||||
|  | 		   1;4 188 | ||||||
|  | 		   1;5 230 | ||||||
|  | 		   1;6 80 | ||||||
|  | 		   1;7 54 | ||||||
|  | 		   2;3 218 | ||||||
|  | 		   2;4 56 | ||||||
|  | 		   2;5 74 | ||||||
|  | 		   2;6 212 | ||||||
|  | 		   2;7 278 | ||||||
|  | 		   3;4 174 | ||||||
|  | 		   3;5 216 | ||||||
|  | 		   3;6 66 | ||||||
|  | 		   3;7 80 | ||||||
|  | 		   4;5 54 | ||||||
|  | 		   4;6 168 | ||||||
|  | 		   4;7 234 | ||||||
|  | 		   5;6 210 | ||||||
|  | 		   5;7 276 | ||||||
|  | 		   6;7 126 | ||||||
|  | 		*/ | ||||||
|  |  | ||||||
|  | 		f.shortestPaths["0;1"] = 30 | ||||||
|  | 		f.shortestPaths["0;2"] = 258 | ||||||
|  | 		f.shortestPaths["0;3"] = 56 | ||||||
|  | 		f.shortestPaths["0;4"] = 214 | ||||||
|  | 		f.shortestPaths["0;5"] = 256 | ||||||
|  | 		f.shortestPaths["0;6"] = 106 | ||||||
|  | 		f.shortestPaths["0;7"] = 44 | ||||||
|  | 		f.shortestPaths["1;2"] = 232 | ||||||
|  | 		f.shortestPaths["1;3"] = 30 | ||||||
|  | 		f.shortestPaths["1;4"] = 188 | ||||||
|  | 		f.shortestPaths["1;5"] = 230 | ||||||
|  | 		f.shortestPaths["1;6"] = 80 | ||||||
|  | 		f.shortestPaths["1;7"] = 54 | ||||||
|  | 		f.shortestPaths["2;3"] = 218 | ||||||
|  | 		f.shortestPaths["2;4"] = 56 | ||||||
|  | 		f.shortestPaths["2;5"] = 74 | ||||||
|  | 		f.shortestPaths["2;6"] = 212 | ||||||
|  | 		f.shortestPaths["2;7"] = 278 | ||||||
|  | 		f.shortestPaths["3;4"] = 174 | ||||||
|  | 		f.shortestPaths["3;5"] = 216 | ||||||
|  | 		f.shortestPaths["3;6"] = 66 | ||||||
|  | 		f.shortestPaths["3;7"] = 80 | ||||||
|  | 		f.shortestPaths["4;5"] = 54 | ||||||
|  | 		f.shortestPaths["4;6"] = 168 | ||||||
|  | 		//f.shortestPaths["4;7"] = 266 | ||||||
|  | 		//f.shortestPaths["5;6"] = 262 | ||||||
|  | 		//f.shortestPaths["5;7"] = 304 | ||||||
|  | 		f.shortestPaths["6;7"] = 126 | ||||||
|  | 	} | ||||||
|  | 	for i := range f.pois { | ||||||
|  | 		for j := range f.pois { | ||||||
|  | 			if i != j { | ||||||
|  | 				one, two := f.pois[i], f.pois[j] | ||||||
|  | 				if one.label > two.label { | ||||||
|  | 					one, two = two, one | ||||||
|  | 				} | ||||||
|  | 				idx := string(one.label) + ";" + string(two.label) | ||||||
|  | 				if _, ok := f.shortestPaths[idx]; !ok { | ||||||
|  | 					p := f.GetShortestPath(&one, &two) | ||||||
|  | 					if f.debug { | ||||||
|  | 						fmt.Println(p.Name()) | ||||||
|  | 						f.PrintPath(p) | ||||||
|  | 						fmt.Println("Path length:", len(p.coords), "\n") | ||||||
|  | 						for i := range p.coords { | ||||||
|  | 							fmt.Print(p.coords[i].Name(), ",") | ||||||
|  | 						} | ||||||
|  | 						fmt.Println() | ||||||
|  | 					} | ||||||
|  | 					f.shortestPaths[idx] = len(p.coords) - 1 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return f | ||||||
| } | } | ||||||
|  |  | ||||||
| func FindSLDistance(p1, p2 *Coord) int { | func (f *Floor) GetPoi(lbl byte) *Coord { | ||||||
| 	a := math.Abs(float64(p1.x) - float64(p2.x)) | 	for i := range f.pois { | ||||||
| 	b := math.Abs(float64(p1.y) - float64(p2.y)) | 		if f.pois[i].label == lbl { | ||||||
| 	return int(math.Pow(a, 2) + math.Pow(b, 2)) | 			return &f.pois[i] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var shortestFound int | ||||||
|  |  | ||||||
|  | func (f *Floor) GetShortestPath(st, end *Coord) Path { | ||||||
|  | 	f.solvePath = *new(Path) | ||||||
|  | 	f.testedPath = *new(Path) | ||||||
|  | 	hldStart := f.start | ||||||
|  | 	f.start = end | ||||||
|  | 	f.end = st | ||||||
|  | 	shortestFound = -1 | ||||||
|  | 	path, sol := f.Solve(f.start.x, f.start.y, 0) | ||||||
|  | 	f.start = hldStart | ||||||
|  | 	if sol { | ||||||
|  | 		return path | ||||||
|  | 	} | ||||||
|  | 	return *new(Path) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Floor) Print() { | ||||||
|  | 	for y := range f.cells { | ||||||
|  | 		for x := range f.cells[y] { | ||||||
|  | 			fmt.Print(string(f.cells[y][x])) | ||||||
|  | 		} | ||||||
|  | 		fmt.Println() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Floor) PrintPath(p Path) { | ||||||
|  | 	pathCol := color.New(color.BgGreen) | ||||||
|  | 	for y := range f.cells { | ||||||
|  | 		for x := range f.cells[y] { | ||||||
|  | 			if p.ContainsCoord(x, y) { | ||||||
|  | 				if f.cells[y][x] == '.' { | ||||||
|  | 					pathCol.Print("O") | ||||||
|  | 				} else { | ||||||
|  | 					pathCol.Print(string(f.cells[y][x])) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				fmt.Print(string(f.cells[y][x])) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		fmt.Println() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Floor) IsWall(x, y int) bool { | ||||||
|  | 	return f.cells[y][x] == '#' | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Floor) GetCoord(x, y int) *Coord { | ||||||
|  | 	return NewCoord(x, y, f.cells[y][x]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Floor) Solve(x, y, dist int) (Path, bool) { | ||||||
|  | 	var tPathContains bool | ||||||
|  | 	shortest := new(Path) | ||||||
|  | 	wrkCoord := *f.GetCoord(x, y) | ||||||
|  | 	wrkCoord.dist = dist | ||||||
|  |  | ||||||
|  | 	if f.end.Is(x, y) { | ||||||
|  | 		shortest.Append(wrkCoord) | ||||||
|  | 		if shortestFound == -1 || dist < shortestFound { | ||||||
|  | 			shortestFound = dist | ||||||
|  | 		} | ||||||
|  | 		return *shortest, true | ||||||
|  | 	} | ||||||
|  | 	if f.IsWall(x, y) || (shortestFound != -1 && dist > shortestFound) { | ||||||
|  | 		return *shortest, false | ||||||
|  | 	} | ||||||
|  | 	// Test if we already have this coord via a shorter path | ||||||
|  | 	if t := f.testedPath.GetCoordAt(x, y); t != nil { | ||||||
|  | 		if t.dist <= dist { | ||||||
|  | 			return *shortest, false | ||||||
|  | 		} | ||||||
|  | 		tPathContains = true | ||||||
|  | 		t.dist = dist | ||||||
|  | 	} | ||||||
|  | 	// Test if there is a shorter path to this coordinate | ||||||
|  | 	if !f.IsWall(x-1, y) { | ||||||
|  | 		if t := f.testedPath.GetCoordAt(x-1, y); t != nil { | ||||||
|  | 			if t.dist+1 < wrkCoord.dist { | ||||||
|  | 				return *shortest, false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !f.IsWall(x+1, y) { | ||||||
|  | 		if t := f.testedPath.GetCoordAt(x+1, y); t != nil { | ||||||
|  | 			if t.dist+1 < wrkCoord.dist { | ||||||
|  | 				return *shortest, false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !f.IsWall(x, y-1) { | ||||||
|  | 		if t := f.testedPath.GetCoordAt(x, y-1); t != nil { | ||||||
|  | 			if t.dist+1 < wrkCoord.dist { | ||||||
|  | 				return *shortest, false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !f.IsWall(x, y+1) { | ||||||
|  | 		if t := f.testedPath.GetCoordAt(x, y+1); t != nil { | ||||||
|  | 			if t.dist+1 < wrkCoord.dist { | ||||||
|  | 				return *shortest, false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// We haven't found a shorter path to this coordinate, so carry on | ||||||
|  | 	if !tPathContains { | ||||||
|  | 		f.testedPath.Append(wrkCoord) | ||||||
|  | 		if f.debug { | ||||||
|  | 			fmt.Println(aoc.ClearScreen, f.start.Name(), "=>", f.end.Name(), "\n", len(f.testedPath.coords), "/", f.cellCount, "\n", "Shortest:", shortestFound) | ||||||
|  | 			f.PrintPath(f.testedPath) | ||||||
|  | 			time.Sleep(time.Millisecond * 50) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	var solvePaths []Path | ||||||
|  | 	var didN, didE, didS, didW bool | ||||||
|  |  | ||||||
|  | 	var preferNS bool | ||||||
|  | 	if math.Abs(float64(f.end.y-y)) > math.Abs(float64(f.end.x-x)) { | ||||||
|  | 		// Favor N/S movements | ||||||
|  | 		preferNS = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for tdir := 0; tdir < 2; tdir++ { | ||||||
|  | 		if preferNS { | ||||||
|  | 			switch { | ||||||
|  | 			case f.end.y < y && !didN: | ||||||
|  | 				f.TestAndUpdate(x, y-1, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x, y-1, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didN = true | ||||||
|  | 			case f.end.y > y && !didS: | ||||||
|  | 				f.TestAndUpdate(x, y+1, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x, y+1, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didS = true | ||||||
|  | 			case f.end.x > x && !didE: | ||||||
|  | 				f.TestAndUpdate(x+1, y, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x+1, y, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didE = true | ||||||
|  | 			case f.end.x < x && !didW: | ||||||
|  | 				f.TestAndUpdate(x-1, y, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x-1, y, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didW = true | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			switch { | ||||||
|  | 			case f.end.x > x && !didE: | ||||||
|  | 				f.TestAndUpdate(x+1, y, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x+1, y, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didE = true | ||||||
|  | 			case f.end.x < x && !didW: | ||||||
|  | 				f.TestAndUpdate(x-1, y, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x-1, y, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didW = true | ||||||
|  | 			case f.end.y < y && !didN: | ||||||
|  | 				f.TestAndUpdate(x, y-1, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x, y-1, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didN = true | ||||||
|  | 			case f.end.y > y && !didS: | ||||||
|  | 				f.TestAndUpdate(x, y+1, dist+1) | ||||||
|  | 				if p, sol := f.Solve(x, y+1, wrkCoord.dist+1); sol { | ||||||
|  | 					p.Append(wrkCoord) | ||||||
|  | 					solvePaths = append(solvePaths, p) | ||||||
|  | 				} | ||||||
|  | 				didS = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !didN { | ||||||
|  | 		f.TestAndUpdate(x, y-1, dist+1) | ||||||
|  | 		if p, sol := f.Solve(x, y-1, wrkCoord.dist+1); sol { | ||||||
|  | 			p.Append(wrkCoord) | ||||||
|  | 			solvePaths = append(solvePaths, p) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !didE { | ||||||
|  | 		f.TestAndUpdate(x+1, y, dist+1) | ||||||
|  | 		if p, sol := f.Solve(x+1, y, wrkCoord.dist+1); sol { | ||||||
|  | 			p.Append(wrkCoord) | ||||||
|  | 			solvePaths = append(solvePaths, p) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !didS { | ||||||
|  | 		f.TestAndUpdate(x, y+1, dist+1) | ||||||
|  | 		if p, sol := f.Solve(x, y+1, wrkCoord.dist+1); sol { | ||||||
|  | 			p.Append(wrkCoord) | ||||||
|  | 			solvePaths = append(solvePaths, p) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !didW { | ||||||
|  | 		f.TestAndUpdate(x-1, y, dist+1) | ||||||
|  | 		if p, sol := f.Solve(x-1, y, wrkCoord.dist+1); sol { | ||||||
|  | 			p.Append(wrkCoord) | ||||||
|  | 			solvePaths = append(solvePaths, p) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var sol bool | ||||||
|  | 	if sol = len(solvePaths) > 0; sol { | ||||||
|  | 		shortest = &solvePaths[0] | ||||||
|  | 		for i := range solvePaths { | ||||||
|  | 			if solvePaths[i].Length()+1 < shortest.Length() { | ||||||
|  | 				shortest = &solvePaths[i] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return *shortest, sol | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Floor) TestAndUpdate(x, y, dist int) { | ||||||
|  | 	if t := f.testedPath.GetCoordAt(x, y); t != nil { | ||||||
|  | 		if t.dist > dist+1 { | ||||||
|  | 			f.testedPath.SetCoordDist(x, y, dist+1) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Coord struct { | ||||||
|  | 	x, y, dist int | ||||||
|  | 	label      byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewCoord(x, y int, label byte) *Coord { | ||||||
|  | 	return &Coord{x, y, -1, label} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *Coord) Name() string { | ||||||
|  | 	if c.label != '#' && c.label != '.' && c.label != 0 { | ||||||
|  | 		return string(c.label) | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprint("(", c.x, ",", c.y, ")") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *Coord) Is(x, y int) bool { | ||||||
|  | 	return c.x == x && c.y == y | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CoordByLabel []Coord | ||||||
|  |  | ||||||
|  | func (a CoordByLabel) Len() int           { return len(a) } | ||||||
|  | func (a CoordByLabel) Swap(i, j int)      { a[i], a[j] = a[j], a[i] } | ||||||
|  | func (a CoordByLabel) Less(i, j int) bool { return a[i].label < a[j].label } | ||||||
|  |  | ||||||
|  | type Path struct { | ||||||
|  | 	coords []Coord | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Path) Append(c Coord) { | ||||||
|  | 	p.coords = append(p.coords, c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Path) ContainsCoord(x, y int) bool { | ||||||
|  | 	for i := range p.coords { | ||||||
|  | 		if p.coords[i].Is(x, y) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Path) GetCoordAt(x, y int) *Coord { | ||||||
|  | 	for i := range p.coords { | ||||||
|  | 		if p.coords[i].Is(x, y) { | ||||||
|  | 			return &p.coords[i] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Path) SetCoordDist(x, y, dist int) bool { | ||||||
|  | 	for i := range p.coords { | ||||||
|  | 		if p.coords[i].Is(x, y) { | ||||||
|  | 			p.coords[i].dist = dist | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Path) Length() int { | ||||||
|  | 	return len(p.coords) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Path) Name() string { | ||||||
|  | 	return p.coords[0].Name() + " -> " + p.coords[len(p.coords)-1].Name() | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										69
									
								
								2016/day24/problem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								2016/day24/problem
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | Advent of Code | ||||||
|  |  | ||||||
|  | --- Day 24: Air Duct Spelunking --- | ||||||
|  |  | ||||||
|  |    You've finally met your match; the doors that provide access to the roof are locked tight, and all of the controls and | ||||||
|  |    related electronics are inaccessible. You simply can't reach them. | ||||||
|  |  | ||||||
|  |    The robot that cleans the air ducts, however, can. | ||||||
|  |  | ||||||
|  |    It's not a very fast little robot, but you reconfigure it to be able to interface with some of the exposed wires that | ||||||
|  |    have been routed through the HVAC system. If you can direct it to each of those locations, you should be able to bypass | ||||||
|  |    the security controls. | ||||||
|  |  | ||||||
|  |    You extract the duct layout for this area from some blueprints you acquired and create a map with the relevant locations | ||||||
|  |    marked (your puzzle input). 0 is your current location, from which the cleaning robot embarks; the other numbers are (in | ||||||
|  |    no particular order) the locations the robot needs to visit at least once each. Walls are marked as #, and open passages | ||||||
|  |    are marked as .. Numbers behave like open passages. | ||||||
|  |  | ||||||
|  |    For example, suppose you have a map like the following: | ||||||
|  |  | ||||||
|  |  ########### | ||||||
|  |  #0.1.....2# | ||||||
|  |  #.#######.# | ||||||
|  |  #4.......3# | ||||||
|  |  ########### | ||||||
|  |  | ||||||
|  |    To reach all of the points of interest as quickly as possible, you would have the robot take the following path: | ||||||
|  |  | ||||||
|  |      • 0 to 4 (2 steps) | ||||||
|  |      • 4 to 1 (4 steps; it can't move diagonally) | ||||||
|  |      • 1 to 2 (6 steps) | ||||||
|  |      • 2 to 3 (2 steps) | ||||||
|  |  | ||||||
|  |    Since the robot isn't very fast, you need to find it the shortest route. This path is the fewest steps (in the above | ||||||
|  |    example, a total of 14) required to start at 0 and then visit every other location at least once. | ||||||
|  |  | ||||||
|  |    Given your actual map, and starting from location 0, what is the fewest number of steps required to visit every non-0 | ||||||
|  |    number marked on the map at least once? | ||||||
|  |  | ||||||
|  |    Your puzzle answer was ____. | ||||||
|  |  | ||||||
|  | --- Part Two --- | ||||||
|  |  | ||||||
|  |    Of course, if you leave the cleaning robot somewhere weird, someone is bound to notice. | ||||||
|  |  | ||||||
|  |    What is the fewest number of steps required to start at 0, visit every non-0 number marked on the map at least once, and | ||||||
|  |    then return to 0? | ||||||
|  |  | ||||||
|  |    Your puzzle answer was _____. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | References | ||||||
|  |  | ||||||
|  |    Visible links | ||||||
|  |    . http://adventofcode.com/ | ||||||
|  |    . http://adventofcode.com/2016/about | ||||||
|  |    . http://adventofcode.com/2016/support | ||||||
|  |    . http://adventofcode.com/2016/events | ||||||
|  |    . http://adventofcode.com/2016/settings | ||||||
|  |    . http://adventofcode.com/2016/auth/logout | ||||||
|  |    . http://adventofcode.com/2016 | ||||||
|  |    . http://adventofcode.com/2016 | ||||||
|  |    . http://adventofcode.com/2016/leaderboard | ||||||
|  |    . http://adventofcode.com/2016/stats | ||||||
|  |    . http://adventofcode.com/2016/sponsors | ||||||
|  |    . http://adventofcode.com/2016/sponsors | ||||||
|  |    . https://en.wikipedia.org/wiki/HVAC | ||||||
|  |    . http://adventofcode.com/2016 | ||||||
|  |    . http://adventofcode.com/2016/day/24/input | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| ########### |  | ||||||
| #0.1.....2# |  | ||||||
| #.#######.# |  | ||||||
| #4.......3# |  | ||||||
| ########### |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| ################################################################################################################################################################################### |  | ||||||
| #.........#...#.............#...#.#.#.....#...........#.........#.#...#.......#.#.#...#...#.................#...........#.#...#.#.......#.......#.......#...#...#.....#.....#.....# |  | ||||||
| #.#.#.#.#.#.#########.#.#.###.#.#.#.###.###.#.###.#.#.#.###.#.###.#.#.#.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#.###.#.#.#.#####.#.#.#.###.#.#.#.#.#.###.#.###.###.###.###.#.# |  | ||||||
| #...#...#...#.......#...#.#.#.....#...#.....#.........#.......#.#...#...#.#.............#...#.......#.#.#...#.#.....#.......#...#.....#...#...........#...#...#.#...............#2# |  | ||||||
| #.###.#.#.#####.###.###.#.#.#.#.###.#.#.#####.#######.#.###.###.#.#.#.#.#.#####.###.###.#.#.#####.#.###.#.###.#.#.#.#.#.#######.#######.#.#.###.###.###.#.#.#.#.#.#.###.#.###.#.### |  | ||||||
| #.......#.........#.#.#...#...#...#.....#.#.............#.....#...#.......#.#.....#...#...#.......#.............................#.#...#...#...#.....#...#.......#.......#.......#.# |  | ||||||
| #.###.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#####.#.#.#######.#.#.#.#.#.#.#####.#.###.#.#####.#.###.###.#.#.###.###.#.#.#.#####.#.###.#.#.#.#######.###.#.#.#.###.###.# |  | ||||||
| #...#.#...#...#...#...#.#...#.....#...#...........#.....#.........#.#...#...#...#.#...#.......#...#.#.....#.#.....#...#.#.......#.#.#.......#.......#...........#.#.#...#.#.......# |  | ||||||
| #.#.#.###.###.#.#.#######.#.#.#.#.#.#.###.###########.#.#.#####.###.#.#.#####.#.#.#.#####.###.#.###.#####.###.#####.#########.#.###.#.###.#.#.#.#.###.###.#.#####.#.#.#.#.#.###.#.# |  | ||||||
| #.......#.......#...........#...#.#...#.............#.#.#...#...#.....#...#...#.#...#...#.......#.#.#.#...#.....#.#.#.........#...#...#.....#.#...........#.#.......#.#.#...#...#.# |  | ||||||
| #.#.#.###.#####.#.#####.#.###.#.#.#.###.#.#.#.###.###.#.#####.###.#####.#.#.#####.#.#.#.#######.#.###.#.###.#####.#.#####.#.#.#####.#.#.#.#.###.#.#######.#.#.#.###########.#.#.#.# |  | ||||||
| #.#.#.....#.#1..........#.#...#...#.....#.........#...............#.#...#.....#...#.......#...........#.#...#.#.....#.............#.............#.....#...#.....#...#.....#.#.....# |  | ||||||
| #.#.###.#.#.#####.#.#.#.###.#####.#.#.#.###.###.#.#.#.#####.#.#.###.#.#.#####.#.#.#.#.#.#.###.#.###.#.#.#.#.#.#.###.#.#.#####.###.###.###.#.#.#.###.#.#.#.#.#.###.#.#.#####.#####.# |  | ||||||
| #...#...#.#...#.#.#.#.#.......#.....................#.#...............#.......#.#...#.#.#.....#.#.#...#...#.#.......#.....#.#...#.........#.#.#...#.........#.............#.....#.# |  | ||||||
| #.###.###.###.#.#.###.#.#####.#.#####.###.###########.###.#.#.#####.#.#.#.###.###.#.#.#.#.###.#.#.#.#.###.#.###.#.#########.#.#.#.#.###.#.#.#.#.#.#.#.#.#.###.###.#####.#.#####.### |  | ||||||
| #.....#.#.......#.#.#.....#...#.......#...#.#...#.............#.#.#.....#.........#...#.#.........#.#.#.#...#.#...#...#.......#.....#.#.....#.#.#.......#.#...#.#.....#.......#...# |  | ||||||
| #.#.###.#.#######.#.#.#.###.#.###.###.#####.#.#.#.#.###.###.###.#.#.#####.#.#####.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.###.#.###.#.#.###.###.#.#######.#.#.#.#.#.###.#.#.#.#.###.# |  | ||||||
| #.#.#.......#...#...#.#.#.#.....#.....#...#...#.....#...#...#...#...........#...........#.........#.#.....#.....#.......#...#...#...#.#...#.#.#.........#...#.....#.#...#.#.....#.# |  | ||||||
| #.#.#.#####.#.#.#.###.###.#.#.#####.#.###.#.#.#####.#.#####.#.#.#.#####.###.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#####.###.###.#.#.#.#.#.#.#.#.#.###.###.#.#######.#####.###.#.###.###.# |  | ||||||
| #.#.......#.#...#.#...#.#.#.......#...#.....#.........#.....#.#.....#...#...#.#.#...#.....#...#...#...#.....#.......#.#.#...#.#.........#...#.#...#.#.#.#.#.....#.....#.........#.# |  | ||||||
| #.###.#.#.#.###.#.#.###.#.#######.#.#.###.#.###.#.#.#.#.#.#.###.#####.#.###.#######.#####.###.#.#.#.#.#.###.#.#####.#.#.#.#.#.#.#.#.#.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.###.#.#.# |  | ||||||
| #0#...#.#.#.....#...#...#.#...#...#...#...#.......#.#.#.....#...#...#...#...#...#...#.........#.......#...#.......#...#...#...#.......#...#...............#.....#...#...#...#.#...# |  | ||||||
| ###.#.#.###.###########.#.#####.###.###.###.###.###.#.###.#.#.#.#.#.#.#.#.#.#.###.#.###.#.#.#.#####.#.#.#.#.#.#.#.#.#.#.#.###.#####.#.#####.#.#.#.#####.#.#.#####.#####.#.#.#.#.### |  | ||||||
| #...#.#...#.......#...#...........#...#.....#...#...#.#.........#.....#.#...#.......#.....#.#.#.........#.........#...#.#.#...#.#.....#.....................#...............#.....# |  | ||||||
| ###.#.#.#.#.#.###.#.#.#.#.#######.#.#.###.###.#.#.#####.###.###.###.###.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#####.###.#######.###.###.#.###.###.###.###.#.#.# |  | ||||||
| #...#.............#.#...........#.#.#.....#.....#.......#.#.....#...#.........#.....#.......#.....#...#.....#...#.......#...#...#.#.........#...#...........#.......#...#.......#.# |  | ||||||
| ###.#.#########.#.#.#####.#.#.#.#.#.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.#.#####.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.###.#######.#######.#.#.###.#.#.#.#.#.#.###.#.# |  | ||||||
| #.#...............#.#.......#.....#.................#...#.#.#.......#...#.......#.#.#...#.#.......#...#.....#.....#...#.#.......#.#.#.....#.....#.....#.#...#.#...#...#.#...#.#.#.# |  | ||||||
| #.#.#.###.#.#.#.#.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.#.###.#.#.###.#.#.###.###.#.#.#####.#####.#####.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.###.#.#.#.##### |  | ||||||
| #...#.#.....#...#.............#.#.#.#.........#.#...#.#.....#.......#...#.#.#.....#.......#...#...#...........#.....#.#.......#...#...#.#.........#...#...#.....#.......#...#.....# |  | ||||||
| ###.#.#.#.#.#.#.###.#.###.#.###.#.#.#.#.###.#.###.#.#.#####.###.###.#.#.#.###.#.#.###.###.###.#.#.#.#.###.#####.###.#####.#.#.###.###.#####.#####.#.#.#######.###.###.###.#.#.#.#.# |  | ||||||
| #...#.#.....#.........#.......#.#...#.......#...#...#...........#.....#.......#...........#.......#...#...#.....#.....#...#.......#.#.#...#.#.....#.........#.....#...#.#...#.#...# |  | ||||||
| ###.###.###.###.#.#####.###.#.#.#.#.#.###.###.###.###.#.#####.#.#.#.#.###.#.#.###.#.###.###.#.#.###.###.#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.#.###.#.#.#####.#.#####.#.#.#.#####.#.#.# |  | ||||||
| #.#.#...#.#.#...................#.......#.#.#.#.#.....#.#.#.....#...#.......#.#...#.......#.....#...#.....#...#...#.#...#...#.........#...#...#...........#.........#.#...#...#...# |  | ||||||
| #.#.#.###.#.###.#####.#.#.###.#.#####.#.#.#.#.#.#.###.#.#.#.#######.###.#.#.#.#.#.###.#.#.#.###.#.#####.#.#.#.#####.#.###.#.#.###.###.#.###.#.#.###########.#.#######.#.#.#.###.### |  | ||||||
| #...#.#.#...#.#.................#.#.....#.#.........#.....#.#.#...#.#.........#...#.#...#.#.........#...#...#.....#.#.#.#.....#...#.#.....#.#.............#.#...#.#.#.....#...#...# |  | ||||||
| #.#.###.#.#.#.#.#.#.#.#.###.###.#.#.#######.#######.###.#.#.#.###.#.#.#####.###.###.#####.#.#####.#.#.#.#.#.#.###.#.#.#.#.###.#.#.#.#.#.#.#.#####.#.#.#.#.###.#.#.#.#.#####.###.#.# |  | ||||||
| #...#.....#.....#.....#.#...#...#...............#...#...#...#.....#...#.#.....#.#.#...#...#.#...#...#.....#...#...#...................#.........#...#.........#.#...#.......#...#.# |  | ||||||
| ###.#.###.#.#.#.###.#.#.#.#.#.###.###.#.###.###.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#.#.###.#.#.#.#####.#.#.###.#.#######.#######.###.#######.#.#.#.#.###.###.###.#.#.#.#.#.#.#.# |  | ||||||
| #.....#...#.#.#...........#.#...#.........#.....#.#.#...#.....#.............#...#...#...#.#.#...#.......#...#.....#.#.......#.#.....#...#...#...#...#...#...#.#.....#.#.......#.#.# |  | ||||||
| #.#.#.#.#.#.#.#.#.#.###.#####.#.#############.#.###.#.#.#.#.###.###.#######.#.#.###.#.###.#.###.#######.###.###.#.#.###.#.#.#.#.#.###.###.###.#.#.###.#.#.#.#.#.#.###.#.###.#.#.### |  | ||||||
| #...#.....#.#...#.#.....#.....#...#...#.......#.#.......#.#.......#.#.........#...#.#.....#.....#.#.......#.#.......#.......#.....#.....#.....#...#.#.#.#...#.#...#.#...#...#.#.#.# |  | ||||||
| ################################################################################################################################################################################### |  | ||||||
							
								
								
									
										30
									
								
								2016/day25/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								2016/day25/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | cpy a d | ||||||
|  | cpy 7 c | ||||||
|  | cpy 365 b | ||||||
|  | inc d | ||||||
|  | dec b | ||||||
|  | jnz b -2 | ||||||
|  | dec c | ||||||
|  | jnz c -5 | ||||||
|  | cpy d a | ||||||
|  | jnz 0 0 | ||||||
|  | cpy a b | ||||||
|  | cpy 0 a | ||||||
|  | cpy 2 c | ||||||
|  | jnz b 2 | ||||||
|  | jnz 1 6 | ||||||
|  | dec b | ||||||
|  | dec c | ||||||
|  | jnz c -4 | ||||||
|  | inc a | ||||||
|  | jnz 1 -7 | ||||||
|  | cpy 2 b | ||||||
|  | jnz c 2 | ||||||
|  | jnz 1 4 | ||||||
|  | dec b | ||||||
|  | dec c | ||||||
|  | jnz 1 -4 | ||||||
|  | jnz 0 0 | ||||||
|  | out b | ||||||
|  | jnz a -19 | ||||||
|  | jnz 1 -21 | ||||||
							
								
								
									
										505
									
								
								2016/day25/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								2016/day25/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,505 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/fatih/color" | ||||||
|  | 	termbox "github.com/nsf/termbox-go" | ||||||
|  |  | ||||||
|  | 	"../../" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var regs = map[string]int{ | ||||||
|  | 	"a": 0, | ||||||
|  | 	"b": 0, | ||||||
|  | 	"c": 0, | ||||||
|  | 	"d": 0, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var instructions, lastInst []string | ||||||
|  | var curr, cursor int | ||||||
|  | var done, debug, pause, step, skip bool | ||||||
|  | var breakpoints []int | ||||||
|  | var eventChan chan termbox.Event | ||||||
|  | var outBuff string | ||||||
|  |  | ||||||
|  | // Available arguments: | ||||||
|  | // -d		Debug Mode | ||||||
|  | // -p		Pause on start | ||||||
|  | // -25  Run day 25 simulation | ||||||
|  | func main() { | ||||||
|  | 	var inpFn string | ||||||
|  | 	if inpFn = aoc.GetArgNumber(1); inpFn == "" { | ||||||
|  | 		done = true | ||||||
|  | 	} | ||||||
|  | 	if inpFn != "" { | ||||||
|  | 		instructions = aoc.FileToStringSlice(inpFn) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if aoc.ArgIsSet("-d") { | ||||||
|  | 		debug = true | ||||||
|  | 	} | ||||||
|  | 	if aoc.ArgIsSet("-p") { | ||||||
|  | 		pause = true | ||||||
|  | 	} | ||||||
|  | 	if aoc.ArgIsSet("-25") { | ||||||
|  | 		// If running the day 25 simulation, ignore debug and pause flags | ||||||
|  | 		fmt.Println("Running Day 25 simulation, disabling debug & pause") | ||||||
|  | 		debug = false | ||||||
|  | 		pause = false | ||||||
|  | 	} | ||||||
|  | 	if debug { | ||||||
|  | 		err := termbox.Init() | ||||||
|  | 		//tWidth, tHeight := termbox.Size() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println("Error initializing termbox") | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		defer termbox.Close() | ||||||
|  | 		eventChan = make(chan termbox.Event) | ||||||
|  | 		go readUserInput(eventChan) | ||||||
|  | 		go sendNoneEvent(eventChan) | ||||||
|  | 	} | ||||||
|  | 	if aoc.ArgIsSet("-25") { | ||||||
|  | 		var day25Solved bool | ||||||
|  | 		regAStart := regs["a"] | ||||||
|  | 		regBStart := regs["b"] | ||||||
|  | 		regCStart := regs["c"] | ||||||
|  | 		regDStart := regs["d"] | ||||||
|  | 		for !day25Solved { | ||||||
|  | 			regs["a"] = regAStart | ||||||
|  | 			regs["b"] = regBStart | ||||||
|  | 			regs["c"] = regCStart | ||||||
|  | 			regs["d"] = regDStart | ||||||
|  | 			ProcInstructions() | ||||||
|  | 			day25Solved = true | ||||||
|  | 			fmt.Println(regAStart, ":", outBuff) | ||||||
|  | 			if outBuff != "0101010101" { | ||||||
|  | 				day25Solved = false | ||||||
|  | 				regAStart++ | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		ProcInstructions() | ||||||
|  | 		PrintState() | ||||||
|  | 	} | ||||||
|  | 	//fmt.Println("Press any key to exit") | ||||||
|  | 	//termbox.PollEvent() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ProcInstructions() { | ||||||
|  | 	locRegs := make(map[string]int) | ||||||
|  | 	for k, v := range regs { | ||||||
|  | 		locRegs[k] = v | ||||||
|  | 	} | ||||||
|  | 	curr = 0 | ||||||
|  | 	outBuff = "" | ||||||
|  | 	var ins []string | ||||||
|  | 	for curr < len(instructions) || done { | ||||||
|  | 		isBreak := isBreakpoint(curr) | ||||||
|  | 		if debug { | ||||||
|  | 			lastInst = ins | ||||||
|  | 		} | ||||||
|  | 		ins = strings.Fields(instructions[curr]) | ||||||
|  | 		if len(ins) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		if isMultOp(curr) { | ||||||
|  | 			curr = doMultOp(curr) | ||||||
|  | 			skip = true | ||||||
|  | 		} | ||||||
|  | 		if debug { | ||||||
|  | 			if isBreak { | ||||||
|  | 				pause = true | ||||||
|  | 				step = false | ||||||
|  | 			} | ||||||
|  | 			for pause && !step && !done { | ||||||
|  | 				// print state and wait for user to step | ||||||
|  | 				PrintState() | ||||||
|  | 				PrintInstructionState() | ||||||
|  | 				PrintDebugHelp() | ||||||
|  | 				ev := <-eventChan | ||||||
|  | 				if ev.Type == termbox.EventKey { | ||||||
|  | 					switch { | ||||||
|  | 					case ev.Key == termbox.KeySpace: | ||||||
|  | 						pause = !pause | ||||||
|  | 					case ev.Ch == 'q': | ||||||
|  | 						done = true | ||||||
|  | 					case ev.Ch == 'b': | ||||||
|  | 						toggleBreakpoint(cursor) | ||||||
|  | 					case ev.Ch == 's': | ||||||
|  | 						step = true | ||||||
|  | 					case ev.Ch == 'u': | ||||||
|  | 						updateRegister() | ||||||
|  | 					case ev.Key == termbox.KeyArrowUp: | ||||||
|  | 						if cursor > 0 { | ||||||
|  | 							cursor-- | ||||||
|  | 						} | ||||||
|  | 					case ev.Key == termbox.KeyArrowDown: | ||||||
|  | 						if cursor < len(instructions)-1 { | ||||||
|  | 							cursor++ | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			step = false | ||||||
|  | 		} | ||||||
|  | 		if done { | ||||||
|  | 			// User hit 'q' | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		if skip { | ||||||
|  | 			skip = false | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		curr++ | ||||||
|  | 		switch ins[0] { | ||||||
|  | 		case "out": | ||||||
|  | 			v, ok := regs[ins[1]] | ||||||
|  | 			if !ok { | ||||||
|  | 				outBuff += ins[1] | ||||||
|  | 			} else { | ||||||
|  | 				outBuff += aoc.Itoa(v) | ||||||
|  | 			} | ||||||
|  | 			if aoc.ArgIsSet("-25") && len(outBuff) == 10 { | ||||||
|  | 				// This should be long enough for our day 25 answer | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			// If we're not debugging, just print it and reset the buffer | ||||||
|  | 			if !debug && !aoc.ArgIsSet("-25") { | ||||||
|  | 				fmt.Print(outBuff) | ||||||
|  | 				outBuff = "" | ||||||
|  | 			} | ||||||
|  | 		case "jnz": | ||||||
|  | 			// If we have a jnz c -2 it could be moving all of c into another register | ||||||
|  | 			v, ok := regs[ins[1]] | ||||||
|  | 			if !ok { | ||||||
|  | 				v = aoc.Atoi(ins[1]) | ||||||
|  | 			} | ||||||
|  | 			var p int | ||||||
|  | 			if p, ok = regs[ins[2]]; !ok { | ||||||
|  | 				p = aoc.Atoi(ins[2]) | ||||||
|  | 			} | ||||||
|  | 			if v != 0 { | ||||||
|  | 				// Subtract 1 from the jump because we incremented already | ||||||
|  | 				curr += p - 1 | ||||||
|  | 			} | ||||||
|  | 		case "mlt": | ||||||
|  | 			// a three arg instruction: mlt a b c | ||||||
|  | 			// take a * b, store in c | ||||||
|  | 			var ok bool | ||||||
|  | 			src1 := ins[1] | ||||||
|  | 			src2 := ins[2] | ||||||
|  | 			dst := ins[3] | ||||||
|  | 			if _, ok := regs[dst]; !ok { | ||||||
|  | 				// invalid destination | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			var src1I, src2I int | ||||||
|  | 			if src1I, ok = regs[src1]; !ok { | ||||||
|  | 				src1I = aoc.Atoi(src1) | ||||||
|  | 			} | ||||||
|  | 			if src2I, ok = regs[src2]; !ok { | ||||||
|  | 				src2I = aoc.Atoi(src2) | ||||||
|  | 			} | ||||||
|  | 			regs[dst] = src1I * src2I | ||||||
|  | 		case "cpy": | ||||||
|  | 			src := ins[1] | ||||||
|  | 			dst := ins[2] | ||||||
|  | 			// check if dst is a register | ||||||
|  | 			if _, ok := regs[dst]; !ok { | ||||||
|  | 				// Nope, move along | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			// check if the src is a register | ||||||
|  | 			if v, ok := regs[src]; ok { | ||||||
|  | 				regs[dst] = v | ||||||
|  | 			} else { | ||||||
|  | 				// It's not, must be an int | ||||||
|  | 				regs[dst] = aoc.Atoi(src) | ||||||
|  | 			} | ||||||
|  | 		case "inc": | ||||||
|  | 			if _, ok := regs[ins[1]]; !ok { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			regs[ins[1]] = regs[ins[1]] + 1 | ||||||
|  | 		case "dec": | ||||||
|  | 			if _, ok := regs[ins[1]]; !ok { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			regs[ins[1]] = regs[ins[1]] - 1 | ||||||
|  | 		case "tgl": // tgl alters an instruction | ||||||
|  | 			src := ins[1] | ||||||
|  | 			var srcI int | ||||||
|  | 			if v, ok := regs[src]; ok { | ||||||
|  | 				srcI = v | ||||||
|  | 			} else { | ||||||
|  | 				srcI = aoc.Atoi(src) | ||||||
|  | 			} | ||||||
|  | 			srcI = curr + srcI | ||||||
|  | 			if srcI < 0 || srcI > len(instructions)-1 { | ||||||
|  | 				// Invalid instruction | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			altPts := strings.Fields(instructions[srcI-1]) | ||||||
|  | 			if len(altPts) == 2 { // one argument | ||||||
|  | 				if altPts[0] == "inc" { | ||||||
|  | 					altPts[0] = "dec" | ||||||
|  | 				} else { | ||||||
|  | 					altPts[0] = "inc" | ||||||
|  | 				} | ||||||
|  | 			} else { // two arguments | ||||||
|  | 				if altPts[0] == "jnz" { | ||||||
|  | 					altPts[0] = "cpy" | ||||||
|  | 				} else { | ||||||
|  | 					altPts[0] = "jnz" | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			instructions[srcI-1] = strings.Join(altPts, " ") | ||||||
|  | 		} | ||||||
|  | 		if debug { | ||||||
|  | 			PrintState() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fancy State Printing | ||||||
|  | func PrintState() { | ||||||
|  | 	fmt.Println(aoc.ClearScreen) | ||||||
|  | 	PrintRegs() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func PrintRegs() { | ||||||
|  | 	datLine := fmt.Sprint("\u2502  a:", regs["a"], "  b:", regs["b"], "  c:", regs["c"], "  d:", regs["d"], "  \u2502") | ||||||
|  | 	fmt.Println("\u250C" + strings.Repeat("\u2500", len(datLine)-6) + "\u2510") | ||||||
|  | 	fmt.Println(datLine) | ||||||
|  | 	fmt.Println("\u2514" + strings.Repeat("\u2500", len(datLine)-6) + "\u2518") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func PrintInstructionState() { | ||||||
|  | 	optim := color.New(color.FgGreen) | ||||||
|  | 	for pi := range instructions { | ||||||
|  | 		if pi == curr { | ||||||
|  | 			fmt.Print(">") | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Print(" ") | ||||||
|  | 		} | ||||||
|  | 		if isBreakpoint(pi) { | ||||||
|  | 			fmt.Print("B") | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Print(" ") | ||||||
|  | 		} | ||||||
|  | 		if cursor == pi { | ||||||
|  | 			cursor := color.New(color.FgBlack).Add(color.BgWhite) | ||||||
|  | 			if isMultOp(pi) { | ||||||
|  | 				cursor.Println("+ " + instructions[pi] + " == optimized") | ||||||
|  | 			} else if mo, _ := isInMultOp(pi); mo { | ||||||
|  | 				cursor.Println("| " + instructions[pi]) | ||||||
|  | 			} else { | ||||||
|  | 				cursor.Println(instructions[pi]) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if isMultOp(pi) { | ||||||
|  | 				optim.Println("+ " + instructions[pi] + " == optimized") | ||||||
|  | 			} else if mo, _ := isInMultOp(pi); mo { | ||||||
|  | 				optim.Println("| " + instructions[pi]) | ||||||
|  | 			} else { | ||||||
|  | 				fmt.Println(instructions[pi]) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func PrintDebugHelp() { | ||||||
|  | 	fmt.Println("(s): step | (space): pause/unpause | (b): toggle breakpoint") | ||||||
|  | 	fmt.Println("(u): update register to value | (q): quit") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func readUserInput(e chan termbox.Event) { | ||||||
|  | 	for { | ||||||
|  | 		e <- termbox.PollEvent() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func sendNoneEvent(e chan termbox.Event) { | ||||||
|  | 	for { | ||||||
|  | 		time.Sleep(time.Second / 32) | ||||||
|  | 		if !pause { | ||||||
|  | 			e <- termbox.Event{Type: termbox.EventNone} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isInMultOp returns true and the pc for the | ||||||
|  | // start of the multiplication operation if pos is | ||||||
|  | // located within it | ||||||
|  | func isInMultOp(pos int) (bool, int) { | ||||||
|  | 	// We need to check instructions[pos +- 5] to see if any | ||||||
|  | 	// are a multiplication op and return the pos for the (first) one | ||||||
|  | 	// that is | ||||||
|  | 	if isMultOp(pos) { | ||||||
|  | 		return true, pos | ||||||
|  | 	} | ||||||
|  | 	for i := 1; i < 6; i++ { | ||||||
|  | 		if isMultOp(pos - i) { | ||||||
|  | 			return true, (pos - i) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false, pos | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isMultOpStart return whether pos is the start of | ||||||
|  | // what can be optimized as multiplication | ||||||
|  | /* | ||||||
|  | 	   > cpy b c | ||||||
|  | 	     inc a | ||||||
|  | 	     dec c | ||||||
|  | 	     jnz c -2 | ||||||
|  | 	     dec d | ||||||
|  | 	     jnz d -5 | ||||||
|  | 		 == add (b * d) to a | ||||||
|  | 				set c & d to 0 | ||||||
|  | 				curr += 5 | ||||||
|  | */ | ||||||
|  | func isMultOp(pos int) bool { | ||||||
|  | 	if pos < 0 || pos > len(instructions)-1 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	ins := strings.Fields(instructions[pos]) | ||||||
|  | 	if len(ins) < 3 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	if ins[0] == "cpy" { | ||||||
|  | 		if len(instructions) >= pos+6 { | ||||||
|  | 			ins1 := strings.Fields(instructions[pos+1]) | ||||||
|  | 			ins2 := strings.Fields(instructions[pos+2]) | ||||||
|  | 			ins3 := strings.Fields(instructions[pos+3]) | ||||||
|  | 			ins4 := strings.Fields(instructions[pos+4]) | ||||||
|  | 			ins5 := strings.Fields(instructions[pos+5]) | ||||||
|  | 			if ins1[0] == "inc" && ins2[0] == "dec" && ins3[0] == "jnz" && ins4[0] == "dec" && ins5[0] == "jnz" { | ||||||
|  | 				allGood := true | ||||||
|  | 				if allGood { | ||||||
|  | 					// Do the multiplication | ||||||
|  | 					// ins[1] * ins4[1] | ||||||
|  | 					// add that value to ins1[1] | ||||||
|  | 					// set ins[2] to 0 | ||||||
|  | 					// set ins4[1] to 0 | ||||||
|  | 					// Then add 5 to the pc | ||||||
|  | 					src1 := ins[1] | ||||||
|  | 					src2 := ins4[1] | ||||||
|  | 					dst := ins1[1] | ||||||
|  | 					if _, ok := regs[dst]; !ok { | ||||||
|  | 						// Invalid destination register | ||||||
|  | 						allGood = false | ||||||
|  | 					} | ||||||
|  | 					if _, ok := regs[src1]; !ok { | ||||||
|  | 						// Invalid source register | ||||||
|  | 						allGood = false | ||||||
|  | 					} | ||||||
|  | 					if _, ok := regs[src2]; !ok { | ||||||
|  | 						allGood = false | ||||||
|  | 					} | ||||||
|  | 					return allGood | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // doMultOp performs a multiplcation operation and returns the new pc | ||||||
|  | func doMultOp(pos int) int { | ||||||
|  | 	isOp, pos := isInMultOp(pos) | ||||||
|  | 	if !isOp { | ||||||
|  | 		// No optimization to do | ||||||
|  | 		return pos | ||||||
|  | 	} | ||||||
|  | 	ins := strings.Fields(instructions[pos]) | ||||||
|  | 	ins1 := strings.Fields(instructions[pos+1]) | ||||||
|  | 	ins4 := strings.Fields(instructions[pos+4]) | ||||||
|  | 	// Do the multiplication | ||||||
|  | 	// ins[1] * ins4[1] | ||||||
|  | 	// add that value to ins1[1] | ||||||
|  | 	// set ins[2] to 0 | ||||||
|  | 	// set ins4[1] to 0 | ||||||
|  | 	// Then add 5 to the pc | ||||||
|  | 	src1 := ins[1] | ||||||
|  | 	src2 := ins4[1] | ||||||
|  | 	dst := ins1[1] | ||||||
|  | 	regs[dst] += (regs[src1] * regs[src2]) | ||||||
|  | 	regs[ins[2]] = 0 | ||||||
|  | 	regs[ins4[1]] = 0 | ||||||
|  | 	pos += 6 | ||||||
|  | 	return pos | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func toggleBreakpoint(pos int) { | ||||||
|  | 	_, pos = isInMultOp(pos) | ||||||
|  | 	for i := 0; i < len(breakpoints); i++ { | ||||||
|  | 		if breakpoints[i] == pos { | ||||||
|  | 			breakpoints = append(breakpoints[:i], breakpoints[i+1:]...) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	breakpoints = append(breakpoints, pos) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func isBreakpoint(pos int) bool { | ||||||
|  | 	_, pos = isInMultOp(pos) | ||||||
|  | 	for i := 0; i < len(breakpoints); i++ { | ||||||
|  | 		if breakpoints[i] == pos { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func updateRegister() { | ||||||
|  | 	var updReg string | ||||||
|  | 	fmt.Println("Update which register? (a,b,c,d)") | ||||||
|  | 	ev := <-eventChan | ||||||
|  | 	if ev.Type == termbox.EventKey { | ||||||
|  | 		switch { | ||||||
|  | 		case ev.Ch == 'a': | ||||||
|  | 			updReg = "a" | ||||||
|  | 		case ev.Ch == 'b': | ||||||
|  | 			updReg = "b" | ||||||
|  | 		case ev.Ch == 'c': | ||||||
|  | 			updReg = "c" | ||||||
|  | 		case ev.Ch == 'd': | ||||||
|  | 			updReg = "d" | ||||||
|  | 		default: | ||||||
|  | 			fmt.Println("Invalid register (" + string(ev.Ch) + ")") | ||||||
|  | 			fmt.Println("Press any key to continue") | ||||||
|  | 			ev = <-eventChan | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Println("Enter new value (must be integer, end with enter)") | ||||||
|  | 	var newVal string | ||||||
|  | 	for ev.Key != termbox.KeyEnter { | ||||||
|  | 		ev = <-eventChan | ||||||
|  | 		if ev.Ch >= '0' && ev.Ch <= '9' { | ||||||
|  | 			newVal += string(ev.Ch) | ||||||
|  | 			fmt.Print(string(ev.Ch)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	fmt.Println("Setting Register (" + updReg + ") to " + newVal) | ||||||
|  | 	var v int | ||||||
|  | 	var e error | ||||||
|  | 	if v, e = strconv.Atoi(newVal); e != nil { | ||||||
|  | 		fmt.Println() | ||||||
|  | 		fmt.Println("Error parsing integer") | ||||||
|  | 		fmt.Println(e) | ||||||
|  | 		fmt.Println("Press any key to continue") | ||||||
|  | 		ev = <-eventChan | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	regs[updReg] = v | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								2016/day25/problem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								2016/day25/problem
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | Advent of Code | ||||||
|  |  | ||||||
|  | --- Day 25: Clock Signal --- | ||||||
|  |  | ||||||
|  |    You open the door and find yourself on the roof. The city sprawls away from you for miles and miles. | ||||||
|  |  | ||||||
|  |    There's not much time now - it's already Christmas, but you're nowhere near the North Pole, much too far to deliver | ||||||
|  |    these stars to the sleigh in time. | ||||||
|  |  | ||||||
|  |    However, maybe the huge antenna up here can offer a solution. After all, the sleigh doesn't need the stars, exactly; it | ||||||
|  |    needs the timing data they provide, and you happen to have a massive signal generator right here. | ||||||
|  |  | ||||||
|  |    You connect the stars you have to your prototype computer, connect that to the antenna, and begin the transmission. | ||||||
|  |  | ||||||
|  |    Nothing happens. | ||||||
|  |  | ||||||
|  |    You call the service number printed on the side of the antenna and quickly explain the situation. "I'm not sure what | ||||||
|  |    kind of equipment you have connected over there," he says, "but you need a clock signal." You try to explain that this | ||||||
|  |    is a signal for a clock. | ||||||
|  |  | ||||||
|  |    "No, no, a clock signal - timing information so the antenna computer knows how to read the data you're sending it. An | ||||||
|  |    endless, alternating pattern of 0, 1, 0, 1, 0, 1, 0, 1, 0, 1...." He trails off. | ||||||
|  |  | ||||||
|  |    You ask if the antenna can handle a clock signal at the frequency you would need to use for the data from the stars. | ||||||
|  |    "There's no way it can! The only antenna we've installed capable of that is on top of a top-secret Easter Bunny | ||||||
|  |    installation, and you're definitely not-" You hang up the phone. | ||||||
|  |  | ||||||
|  |    You've extracted the antenna's clock signal generation assembunny code (your puzzle input); it looks mostly compatible | ||||||
|  |    with code you worked on just recently. | ||||||
|  |  | ||||||
|  |    This antenna code, being a signal generator, uses one extra instruction: | ||||||
|  |  | ||||||
|  |      • out x transmits x (either an integer or the value of a register) as the next value for the clock signal. | ||||||
|  |  | ||||||
|  |    The code takes a value (via register a) that describes the signal to generate, but you're not sure how it's used. You'll | ||||||
|  |    have to find the input to produce the right signal through experimentation. | ||||||
|  |  | ||||||
|  |    What is the lowest positive integer that can be used to initialize register a and cause the code to output a clock | ||||||
|  |    signal of 0, 1, 0, 1... repeating forever? | ||||||
|  |  | ||||||
|  |    Your puzzle answer was ____. | ||||||
|  |  | ||||||
|  | --- Part Two --- | ||||||
|  |  | ||||||
|  |    The antenna is ready. Now, all you need is the fifty stars required to generate the signal for the sleigh, but you don't | ||||||
|  |    have enough. | ||||||
|  |  | ||||||
|  |    You look toward the sky in desperation... suddenly noticing that a lone star has been installed at the top of the | ||||||
|  |    antenna! Only 49 more to go. | ||||||
|  |  | ||||||
|  |    If you like, you can [ [Retransmit the Signal] ] . | ||||||
|  |  | ||||||
|  |    Both parts of this puzzle are complete! They provide two gold stars: ** | ||||||
|  |  | ||||||
|  | References | ||||||
|  |  | ||||||
|  |    Visible links | ||||||
|  |    . http://adventofcode.com/ | ||||||
|  |    . http://adventofcode.com/2016/about | ||||||
|  |    . http://adventofcode.com/2016/support | ||||||
|  |    . http://adventofcode.com/2016/events | ||||||
|  |    . http://adventofcode.com/2016/settings | ||||||
|  |    . http://adventofcode.com/2016/auth/logout | ||||||
|  |    . http://adventofcode.com/2016 | ||||||
|  |    . http://adventofcode.com/2016 | ||||||
|  |    . http://adventofcode.com/2016/leaderboard | ||||||
|  |    . http://adventofcode.com/2016/stats | ||||||
|  |    . http://adventofcode.com/2016/sponsors | ||||||
|  |    . http://adventofcode.com/2016/sponsors | ||||||
|  |    . https://en.wikipedia.org/wiki/Clock_signal | ||||||
|  |    . http://adventofcode.com/2016/day/12 | ||||||
|  |    . http://adventofcode.com/2016/day/23 | ||||||
|  |    . http://adventofcode.com/2016 | ||||||
|  |    . http://adventofcode.com/2016/day/25/input | ||||||
							
								
								
									
										16
									
								
								helpers.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								helpers.go
									
									
									
									
									
								
							| @@ -95,7 +95,21 @@ func PrintProgress(curr, total int) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func StringPermutations(str string) []string { | func StringPermutations(str string) []string { | ||||||
| 	return stringPermHelper(str, 0) | 	perms := stringPermHelper(str, 0) | ||||||
|  | 	var wrk []string | ||||||
|  | 	// Now de-dupe | ||||||
|  | 	for i := range perms { | ||||||
|  | 		var found bool | ||||||
|  | 		for j := range wrk { | ||||||
|  | 			if wrk[j] == perms[i] { | ||||||
|  | 				found = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if !found { | ||||||
|  | 			wrk = append(wrk, perms[i]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return wrk | ||||||
| } | } | ||||||
|  |  | ||||||
| func stringPermHelper(str string, i int) []string { | func stringPermHelper(str string, i int) []string { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user