diff --git a/2016/day24/main.go b/2016/day24/main.go index 532efd9..3d49a87 100644 --- a/2016/day24/main.go +++ b/2016/day24/main.go @@ -13,6 +13,7 @@ import ( ) var shortestSolutionDist int +var pois []string func main() { playMode := aoc.ArgIsSet("-play") @@ -58,8 +59,8 @@ func main() { } else { shortestSolutionDist = -1 m.PrintMaze() - m.StartSolve(0) - fmt.Println("Shortest Solution: ", shortestSolutionDist) + m.StartSolve() + //fmt.Println("Shortest Solution: ", shortestSolutionDist) } } @@ -259,7 +260,7 @@ func (p *Path) Append(c Coord) { p.coords = append(p.coords, c) } -func (p *Path) ContainsCoord(x, y int) bool { +func (p *Path) Contains(x, y int) bool { for i := range p.coords { if p.coords[i].is(x, y) { return true @@ -293,13 +294,23 @@ func CreateMaze(inp []string) *Maze { for y := range inp { for x := range inp[y] { if inp[y][x] == '#' { - m.walls.Add(x, y, aoc.FillChar) + 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 @@ -346,167 +357,148 @@ func (m *Maze) PrintMaze() { } } -// StartSolve kicks off the solve and returns the lowest number of steps, or an error -func (m *Maze) StartSolve(dist int) (int, error) { - numWorkers++ - solved, d := m.Solve(m.start.x, m.start.y, 0) - dist += d - numWorkers-- - if !solved { - return dist, errors.New("Couldn't solve maze. :(") - } - fmt.Println("Ended Solve (current workers:", numWorkers, "; current shortest:", shortestSolutionDist, ")") - if dist < shortestSolutionDist || shortestSolutionDist == -1 { - shortestSolutionDist = dist - } - return dist, nil -} - -// We want to build a set of all solutions from one poi to all others -// then find the shortest path that hits them all -func (m *Maze) Solve(x, y, dist int) (bool, int) { - wrkCoord := Coord{x: x, y: y, dist: dist} - if m.end == nil || m.end.is(x, y) { - // We found a point of interest! (or we're just starting) - - // No end set. For each poi, we want to fork a new maze being solved - // with the end set to that poi, and that poi removed from the poi list - if len(m.pois.coords) == 0 { - // Unless there aren't any left, then we solved it - return true, dist - } - for i := range m.pois.coords { - endPoi := m.pois.coords[i] - // Copy the pois - newPois := make([]Coord, len(m.pois.coords)) - copy(newPois, m.pois.coords) - // Then delete the endPoi from it - newPois = append(newPois[:i], newPois[i+1:]...) - // Create a new maze with start set to this point and end set to the first poi - newM := CopyMaze(m.walls, CreateCCFromCoordSlice(newPois)) - newM.start = &wrkCoord - newM.end = &endPoi - newM.StartSolve(dist + 1) - } - } - if m.testedPath.ContainsCoord(x, y) { - return false, 0 - } - // Figure out if there is a shorter path to this coordinate - if !m.walls.Contains(x-1, y) { - if t := m.testedPath.GetCoordAt(x-1, y); t != nil { - if t.dist+1 < wrkCoord.dist { - return false, 0 - } - } - } - if !m.walls.Contains(x+1, y) { - if t := m.testedPath.GetCoordAt(x+1, y); t != nil { - if t.dist+1 < wrkCoord.dist { - return false, 0 - } - } - } - if !m.walls.Contains(x, y-1) { - if t := m.testedPath.GetCoordAt(x, y-1); t != nil { - if t.dist+1 < wrkCoord.dist { - return false, 0 - } - } - } - if !m.walls.Contains(x, y+1) { - if t := m.testedPath.GetCoordAt(x, y+1); t != nil { - if t.dist+1 < wrkCoord.dist { - return false, 0 - } - } - } - m.testedPath.Append(wrkCoord) - shortest := -1 - var foundSol bool - if m.testedPath.GetCoordAt(x-1, y) == nil && !m.walls.Contains(x-1, y) { - if sol, nDist := m.Solve(x-1, y, wrkCoord.dist+1); sol { - foundSol = true - if nDist < shortest || shortest == -1 { - shortest = nDist - } - } - } - if m.testedPath.GetCoordAt(x, y-1) == nil && !m.walls.Contains(x, y-1) { - if sol, nDist := m.Solve(x, y-1, wrkCoord.dist+1); sol { - foundSol = true - if nDist < shortest || shortest == -1 { - shortest = nDist - } - } - } - if m.testedPath.GetCoordAt(x+1, y) == nil && !m.walls.Contains(x+1, y) { - if sol, nDist := m.Solve(x+1, y, wrkCoord.dist+1); sol { - foundSol = true - if nDist < shortest || shortest == -1 { - shortest = nDist - } - } - } - if m.testedPath.GetCoordAt(x, y+1) == nil && !m.walls.Contains(x, y+1) { - if sol, nDist := m.Solve(x, y+1, wrkCoord.dist+1); sol { - foundSol = true - if nDist < shortest || shortest == -1 { - shortest = nDist - } - } - } - return foundSol, dist + shortest -} - var shortestPoiDist map[string]int -// Pt2StartSolve finds the shortest distance between every poi and every other poi -func (m *Maze) Pt2StartSolve() { +// 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 gud, dist := m.GetShortestPath(m.start, i, 0); gud { + 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[0] > j[0] { + if i.label[0] > j.label[0] { fst, scd = j, i } - if _, ok := shortestPoiDist[fst+";"+scd]; !ok { - if gud, dist := m.GetShortestPath(i, j, 0); gud { - shortestPoiDist[fst+";"+scd] = dist + 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 } } } } } - // TODO: Find shortest path that hits them all + + var poiString string + fmt.Println("pois", pois) + for i := range pois { + poiString += pois[i] + } + 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 + } + var permTtl int + for i := range perm { + if i > 0 { + beg, end := string(perm[i-1]), string(perm[i]) + if beg[0] > end[0] { + beg, end = end, beg + } + permTtl += shortestPoiDist[beg+";"+end] + } + } + if permTtl < shortest || shortest == -1 { + shortestPerm = perm + shortest = permTtl + } + } + fmt.Println(shortestPerm, ": ", shortest) } // GetShortestPath just finds the shortest path between two points -func (m *Maze) GetShortestPath(beg *Coord, end *Coord, dist int) (int, bool) { - // TODO: actually solve for shortest path - return 0, false -} - -/* Put on hold -// We're going to find the closest POI (straight-line) and solve to it -func (m *Maze) Solve(x, y, dist int) bool { - wrkCoord := Coord{x: x, y: y, dist: dist} - _ = wrkCoord - if m.end == nil || m.end.is(x, y) { - // We made it (or haven't started)! Do we have any more pois? - if len(m.pois.coords) == 0 { - // Nope, none left, we're done - return true - } - m.end = m.FindClosestPoi() +func (m *Maze) GetShortestPath(begX, begY, endX, endY, dist int, path Path) (int, bool) { + if begX == endX && begY == endY { + return dist, true } - return false + 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 } -*/ func (m *Maze) FindClosestPoi() *Coord { var shortestPoi Coord