243 lines
5.1 KiB
Go
243 lines
5.1 KiB
Go
package day9
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func Run() int {
|
|
log.Println("hello day 9")
|
|
filename := "day9/input"
|
|
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
|
|
}
|
|
|
|
func Run2() int {
|
|
log.Println("hello day 9, part2")
|
|
filename := "day9/input"
|
|
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)
|
|
result += seq.Get(-1)
|
|
}
|
|
|
|
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)
|
|
|
|
var calcFunc func() int
|
|
|
|
if col > 0 {
|
|
calcFunc = 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
|
|
}
|
|
} else {
|
|
// for part 2, propagate backward from known elements on the right
|
|
calcFunc = 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})
|
|
nextCell := s.GetAny(Coord{row, col + 1})
|
|
value := nextCell.Value() - simplerCell.Value()
|
|
cell.IsCached = true
|
|
cell.SavedValue = value
|
|
return value
|
|
}
|
|
}
|
|
|
|
cell = Cell{
|
|
IsCached: false,
|
|
Value: calcFunc,
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|