2023 Day 23 Complete!

This commit is contained in:
Brian Buller 2023-12-27 11:42:49 -06:00
parent ce40faf813
commit 109ce307e2
2 changed files with 174 additions and 113 deletions

View File

@ -2,138 +2,195 @@ package main
import ( import (
"fmt" "fmt"
"os"
h "git.bullercodeworks.com/brian/adventofcode/helpers" h "git.bullercodeworks.com/brian/adventofcode/helpers"
) )
func main() { func main() {
inp := h.StdinToStringSlice() inp := h.StdinToStringSlice()
if len(os.Args) == 1 || os.Args[1] != "-p2" { part1(inp)
part1(inp) fmt.Println()
} part2(inp)
if len(os.Args) == 1 {
fmt.Println()
}
if len(os.Args) == 1 || os.Args[1] != "-p1" {
part2(inp)
}
} }
var history map[string]int func part1(inp []string) {
func part1(input []string) {
history = make(map[string]int)
m := h.StringSliceToCoordByteMap(input)
start, _ := m.FindFirst('.')
start.Y++
end, _ := m.FindLast('.')
fmt.Println("# Part 1") fmt.Println("# Part 1")
fmt.Println(len(findPath(&m, start, end, []h.Coordinate{}))) start, end := h.Coordinate{}, h.Coordinate{}
start.X, start.Y = findStart(inp)
end.X, end.Y = findEnd(inp)
visited := map[h.Coordinate]int{start: 0}
currentDir := S
walk(inp, start, currentDir, visited)
fmt.Println(visited[end])
} }
func part2(input []string) { func part2(inp []string) {
history = make(map[string]int)
m := h.StringSliceToCoordByteMap(input)
start, _ := m.FindFirst('.')
start.Y++
end, _ := m.FindLast('.')
m.ReplaceAll('^', '.')
m.ReplaceAll('>', '.')
m.ReplaceAll('v', '.')
m.ReplaceAll('<', '.')
fmt.Println("# Part 2") fmt.Println("# Part 2")
fmt.Println(len(findPath(&m, start, end, []h.Coordinate{}))) start, end := h.Coordinate{}, h.Coordinate{}
start.X, start.Y = findStart(inp)
end.X, end.Y = findEnd(inp)
conns := getConnections(inp)
conns[start] = true
conns[end] = true
paths := getPaths(inp, conns)
visited := make([]bool, len(conns))
visited[paths[start][0].index] = true
fmt.Println(part2Walk(inp, paths, start, end, 0, visited))
} }
func findPath(m *h.CoordByteMap, pos h.Coordinate, end h.Coordinate, path []h.Coordinate) []h.Coordinate { var (
ret := []h.Coordinate{pos} N = h.Coordinate{X: 0, Y: -1}
if pos.Equals(end) { E = h.Coordinate{X: 1, Y: 0}
//printPath(m, pos, path) S = h.Coordinate{X: 0, Y: 1}
//time.Sleep(time.Second / 20) W = h.Coordinate{X: -1, Y: 0}
return ret dirs = []h.Coordinate{N, E, S, W}
badSlope = map[h.Coordinate]byte{
N: 'v', E: '<', S: '^', W: '>',
} }
)
path = append(path, pos) func getLeft(from h.Coordinate) h.Coordinate {
switch m.Get(pos) { switch {
case '^': case from.Equals(N):
if inPath(pos.North(), path) { return W
return ret case from.Equals(E):
} else { return N
return append(ret, findPath(m, pos.North(), end, path)...) case from.Equals(S):
} return E
case '>': default:
if inPath(pos.East(), path) { return S
return ret }
} else { }
return append(ret, findPath(m, pos.East(), end, path)...) func getRight(from h.Coordinate) h.Coordinate {
} switch {
case 'v': case from.Equals(N):
if inPath(pos.South(), path) { return E
return ret case from.Equals(E):
} else { return S
return append(ret, findPath(m, pos.South(), end, path)...) case from.Equals(S):
} return W
case '<': default:
if inPath(pos.West(), path) { return N
return ret }
} else { }
return append(ret, findPath(m, pos.West(), end, path)...)
func findStart(inp []string) (int, int) {
y := 0
for x := 0; x < len(inp[0]); x++ {
if inp[y][x] == '.' {
return x, y
} }
} }
return -1, -1
}
var longestPath []h.Coordinate func findEnd(inp []string) (int, int) {
longest := h.MIN_INT y := len(inp) - 1
for _, chk := range []h.Coordinate{pos.North(), pos.East(), pos.South(), pos.West()} { for x := 0; x < len(inp[0]); x++ {
if m.Get(chk) == '#' || inPath(chk, path) || !m.ContainsCoord(chk) { if inp[y][x] == '.' {
continue return x, y
} }
}
return -1, -1
}
var run bool func isValidCoord(inp []string, c h.Coordinate) bool {
var v int return c.Y >= 0 && c.Y < len(inp) && c.X >= 0 && c.X < len(inp[0])
if v, run = history[chk.String()]; run { }
run = v < len(path)+1
} func walk(inp []string, start, currDir h.Coordinate, visited map[h.Coordinate]int) {
if run { curr := start
history[chk.String()] = len(path) + 1 currStep := visited[curr]
chkPath := append(ret, findPath(m, chk, end, path)...)
if inPath(end, chkPath) && len(chkPath) > longest { for _, dir := range []h.Coordinate{currDir, getLeft(currDir), getRight(currDir)} {
longest = len(chkPath) next := h.Coordinate{X: (curr.X + dir.X), Y: (curr.Y + dir.Y)}
longestPath = chkPath if isValidCoord(inp, next) && inp[next.Y][next.X] != '#' {
bt := inp[next.Y][next.X]
if badSlope[dir] == bt {
continue
}
if val, found := visited[next]; !found || val < currStep+1 {
visited[next] = currStep + 1
walk(inp, next, dir, visited)
} }
} }
} }
return longestPath
} }
func inPath(pos h.Coordinate, path []h.Coordinate) bool { func getConnections(inp []string) map[h.Coordinate]bool {
for _, chk := range path { conns := map[h.Coordinate]bool{}
if chk.Equals(pos) { for y, line := range inp {
return true for x, bt := range line {
} if bt == '#' {
} continue
return false }
} pos := h.Coordinate{X: x, Y: y}
neighbors := 0
func printPath(m *h.CoordByteMap, pos h.Coordinate, path []h.Coordinate) { for _, nbr := range pos.GetOrthNeighbors() {
limit := (m.BRY - m.TLY) > 30 if isValidCoord(inp, nbr) && inp[nbr.Y][nbr.X] != '#' {
fmt.Print(h.CLEAR_SCREEN) neighbors++
for y := m.TLY; y <= m.BRY; y++ {
if limit && h.Abs(pos.Y-y) > 15 {
continue
}
for x := m.TLX; x <= m.BRX; x++ {
chk := h.Coordinate{X: x, Y: y}
if chk.Equals(pos) {
fmt.Print("@")
} else {
if inPath(chk, path) {
fmt.Print("O")
} else {
fmt.Print(string(m.Get(chk)))
} }
} }
if neighbors > 2 {
conns[pos] = true
}
} }
fmt.Println() }
return conns
}
type Path struct {
end h.Coordinate
length, index int
}
func getPaths(inp []string, conns map[h.Coordinate]bool) map[h.Coordinate][]Path {
paths := map[h.Coordinate][]Path{}
connIdx := 0
for c := range conns {
for _, d := range dirs {
curr := h.Coordinate{X: c.X + d.X, Y: c.Y + d.Y}
if isValidCoord(inp, curr) && inp[curr.Y][curr.X] != '#' {
path := getPath(inp, c, curr, d, 1, conns)
path.index = connIdx
paths[c] = append(paths[c], path)
}
}
connIdx++
}
return paths
}
func getPath(inp []string, start, curr, dir h.Coordinate, length int, conns map[h.Coordinate]bool) Path {
for _, dir := range []h.Coordinate{dir, getLeft(dir), getRight(dir)} {
next := h.Coordinate{X: curr.X + dir.X, Y: curr.Y + dir.Y}
if inp[next.Y][next.X] != '#' {
if _, found := conns[next]; found {
return Path{next, length + 1, 0}
} else {
return getPath(inp, start, next, dir, length+1, conns)
}
}
}
return Path{
end: h.Coordinate{X: -1, Y: -1},
length: 0, index: 0,
} }
} }
func part2Walk(inp []string, paths map[h.Coordinate][]Path, start, end h.Coordinate, step int, visited []bool) int {
var max int
for _, path := range paths[start] {
if path.end == end {
return step + path.length
}
index := paths[path.end][0].index
if !visited[index] {
visited[index] = true
max = h.Max(max, part2Walk(inp, paths, path.end, end, step+path.length, visited))
visited[index] = false
}
}
return max
}

View File

@ -1,10 +1,10 @@
package aoc package aoc
type Queue[T comparable] struct { type Queue[T any] struct {
items []T items []T
} }
func NewQueue[T comparable](items []T) *Queue[T] { func NewQueue[T any](items []T) *Queue[T] {
return &Queue[T]{ return &Queue[T]{
items: items, items: items,
} }
@ -46,14 +46,18 @@ func (q *Queue[T]) Cut(at, count int) []T {
q.items = append(q.items[:at], q.items[at+count:]...) q.items = append(q.items[:at], q.items[at+count:]...)
return ret return ret
} }
func (q *Queue[T]) Delete(item T) { func (q *Queue[T]) DeleteAt(idx int) {
for i := range q.items { q.items = append(q.items[:idx], q.items[idx+1:]...)
if q.items[i] == item {
q.items = append(q.items[:i], q.items[i+1:]...)
return
}
}
} }
// func (q *Queue[T]) Delete(item T) {
// for i := range q.items {
// if q.items[i] == item {
// q.items = append(q.items[:i], q.items[i+1:]...)
// return
// }
// }
// }
func (q *Queue[T]) Expand(at, by int) { func (q *Queue[T]) Expand(at, by int) {
q.items = append(q.items[:at], append(make([]T, by), q.items[at:]...)...) q.items = append(q.items[:at], append(make([]T, by), q.items[at:]...)...)
} }