Advent-of-Code-2023/day9/dayNine.go

204 lines
4.3 KiB
Go

package day9
import (
"fmt"
"log"
"os"
"strconv"
"strings"
)
func Run() int {
log.Println("hello day 9")
filename := "day9/example"
bytes, err := os.ReadFile(filename)
if err != nil {
panic(fmt.Sprintln("error reading file: ", err))
}
result := 0
for _, line := range strings.Split(string(bytes), "\n") {
seq := CreateSequence(line)
next := seq.Next()
result += next
}
return result
}
type Cell struct {
IsCached bool
Value func() int
SavedValue int
}
func (c Cell) String() string {
if c.IsCached {
return fmt.Sprint(c.SavedValue)
}
return "*"
}
func CachedCell(val int) Cell {
valFunction := func() int {
return val
}
cell := Cell{
IsCached: true,
Value: valFunction,
SavedValue: val,
}
return cell
}
// so top row is 0. botton row is unknown before pre-processing
type Coord struct {
Row, Col int
}
func (c Coord)Equal(other Coord) bool {
return c.Col == other.Col && c.Row == other.Row
}
type Sequence struct {
values map[Coord]Cell
lastKnownRow int
lastReadTop int
zeroRowIndex int
constRowVal int
}
func (s *Sequence)Next() int {
log.Printf(">>>> trying to get next. current last known top %d\n", s.lastReadTop)
result := s.Get(s.lastReadTop + 1)
s.lastReadTop += 1
return result
}
func (s *Sequence) String() string {
lines := ""
for i := 0; i < s.lastKnownRow-1; i++ {
// log.Printf("converting row %d to str\n", i)
line := ""
j := 0
cell := s.GetAny(Coord{i, j})
for cell.IsCached {
// log.Printf("looking at cell %+v coord %d %d", cell, i, j)
line += fmt.Sprintf("%s\t", cell)
j += 1
cell = s.GetAny(Coord{i, j})
}
lines += line
lines += "\n"
}
lines += fmt.Sprintf("(%d)", s.constRowVal)
return lines
}
// TODO if zeroRowIndex == -1 would be better to return error, you know
func (s *Sequence) GetAny(coord Coord) Cell {
// time.Sleep(time.Millisecond * 300)
cell, found := s.values[coord]
row, col := coord.Row, coord.Col
if !found {
if row == (s.zeroRowIndex - 1) {
log.Printf("new visit to Const\n")
return CachedCell(s.constRowVal)
}
log.Printf("creating new uncashed at %d %d\n", row, col)
cell = Cell{
IsCached: false,
Value: func() int {
if cell.IsCached {
return cell.SavedValue
}
log.Printf("calc value for %d %d cell\n", row, col)
simplerCell := s.GetAny(Coord{row + 1, col - 1})
prevCell := s.GetAny(Coord{row, col - 1})
value := simplerCell.Value() + prevCell.Value()
cell.IsCached = true
cell.SavedValue = value
return value
},
}
}
log.Printf("in seq GetAny for %+v, got %+v", coord, cell)
return cell
}
func (s *Sequence) Get(n int) int {
nCell := s.GetAny(Coord{0, n})
return nCell.Value()
}
func CreateSequence(line string) Sequence {
seq := ReadTopRow(line)
SetupLowerRows(&seq)
return seq
}
func ReadTopRow(line string) Sequence {
cells := make(map[Coord]Cell)
maxRead := 0
for i, numStr := range strings.Fields(line) {
num, err := strconv.Atoi(numStr)
if err != nil {
panic(fmt.Sprintf("error reading top row %s, value %s to int\n", line, numStr))
}
cell := CachedCell(num)
cells[Coord{0, i}] = cell
maxRead = i
}
return Sequence{
values: cells,
zeroRowIndex: -1,
lastReadTop: maxRead,
}
}
func SetupLowerRows(s *Sequence) {
// need to get 'zeroValRowIndex'
// need to get 'constRowValue'
// and maybe don't need to get all of the intermediate? only enough to determine this
// after having top row, go down to row of all zeroes
// well actually row with just 2 zeroes one after another should be it
// so? how just iterate
foundZeroRow := false
zeroRow := -1
for i := 1; !foundZeroRow; i++ {
// log.Printf("attempting to calc row %d\n", i)
allZeroes := true
inRowCalculation:
for j := 0; true; j++ {
log.Printf("in row %d for index %d\n", i, j)
topRight := s.GetAny(Coord{i-1, j+1})
topLeft := s.GetAny(Coord{i-1, j})
if !topRight.IsCached || !topLeft.IsCached {
break inRowCalculation
}
calculatedValue := topRight.SavedValue - topLeft.SavedValue
cell := CachedCell(calculatedValue)
if calculatedValue != 0 {
allZeroes = false
}
s.values[Coord{i,j}] = cell
}
if allZeroes {
foundZeroRow = true
zeroRow = i
}
}
s.zeroRowIndex = zeroRow
s.lastKnownRow = zeroRow
s.constRowVal = s.GetAny(Coord{zeroRow-1, 0}).SavedValue
}