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 }