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 (
"fmt"
"os"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
if len(os.Args) == 1 || os.Args[1] != "-p2" {
part1(inp)
}
if len(os.Args) == 1 {
fmt.Println()
}
if len(os.Args) == 1 || os.Args[1] != "-p1" {
part2(inp)
}
part1(inp)
fmt.Println()
part2(inp)
}
var history map[string]int
func part1(input []string) {
history = make(map[string]int)
m := h.StringSliceToCoordByteMap(input)
start, _ := m.FindFirst('.')
start.Y++
end, _ := m.FindLast('.')
func part1(inp []string) {
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) {
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('<', '.')
func part2(inp []string) {
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 {
ret := []h.Coordinate{pos}
if pos.Equals(end) {
//printPath(m, pos, path)
//time.Sleep(time.Second / 20)
return ret
var (
N = h.Coordinate{X: 0, Y: -1}
E = h.Coordinate{X: 1, Y: 0}
S = h.Coordinate{X: 0, Y: 1}
W = h.Coordinate{X: -1, Y: 0}
dirs = []h.Coordinate{N, E, S, W}
badSlope = map[h.Coordinate]byte{
N: 'v', E: '<', S: '^', W: '>',
}
)
path = append(path, pos)
switch m.Get(pos) {
case '^':
if inPath(pos.North(), path) {
return ret
} else {
return append(ret, findPath(m, pos.North(), end, path)...)
}
case '>':
if inPath(pos.East(), path) {
return ret
} else {
return append(ret, findPath(m, pos.East(), end, path)...)
}
case 'v':
if inPath(pos.South(), path) {
return ret
} else {
return append(ret, findPath(m, pos.South(), end, path)...)
}
case '<':
if inPath(pos.West(), path) {
return ret
} else {
return append(ret, findPath(m, pos.West(), end, path)...)
func getLeft(from h.Coordinate) h.Coordinate {
switch {
case from.Equals(N):
return W
case from.Equals(E):
return N
case from.Equals(S):
return E
default:
return S
}
}
func getRight(from h.Coordinate) h.Coordinate {
switch {
case from.Equals(N):
return E
case from.Equals(E):
return S
case from.Equals(S):
return W
default:
return N
}
}
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
longest := h.MIN_INT
for _, chk := range []h.Coordinate{pos.North(), pos.East(), pos.South(), pos.West()} {
if m.Get(chk) == '#' || inPath(chk, path) || !m.ContainsCoord(chk) {
continue
func findEnd(inp []string) (int, int) {
y := len(inp) - 1
for x := 0; x < len(inp[0]); x++ {
if inp[y][x] == '.' {
return x, y
}
}
return -1, -1
}
var run bool
var v int
if v, run = history[chk.String()]; run {
run = v < len(path)+1
}
if run {
history[chk.String()] = len(path) + 1
chkPath := append(ret, findPath(m, chk, end, path)...)
if inPath(end, chkPath) && len(chkPath) > longest {
longest = len(chkPath)
longestPath = chkPath
func isValidCoord(inp []string, c h.Coordinate) bool {
return c.Y >= 0 && c.Y < len(inp) && c.X >= 0 && c.X < len(inp[0])
}
func walk(inp []string, start, currDir h.Coordinate, visited map[h.Coordinate]int) {
curr := start
currStep := visited[curr]
for _, dir := range []h.Coordinate{currDir, getLeft(currDir), getRight(currDir)} {
next := h.Coordinate{X: (curr.X + dir.X), Y: (curr.Y + dir.Y)}
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 {
for _, chk := range path {
if chk.Equals(pos) {
return true
}
}
return false
}
func printPath(m *h.CoordByteMap, pos h.Coordinate, path []h.Coordinate) {
limit := (m.BRY - m.TLY) > 30
fmt.Print(h.CLEAR_SCREEN)
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)))
func getConnections(inp []string) map[h.Coordinate]bool {
conns := map[h.Coordinate]bool{}
for y, line := range inp {
for x, bt := range line {
if bt == '#' {
continue
}
pos := h.Coordinate{X: x, Y: y}
neighbors := 0
for _, nbr := range pos.GetOrthNeighbors() {
if isValidCoord(inp, nbr) && inp[nbr.Y][nbr.X] != '#' {
neighbors++
}
}
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
type Queue[T comparable] struct {
type Queue[T any] struct {
items []T
}
func NewQueue[T comparable](items []T) *Queue[T] {
func NewQueue[T any](items []T) *Queue[T] {
return &Queue[T]{
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:]...)
return ret
}
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]) DeleteAt(idx int) {
q.items = append(q.items[:idx], q.items[idx+1:]...)
}
// 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) {
q.items = append(q.items[:at], append(make([]T, by), q.items[at:]...)...)
}