adventofcode/2023/day10/main.go

169 lines
3.6 KiB
Go

package main
import (
"fmt"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
m, l := part1(inp)
fmt.Println()
part2(m, l)
}
const (
NS = '|'
EW = '-'
NE = 'L'
NW = 'J'
SW = '7'
SE = 'F'
START = 'S'
)
var pipes = []byte{NS, EW, NE, NW, SW, SE}
func part1(input []string) (h.CoordByteMap, []h.Coordinate) {
m := h.StringSliceToCoordByteMap(input)
_, loop := findLoop(&m)
fmt.Println("# Part 1")
fmt.Println(len(loop) / 2)
return m, loop
}
func part2(m h.CoordByteMap, loop []h.Coordinate) {
var insideCoords []h.Coordinate
var inside bool
var enclosed int
for y := m.TLY; y <= m.BRY; y++ {
inside = false
var rowPathStart byte
for x := m.TLX; x <= m.BRX; x++ {
wrk := h.Coordinate{X: x, Y: y}
wrkV := m.Get(wrk)
if h.CoordListContains(wrk, loop) { //&& (wrkV == NS || wrkV == NE || wrkV == NW) {
// This coordinate is part of the loop, so it can change the 'inside' flag
// A corner _only_ changes the flag if the 'exiting' corner continues in the same direction
// i.e.: L--J doesn't change the flag, L--7 does change the flag
switch wrkV {
case NE, SE:
rowPathStart = wrkV
case NW, SW:
if continues(rowPathStart, wrkV) {
inside = !inside
}
}
if wrkV == NS {
// a | always changes the flag
inside = !inside
}
} else {
if inside {
enclosed++
insideCoords = append(insideCoords, wrk)
}
}
}
}
fmt.Println("# Part 2")
fmt.Println(enclosed)
for _, insides := range insideCoords {
m.Put(insides, 'I')
}
}
// continues checks if two _corner_ pieces continue the flow in the same direction
func continues(p1, p2 byte) bool {
switch p1 {
case NE:
return p2 == SW
case NW:
return p2 == SE
case SW:
return p2 == NE
case SE:
return p2 == NW
}
return false
}
func connectsNorth(v byte) bool {
return v == START || v == NS || v == NE || v == NW
}
func connectsSouth(v byte) bool {
return v == START || v == NS || v == SE || v == SW
}
func connectsEast(v byte) bool {
return v == START || v == EW || v == NE || v == SE
}
func connectsWest(v byte) bool {
return v == START || v == EW || v == NW || v == SW
}
func connects(m *h.CoordByteMap, p1, p2 h.Coordinate) bool {
p1v, p2v := m.Get(p1), m.Get(p2)
switch {
case p1.North().Equals(p2):
return connectsNorth(p1v) && connectsSouth(p2v)
case p1.East().Equals(p2):
return connectsEast(p1v) && connectsWest(p2v)
case p1.South().Equals(p2):
return connectsSouth(p1v) && connectsNorth(p2v)
case p1.West().Equals(p2):
return connectsWest(p1v) && connectsEast(p2v)
}
return false
}
func countConnections(m *h.CoordByteMap, p h.Coordinate) int {
var res int
for _, tst := range []h.Coordinate{p.North(), p.East(), p.South(), p.West()} {
if connects(m, p, tst) {
res++
}
}
return res
}
// Find loop finds all coordinates on the loop and returns:
// 1. The starting coordinate
// 2. All coordinates on the loop
// It also replaces the 'S' for the starting coordinate with the appropriate
// pipe byte
func findLoop(m *h.CoordByteMap) (h.Coordinate, []h.Coordinate) {
start, _ := m.FindFirst(START)
next := start
path := []h.Coordinate{start}
// Figure out the starting pipe byte
for _, v := range pipes {
m.Put(start, v)
if countConnections(m, start) == 2 {
break
}
}
for {
for _, tst := range []h.Coordinate{
next.North(),
next.East(),
next.South(),
next.West(),
} {
if !connects(m, next, tst) || (!tst.Equals(start) && h.CoordListContains(tst, path)) {
continue
}
next = tst
}
if next.Equals(start) {
// We're done
return start, path
} else {
path = append(path, next)
}
}
}