214 lines
3.7 KiB
Go
214 lines
3.7 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
_ "embed"
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
"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 := NewMap()
|
||
|
|
||
|
for scan.Scan() {
|
||
|
text := scan.Text()
|
||
|
m.readLine(text)
|
||
|
}
|
||
|
// solution9 := m.expand(9).sumPaths()
|
||
|
// fmt.Println(solution9)
|
||
|
|
||
|
// solution99 := m.expand(99).sumPaths()
|
||
|
// fmt.Println(solution99)
|
||
|
|
||
|
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()
|
||
|
}
|
||
|
|
||
|
for row:=0; row<m.rows; row++ {
|
||
|
for col:=0; col<m.cols; col++ {
|
||
|
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
|
||
|
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if n := m.getRC(row, col); n!= nil {
|
||
|
newM.Add('#', fromXY(offsetC+col, offsetR+row, newM.cols))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return newM
|
||
|
}
|
||
|
func(m *Map) sumPaths() int {
|
||
|
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++ {
|
||
|
p:= positions[i]
|
||
|
pXY := toXY(p, m.cols)
|
||
|
|
||
|
for j := i; j < len(positions); j++ {
|
||
|
c := positions[j]
|
||
|
if c == p { continue }
|
||
|
|
||
|
cXY := toXY(c, m.cols)
|
||
|
|
||
|
path := abs(cXY[0]-pXY[0])+abs(cXY[1]-pXY[1])
|
||
|
paths = append(paths, path)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return sum(paths...)
|
||
|
}
|
||
|
|
||
|
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 }
|
||
|
func toXY(i, w int) []int { return []int{i % w, i / w} }
|
||
|
func abs(i int) int {
|
||
|
if i < 0 {
|
||
|
return -i
|
||
|
}
|
||
|
return i
|
||
|
}
|
||
|
func sum(arr ...int) int {
|
||
|
acc :=0
|
||
|
for _, a := range arr {
|
||
|
acc += a
|
||
|
}
|
||
|
return acc
|
||
|
}
|