advent-of-code/day10/main.go

243 lines
3.4 KiB
Go
Raw Normal View History

2023-12-10 11:53:26 -07:00
package main
import (
"bufio"
"fmt"
"strings"
aoc "go.sour.is/advent-of-code-2023"
)
var log = aoc.Log
func main() { aoc.MustResult(aoc.Runner(run)) }
type result struct {
valuePT1 int
valuePT2 int
}
func (r result) String() string { return fmt.Sprintf("%#v", r) }
func run(scan *bufio.Scanner) (*result, error) {
m := Path{s: -1}
for scan.Scan() {
text := scan.Text()
_ = text
m.readLine(text)
}
dist := m.buildPath()
return &result{valuePT1: dist}, nil
}
type node struct {
value rune
pos int
whence int8
left, right *node
}
func (n *node) add(a *node) *node {
if a == nil {
return n
}
n.left, a.right = a, n
return a
}
func (n *node) String() string {
return fmt.Sprintf("node %s from %s", string(n.value), enum(n.whence))
}
type Path struct {
m []rune
w int
s int
l int
n *node
}
func (m *Path) readLine(text string) {
if m.w == 0 {
m.w = len(text)
}
if m.s == -1 {
if i := strings.IndexRune(text, 'S'); i != -1 {
m.s = i + len(m.m)
}
}
m.m = append(m.m, []rune(text)...)
}
func (m *Path) buildPath() int {
m.start()
// log(m.n)
i := 0
for m.next() {
// log(m.n)
i++
}
// log(m.n)
// log("length", (m.l+2)/2)
return (m.l+2)/2
}
func (m *Path) start() {
m.n = &node{value: 'S', pos: m.s}
// log(m.n)
switch {
case m.peek(UP) != nil:
m.n = m.n.add(m.peek(UP))
case m.peek(DN) != nil:
m.n = m.n.add(m.peek(DN))
case m.peek(LF) != nil:
m.n = m.n.add(m.peek(LF))
case m.peek(RT) != nil:
m.n = m.n.add(m.peek(RT))
}
}
func (m *Path) next() bool {
var n *node
switch m.n.value {
case '7':
if m.n.whence == LF {
n = m.peek(DN)
} else {
n = m.peek(LF)
}
case '|':
if m.n.whence == UP {
n = m.peek(DN)
} else {
n = m.peek(UP)
}
case 'F':
if m.n.whence == RT {
n = m.peek(DN)
} else {
n = m.peek(RT)
}
case '-':
if m.n.whence == LF {
n = m.peek(RT)
} else {
n = m.peek(LF)
}
case 'J':
if m.n.whence == LF {
n = m.peek(UP)
} else {
n = m.peek(LF)
}
case 'L':
if m.n.whence == RT {
n = m.peek(UP)
} else {
n = m.peek(RT)
}
}
if n == nil {
return false
}
m.n = m.n.add(n)
m.l++
return m.n.value != 'S'
}
const (
ST int8 = iota
UP
DN
LF
RT
)
func enum(e int8) string {
switch e {
case ST:
return "ST"
case UP:
return "UP"
case DN:
return "DN"
case LF:
return "LF"
case RT:
return "RT"
default:
return "XX"
}
}
func (m *Path) peek(d int8) *node {
switch d {
case UP:
x, y := toXY(m.n.pos, m.w)
if y == 0 {
return nil
}
p := fromXY(x, y-1, m.w)
r := m.m[p]
if any(r, '7', '|', 'F') {
return &node{value: r, whence: DN, pos: p}
}
case DN:
x, y := toXY(m.n.pos, m.w)
if y == m.w {
return nil
}
p := fromXY(x, y+1, m.w)
r := m.m[p]
if any(r, 'J', '|', 'L') {
return &node{value: r, whence: UP, pos: p}
}
case LF:
x, y := toXY(m.n.pos, m.w)
if x == 0 {
return nil
}
p := fromXY(x-1, y, m.w)
r := m.m[p]
if any(r, 'F', '-', 'L') {
return &node{value: r, whence: RT, pos: p}
}
case RT:
x, y := toXY(m.n.pos, m.w)
if x == m.w {
return nil
}
p := fromXY(x+1, y, m.w)
r := m.m[p]
if any(r, '7', '-', 'J') {
return &node{value: r, whence: LF, pos: p}
}
}
return nil
}
func fromXY(x, y, w int) int { return y*w + x }
func toXY(i, w int) (int, int) {
return i % w, i / w
}
func any[T comparable](n T, stack ...T) bool {
var found bool
for _, s := range stack {
if n == s {
found = true
break
}
}
return found
}