advent-of-code/aoc2023/day11/main.go

198 lines
3.4 KiB
Go
Raw Permalink Normal View History

2023-12-12 13:44:28 -07:00
package main
import (
"bufio"
_ "embed"
"fmt"
"sort"
"strings"
2023-12-15 15:13:24 -07:00
aoc "go.sour.is/advent-of-code"
2023-12-12 13:44:28 -07:00
)
// 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 := NewMap()
for scan.Scan() {
text := scan.Text()
m.readLine(text)
}
return &result{
valuePT1: m.expand(1).sumPaths(),
valuePT2: m.expand(999_999).sumPaths(),
}, nil
}
type Map struct {
rows int
cols int
emptyRows map[int]bool
emptyCols map[int]bool
*aoc.List[rune]
}
func NewMap() *Map {
return &Map{
emptyRows: make(map[int]bool),
emptyCols: make(map[int]bool),
List: aoc.NewList[rune](nil),
}
}
func (m *Map) String() string {
buf := &strings.Builder{}
fmt.Fprintf(buf, "Map size %d x %d\n", m.rows, m.cols)
fmt.Fprintln(buf, "empty rows:", all(m.emptyRows))
fmt.Fprintln(buf, "empty cols:", all(m.emptyCols))
n := m.Head()
for n != nil {
fmt.Fprintln(buf, toXY(n.Position(), m.cols), n.String())
n = n.Next()
}
2023-12-13 08:32:37 -07:00
for row := 0; row < m.rows; row++ {
for col := 0; col < m.cols; col++ {
2023-12-12 13:44:28 -07:00
if n := m.getRC(row, col); n != nil {
buf.WriteRune('#')
} else {
buf.WriteRune('.')
}
}
buf.WriteRune('\n')
}
return buf.String()
}
func (m *Map) readLine(text string) {
if m.cols == 0 {
m.cols = len(text)
}
emptyRow, ok := m.emptyRows[m.rows]
if !ok {
emptyRow = true
}
row := []rune(text)
for col, r := range row {
emptyCol, ok := m.emptyCols[col]
if !ok {
emptyCol = true
}
if r == '#' {
m.Add(r, fromXY(col, m.rows, m.cols))
emptyCol = false
emptyRow = false
}
m.emptyRows[m.rows] = emptyRow
m.emptyCols[col] = emptyCol
}
m.rows++
}
func (m *Map) getRC(row, col int) *aoc.Node[rune] {
return m.List.Get(fromXY(col, row, m.cols))
}
func (m *Map) expand(rate int) *Map {
newM := NewMap()
newM.rows = m.rows + rate*len(all(m.emptyRows))
newM.cols = m.cols + rate*len(all(m.emptyCols))
offsetC := 0
for col := 0; col < m.cols; col++ {
if empty, ok := m.emptyCols[col]; ok && empty {
for r := 0; r <= rate; r++ {
newM.emptyCols[offsetC+col+r] = true
}
offsetC += rate
continue
}
}
offsetR := 0
for row := 0; row < m.rows; row++ {
if empty, ok := m.emptyRows[row]; ok && empty {
for r := 0; r <= rate; r++ {
newM.emptyRows[offsetR+row+r] = true
}
offsetR += rate
continue
}
offsetC := 0
for col := 0; col < m.cols; col++ {
if empty, ok := m.emptyCols[col]; ok && empty {
offsetC += rate
2023-12-13 08:32:37 -07:00
2023-12-12 13:44:28 -07:00
continue
}
2023-12-13 08:32:37 -07:00
if n := m.getRC(row, col); n != nil {
2023-12-12 13:44:28 -07:00
newM.Add('#', fromXY(offsetC+col, offsetR+row, newM.cols))
}
}
}
return newM
}
2023-12-13 08:32:37 -07:00
func (m *Map) sumPaths() int {
2023-12-12 13:44:28 -07:00
var positions []int
n := m.Head()
for n != nil {
positions = append(positions, n.Position())
n = n.Next()
}
var paths []int
for i := 0; i < len(positions); i++ {
2023-12-13 08:32:37 -07:00
p := positions[i]
2023-12-12 13:44:28 -07:00
pXY := toXY(p, m.cols)
2023-12-13 08:32:37 -07:00
2023-12-12 13:44:28 -07:00
for j := i; j < len(positions); j++ {
c := positions[j]
2023-12-13 08:32:37 -07:00
if c == p {
continue
}
2023-12-12 13:44:28 -07:00
2023-12-13 08:32:37 -07:00
cXY := toXY(c, m.cols)
2023-12-12 13:44:28 -07:00
2023-12-13 08:32:37 -07:00
path := aoc.ABS(cXY[0]-pXY[0]) + aoc.ABS(cXY[1]-pXY[1])
paths = append(paths, path)
2023-12-12 13:44:28 -07:00
}
}
2023-12-13 08:32:37 -07:00
return aoc.Sum(paths...)
2023-12-12 13:44:28 -07:00
}
func all(m map[int]bool) []int {
lis := make([]int, 0, len(m))
for k, v := range m {
if v {
lis = append(lis, k)
}
}
sort.Ints(lis)
return lis
}
func fromXY(x, y, w int) int { return y*w + x }
2023-12-15 15:13:24 -07:00
func toXY(i, w int) [2]int { return [2]int{i % w, i / w} }