Advent-of-Code-2023/day21/stepCounter.go

212 lines
5.0 KiB
Go

package day21
import (
"fmt"
"log"
"os"
"strings"
)
func Run() int {
fmt.Print("hello day21")
filename := "day21/input"
field := ReadField(filename)
log.Print(field)
// for i := 6; i <= 10; i++ {
// reachableBySteps := field.ReachableBySteps(i, map[Coord]any{
// Coord{Row: field.RowStart, Col: field.ColStart}: struct{}{},
// })
// log.Print("reachable after steps : ", i, len(reachableBySteps))
// field.PrintCoord(reachableBySteps, 1)
// }
// initialSolutions := make(map[int]int)
// for fullIter := 0; fullIter < 4; fullIter++ {
// steps := 65 + fullIter * 131
// reachableBySteps := field.ReachableBySteps(steps, map[FieldPoint]any{
// FieldPoint{
// InField: Coord{Row: field.RowStart, Col: field.ColStart},
// }: struct{}{},
// })
// log.Printf("after steps %d. full iter %d. got count %d", steps, fullIter, len(reachableBySteps))
// initialSolutions[fullIter] = len(reachableBySteps)
// }
log.Println("will try to use the values to get coeff of Ax^2 + Bx + C = 0")
log.Println("then solve for x == 202300")
// f(x) = 14714x^2 + 14603x + 3791
// no.
// 14669x^2 + 14738*x+3701
x := 202300
result := 14669*x*x + 14738*x+3701
return result
}
// let's do dijkstra?
// i would need lots of space for edges?
// let's use a map with minimal distances?
// OR. just breath first traversal
type Field struct {
RowStart, ColStart int
symbols [][]rune
}
type Coord struct {
Row, Col int
}
type FieldPoint struct {
InField Coord
MetaField Coord
}
func (f Field) ReachableBySteps(n int, startingAt map[FieldPoint]any) map[FieldPoint]any {
if n%100 == 0 {
log.Println("going step: ", n)
}
if n == 0 {
return startingAt
}
// else collect directly available
oneStepExpanded := make(map[FieldPoint]any)
for cur := range startingAt {
for _, neighbor := range f.Neighbors(cur) {
oneStepExpanded[neighbor] = struct{}{}
}
}
// if n < 4 {
// log.Print("reachable after steps : ", n, len(oneStepExpanded))
// f.PrintCoord(oneStepExpanded, 5)
// }
return f.ReachableBySteps(n-1, oneStepExpanded)
}
func (f Field) Neighbors(c FieldPoint) (resut []FieldPoint) {
closeCoords := []FieldPoint{
{InField: Coord{Row: c.InField.Row + 1, Col: c.InField.Col}, MetaField: c.MetaField},
{InField: Coord{Row: c.InField.Row - 1, Col: c.InField.Col}, MetaField: c.MetaField},
{InField: Coord{Row: c.InField.Row, Col: c.InField.Col + 1}, MetaField: c.MetaField},
{InField: Coord{Row: c.InField.Row, Col: c.InField.Col - 1}, MetaField: c.MetaField},
}
for i, close := range closeCoords {
height := len(f.symbols)
width := len(f.symbols[0])
if close.InField.Row == height {
close.InField.Row = 0
close.MetaField.Row += 1
}
if close.InField.Row == -1 {
close.InField.Row = height - 1
close.MetaField.Row -= 1
}
if close.InField.Col == width {
close.InField.Col = 0
close.MetaField.Col += 1
}
if close.InField.Col == -1 {
// log.Printf("moving COL to lefter field from %d to %d", close.Col, width-1)
close.InField.Col = width - 1
close.MetaField.Col -= 1
}
closeCoords[i] = close
// but this is not it. i need to store the XX and YY
// so that points in other 'fields' would count separately. yuk
}
for _, close := range closeCoords {
if f.ValidCoord(close.InField.Row, close.InField.Col) {
symb := f.symbols[close.InField.Row][close.InField.Col]
if symb == '.' || symb == 'S' {
resut = append(resut, close)
}
}
}
// log.Print("getting neighbors for ", c, resut)
return
}
func (f Field) ValidCoord(row, col int) bool {
// log.Print("check valid ", row, col, row >= 0 && row < len(f.symbols) && col >= 0 && col < len(f.symbols[0]))
valid := row >= 0 && row < len(f.symbols) && col >= 0 && col < len(f.symbols[0])
if !valid {
panic(fmt.Sprint("getting invalid coord: ", row, col))
}
return valid
}
func (f Field) String() (result string) {
result += "\n"
for _, line := range f.symbols {
result += string(line)
result += "\n"
}
return
}
func ReadField(filename string) (result Field) {
bytes, err := os.ReadFile(filename)
if err != nil {
panic(err)
}
text := strings.TrimSpace(string(bytes))
lines := strings.Split(text, "\n")
rows := make([][]rune, len(lines))
for rowNum, line := range lines {
rows[rowNum] = []rune(line)
for colNum, symb := range line {
if symb == 'S' {
result.RowStart = rowNum
result.ColStart = colNum
}
}
}
result.symbols = rows
return
}
func (f Field) PrintCoord(coords map[FieldPoint]any, expandByField int) {
for fieldRow := -expandByField; fieldRow <= expandByField; fieldRow++ {
lines := make([]string, len(f.symbols))
for fieldCol := -expandByField; fieldCol <= expandByField; fieldCol++ {
for rowNum, row := range f.symbols {
for colNum, col := range row {
_, marked := coords[FieldPoint{InField: Coord{Row: rowNum, Col: colNum},
MetaField: Coord{Row: fieldRow, Col: fieldCol}}]
if marked {
lines[rowNum] += "O"
} else {
lines[rowNum] += string(col)
}
}
}
}
for _, line := range lines {
fmt.Println(line)
}
}
return
}