Advent of Code 2016 Complete

This commit is contained in:
2016-12-28 08:48:23 -06:00
parent a01a65364c
commit a0c467fd41
8 changed files with 1144 additions and 519 deletions

View File

@@ -1,412 +1,48 @@
package main
import (
"errors"
"fmt"
"math"
"os"
"sort"
"time"
"github.com/fatih/color"
termbox "github.com/nsf/termbox-go"
"../../"
)
var shortestSolutionDist int
var pois []string
var tWidth, tHeight int
func main() {
playMode := aoc.ArgIsSet("-play")
fileNm := aoc.GetArgNumber(1)
txtMaze := aoc.FileToStringSlice(fileNm)
m := CreateMaze(txtMaze)
if playMode {
player := CreatePlayer(m)
err := termbox.Init()
//tWidth, tHeight := termbox.Size()
if err != nil {
fmt.Println("Error initializing termbox")
os.Exit(1)
}
defer termbox.Close()
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)
if len(os.Args) < 2 {
fmt.Println("Usage: ./day24 <mazefile>")
os.Exit(1)
}
}
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 f *Floor
var poiString string
fmt.Println("pois", pois)
for i := range pois {
poiString += pois[i]
f = CreateFloorFromFile(fileNm)
for i := 0; i < len(f.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)
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
var shortestPerm string
for _, perm := range poiPerms {
if perm[0] != '0' {
continue
}
if aoc.ArgIsSet("-2") {
// For part 2 we return to 0
perm = perm + "0"
}
var permTtl int
for i := range perm {
if i > 0 {
@@ -414,7 +50,7 @@ func (m *Maze) StartSolve() {
if beg[0] > end[0] {
beg, end = end, beg
}
permTtl += shortestPoiDist[beg+";"+end]
permTtl += f.shortestPaths[beg+";"+end]
}
}
if permTtl < shortest || shortest == -1 {
@@ -425,95 +61,440 @@ func (m *Maze) StartSolve() {
fmt.Println(shortestPerm, ": ", shortest)
}
// GetShortestPath just finds the shortest path between two points
func (m *Maze) GetShortestPath(begX, begY, endX, endY, dist int, path Path) (int, bool) {
if begX == endX && begY == endY {
return dist, true
}
if path.Contains(begX, begY) {
return 0, false
}
// Figure out if there is a shorter path to this coordinate
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+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
type Floor struct {
cells []string
start *Coord
end *Coord
pois []Coord
shortestPaths map[string]int
testedPath Path
solvePath Path
cellCount int
debug bool
}
func (m *Maze) FindClosestPoi() *Coord {
var shortestPoi Coord
shortestDist := -1
for _, v := range m.pois.coords {
if t := FindSLDistance(m.start, &v); t < shortestDist || shortestDist == -1 {
shortestDist = t
shortestPoi = v
func CreateFloorFromFile(fileNm string) *Floor {
f := new(Floor)
f.debug = aoc.ArgIsSet("-d")
f.cells = aoc.FileToStringSlice(fileNm)
for y := range f.cells {
for x := range f.cells[y] {
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 {
a := math.Abs(float64(p1.x) - float64(p2.x))
b := math.Abs(float64(p1.y) - float64(p2.y))
return int(math.Pow(a, 2) + math.Pow(b, 2))
func (f *Floor) GetPoi(lbl byte) *Coord {
for i := range f.pois {
if f.pois[i].label == lbl {
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
View 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

View File

@@ -1,5 +0,0 @@
###########
#0.1.....2#
#.#######.#
#4.......3#
###########

View File

@@ -1,43 +0,0 @@
###################################################################################################################################################################################
#.........#...#.............#...#.#.#.....#...........#.........#.#...#.......#.#.#...#...#.................#...........#.#...#.#.......#.......#.......#...#...#.....#.....#.....#
#.#.#.#.#.#.#########.#.#.###.#.#.#.###.###.#.###.#.#.#.###.#.###.#.#.#.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#.###.#.#.#.#####.#.#.#.###.#.#.#.#.#.###.#.###.###.###.###.#.#
#...#...#...#.......#...#.#.#.....#...#.....#.........#.......#.#...#...#.#.............#...#.......#.#.#...#.#.....#.......#...#.....#...#...........#...#...#.#...............#2#
#.###.#.#.#####.###.###.#.#.#.#.###.#.#.#####.#######.#.###.###.#.#.#.#.#.#####.###.###.#.#.#####.#.###.#.###.#.#.#.#.#.#######.#######.#.#.###.###.###.#.#.#.#.#.#.###.#.###.#.###
#.......#.........#.#.#...#...#...#.....#.#.............#.....#...#.......#.#.....#...#...#.......#.............................#.#...#...#...#.....#...#.......#.......#.......#.#
#.###.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#####.#.#.#######.#.#.#.#.#.#.#####.#.###.#.#####.#.###.###.#.#.###.###.#.#.#.#####.#.###.#.#.#.#######.###.#.#.#.###.###.#
#...#.#...#...#...#...#.#...#.....#...#...........#.....#.........#.#...#...#...#.#...#.......#...#.#.....#.#.....#...#.#.......#.#.#.......#.......#...........#.#.#...#.#.......#
#.#.#.###.###.#.#.#######.#.#.#.#.#.#.###.###########.#.#.#####.###.#.#.#####.#.#.#.#####.###.#.###.#####.###.#####.#########.#.###.#.###.#.#.#.#.###.###.#.#####.#.#.#.#.#.###.#.#
#.......#.......#...........#...#.#...#.............#.#.#...#...#.....#...#...#.#...#...#.......#.#.#.#...#.....#.#.#.........#...#...#.....#.#...........#.#.......#.#.#...#...#.#
#.#.#.###.#####.#.#####.#.###.#.#.#.###.#.#.#.###.###.#.#####.###.#####.#.#.#####.#.#.#.#######.#.###.#.###.#####.#.#####.#.#.#####.#.#.#.#.###.#.#######.#.#.#.###########.#.#.#.#
#.#.#.....#.#1..........#.#...#...#.....#.........#...............#.#...#.....#...#.......#...........#.#...#.#.....#.............#.............#.....#...#.....#...#.....#.#.....#
#.#.###.#.#.#####.#.#.#.###.#####.#.#.#.###.###.#.#.#.#####.#.#.###.#.#.#####.#.#.#.#.#.#.###.#.###.#.#.#.#.#.#.###.#.#.#####.###.###.###.#.#.#.###.#.#.#.#.#.###.#.#.#####.#####.#
#...#...#.#...#.#.#.#.#.......#.....................#.#...............#.......#.#...#.#.#.....#.#.#...#...#.#.......#.....#.#...#.........#.#.#...#.........#.............#.....#.#
#.###.###.###.#.#.###.#.#####.#.#####.###.###########.###.#.#.#####.#.#.#.###.###.#.#.#.#.###.#.#.#.#.###.#.###.#.#########.#.#.#.#.###.#.#.#.#.#.#.#.#.#.###.###.#####.#.#####.###
#.....#.#.......#.#.#.....#...#.......#...#.#...#.............#.#.#.....#.........#...#.#.........#.#.#.#...#.#...#...#.......#.....#.#.....#.#.#.......#.#...#.#.....#.......#...#
#.#.###.#.#######.#.#.#.###.#.###.###.#####.#.#.#.#.###.###.###.#.#.#####.#.#####.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.###.#.###.#.#.###.###.#.#######.#.#.#.#.#.###.#.#.#.#.###.#
#.#.#.......#...#...#.#.#.#.....#.....#...#...#.....#...#...#...#...........#...........#.........#.#.....#.....#.......#...#...#...#.#...#.#.#.........#...#.....#.#...#.#.....#.#
#.#.#.#####.#.#.#.###.###.#.#.#####.#.###.#.#.#####.#.#####.#.#.#.#####.###.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#####.###.###.#.#.#.#.#.#.#.#.#.###.###.#.#######.#####.###.#.###.###.#
#.#.......#.#...#.#...#.#.#.......#...#.....#.........#.....#.#.....#...#...#.#.#...#.....#...#...#...#.....#.......#.#.#...#.#.........#...#.#...#.#.#.#.#.....#.....#.........#.#
#.###.#.#.#.###.#.#.###.#.#######.#.#.###.#.###.#.#.#.#.#.#.###.#####.#.###.#######.#####.###.#.#.#.#.#.###.#.#####.#.#.#.#.#.#.#.#.#.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.###.#.#.#
#0#...#.#.#.....#...#...#.#...#...#...#...#.......#.#.#.....#...#...#...#...#...#...#.........#.......#...#.......#...#...#...#.......#...#...............#.....#...#...#...#.#...#
###.#.#.###.###########.#.#####.###.###.###.###.###.#.###.#.#.#.#.#.#.#.#.#.#.###.#.###.#.#.#.#####.#.#.#.#.#.#.#.#.#.#.#.###.#####.#.#####.#.#.#.#####.#.#.#####.#####.#.#.#.#.###
#...#.#...#.......#...#...........#...#.....#...#...#.#.........#.....#.#...#.......#.....#.#.#.........#.........#...#.#.#...#.#.....#.....................#...............#.....#
###.#.#.#.#.#.###.#.#.#.#.#######.#.#.###.###.#.#.#####.###.###.###.###.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#####.###.#######.###.###.#.###.###.###.###.#.#.#
#...#.............#.#...........#.#.#.....#.....#.......#.#.....#...#.........#.....#.......#.....#...#.....#...#.......#...#...#.#.........#...#...........#.......#...#.......#.#
###.#.#########.#.#.#####.#.#.#.#.#.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.#.#####.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.###.#######.#######.#.#.###.#.#.#.#.#.#.###.#.#
#.#...............#.#.......#.....#.................#...#.#.#.......#...#.......#.#.#...#.#.......#...#.....#.....#...#.#.......#.#.#.....#.....#.....#.#...#.#...#...#.#...#.#.#.#
#.#.#.###.#.#.#.#.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.#.###.#.#.###.#.#.###.###.#.#.#####.#####.#####.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.###.#.#.#.#####
#...#.#.....#...#.............#.#.#.#.........#.#...#.#.....#.......#...#.#.#.....#.......#...#...#...........#.....#.#.......#...#...#.#.........#...#...#.....#.......#...#.....#
###.#.#.#.#.#.#.###.#.###.#.###.#.#.#.#.###.#.###.#.#.#####.###.###.#.#.#.###.#.#.###.###.###.#.#.#.#.###.#####.###.#####.#.#.###.###.#####.#####.#.#.#######.###.###.###.#.#.#.#.#
#...#.#.....#.........#.......#.#...#.......#...#...#...........#.....#.......#...........#.......#...#...#.....#.....#...#.......#.#.#...#.#.....#.........#.....#...#.#...#.#...#
###.###.###.###.#.#####.###.#.#.#.#.#.###.###.###.###.#.#####.#.#.#.#.###.#.#.###.#.###.###.#.#.###.###.#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.#.###.#.#.#####.#.#####.#.#.#.#####.#.#.#
#.#.#...#.#.#...................#.......#.#.#.#.#.....#.#.#.....#...#.......#.#...#.......#.....#...#.....#...#...#.#...#...#.........#...#...#...........#.........#.#...#...#...#
#.#.#.###.#.###.#####.#.#.###.#.#####.#.#.#.#.#.#.###.#.#.#.#######.###.#.#.#.#.#.###.#.#.#.###.#.#####.#.#.#.#####.#.###.#.#.###.###.#.###.#.#.###########.#.#######.#.#.#.###.###
#...#.#.#...#.#.................#.#.....#.#.........#.....#.#.#...#.#.........#...#.#...#.#.........#...#...#.....#.#.#.#.....#...#.#.....#.#.............#.#...#.#.#.....#...#...#
#.#.###.#.#.#.#.#.#.#.#.###.###.#.#.#######.#######.###.#.#.#.###.#.#.#####.###.###.#####.#.#####.#.#.#.#.#.#.###.#.#.#.#.###.#.#.#.#.#.#.#.#####.#.#.#.#.###.#.#.#.#.#####.###.#.#
#...#.....#.....#.....#.#...#...#...............#...#...#...#.....#...#.#.....#.#.#...#...#.#...#...#.....#...#...#...................#.........#...#.........#.#...#.......#...#.#
###.#.###.#.#.#.###.#.#.#.#.#.###.###.#.###.###.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#.#.###.#.#.#.#####.#.#.###.#.#######.#######.###.#######.#.#.#.#.###.###.###.#.#.#.#.#.#.#.#
#.....#...#.#.#...........#.#...#.........#.....#.#.#...#.....#.............#...#...#...#.#.#...#.......#...#.....#.#.......#.#.....#...#...#...#...#...#...#.#.....#.#.......#.#.#
#.#.#.#.#.#.#.#.#.#.###.#####.#.#############.#.###.#.#.#.#.###.###.#######.#.#.###.#.###.#.###.#######.###.###.#.#.###.#.#.#.#.#.###.###.###.#.#.###.#.#.#.#.#.#.###.#.###.#.#.###
#...#.....#.#...#.#.....#.....#...#...#.......#.#.......#.#.......#.#.........#...#.#.....#.....#.#.......#.#.......#.......#.....#.....#.....#...#.#.#.#...#.#...#.#...#...#.#.#.#
###################################################################################################################################################################################