package day14 import ( "fmt" "log" "os" "strings" ) func Run() int { fmt.Println("hello day 14") field := ReadPlatform("day14/input") fmt.Println(field.String()) // fmt.Printf("> lines for field %+v\n", field.UpIndices()) // field.Move(field.Height(), field.UpIndices()) cycles := 1000000000 states := make(map[string]int) // 2023/12/14 11:50:32 >>> found loop. known state after 10 equal to one after 3 var loopLen, initialStretch int for i := 1; i <= cycles; i++ { field.DoSpinCycle() // fmt.Println(field.String()) stringRepr := field.String() prevIter, known := states[stringRepr] if known { log.Printf(">>> found loop. known state after %d equal to one after %d", i, prevIter) initialStretch = prevIter loopLen = i - prevIter break } states[stringRepr] = i if i % 100000 == 0 { log.Print("done ", i, " cycles") } } // field is already in a 'loop' state. // so we've already done 'initial stretch' so to make field in same state as after 'cycles' // i only need to check rest of (cycles - initialStretch) movesToMake := (cycles - initialStretch)%loopLen log.Printf(">>> data: initial steps %d, loop len %d. to do same as %d iterations i need %d", initialStretch, loopLen, cycles, movesToMake) for i := 1; i <= movesToMake; i++ { field.DoSpinCycle() // fmt.Println(field.String()) } // north rock load return field.NorthLoad() } const Rock rune = 'O' const Wall rune = '#' const Space rune = '.' type Platform struct { Rocks [][]rune } func (p *Platform) Height() int { return len(p.Rocks) } func (p *Platform) Width() int { return len(p.Rocks[0]) } func ReadPlatform(filename string) Platform { bytes, err := os.ReadFile(filename) if err != nil { panic(fmt.Sprint("cannot read file: ", filename)) } text := string(bytes) text = strings.TrimSpace(text) lines := strings.Split(text, "\n") rocks := make([][]rune, len(lines)) for i, line := range lines { rocks[i] = []rune(line) } return Platform{ Rocks: rocks, } } func (p *Platform) String() string { text := "\n" for _, row := range p.Rocks { text += string(row) text += "\n" } return text } type Coord struct{ Row, Col int } // indices for moving UP, from down to up func (p *Platform) UpIndices() [][]Coord { lines := make([][]Coord, 0) for col := 0; col < p.Width(); col++ { line := make([]Coord, 0) for row := 0; row < p.Height(); row++ { line = append(line, Coord{Row: row, Col: col}) } lines = append(lines, line) } return lines } // indices for moving DOWN, from up to down func (p *Platform) DownIndices() [][]Coord { lines := make([][]Coord, 0) for col := 0; col < p.Width(); col++ { line := make([]Coord, 0) for row := p.Height() - 1; row >= 0; row-- { line = append(line, Coord{Row: row, Col: col}) } lines = append(lines, line) } return lines } // indices for moving RIGHT from right to left func (p *Platform) RightIndices() [][]Coord { lines := make([][]Coord, 0) for row := 0; row < p.Height(); row++ { line := make([]Coord, 0) for col := p.Width() - 1; col >= 0; col-- { line = append(line, Coord{Row: row, Col: col}) } lines = append(lines, line) } return lines } // indices for moving LEFT, from left to right func (p *Platform) LeftIndices() [][]Coord { lines := make([][]Coord, 0) for row := 0; row < p.Height(); row++ { line := make([]Coord, 0) for col := 0; col < p.Width(); col++ { line = append(line, Coord{Row: row, Col: col}) } lines = append(lines, line) } return lines } func (p *Platform) SymbAt(coord Coord) rune { return p.Rocks[coord.Row][coord.Col] } func (p *Platform) SetSymbAt(coord Coord, symb rune) { p.Rocks[coord.Row][coord.Col] = symb } func (p *Platform) Move(n int, lines [][]Coord) { for _, line := range lines { moveSize := 0 for i, coord := range line { symb := p.SymbAt(coord) switch symb { case Space: moveSize += 1 if moveSize > n { moveSize = n } case Wall: moveSize = 0 case Rock: if moveSize == 0 { continue } // get coord for moveSize back. and set that to 'o' // and set current to '.' // panic if that place is not '.' i guess moveTo := line[i-moveSize] symbAtTarget := p.SymbAt(moveTo) if symbAtTarget != Space { panic(fmt.Sprintf("attempting to move %+v to %+v, target symbol is %s, not '.'", coord, moveTo, string(symbAtTarget))) } p.SetSymbAt(moveTo, Rock) p.SetSymbAt(coord, Space) } } } } func (p *Platform) NorthLoad() int { total := 0 height := p.Height() for i, row := range p.Rocks { for _, symb := range row { if symb == Rock { total += (height - i) } } } return total } func (p *Platform) DoSpinCycle() { // north, west, south, east - till the end p.Move(p.Height(), p.UpIndices()) p.Move(p.Width(), p.LeftIndices()) p.Move(p.Height(), p.DownIndices()) p.Move(p.Width(), p.RightIndices()) }