2023 Day 23 Complete!
This commit is contained in:
parent
ce40faf813
commit
109ce307e2
@ -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
|
||||
}
|
||||
|
@ -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:]...)...)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user