package day21 import ( "fmt" "log" "os" "strings" ) func Run() int { fmt.Print("hello day21") filename := "day21/example" 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) // } steps := 1000 reachableBySteps := field.ReachableBySteps(steps, map[Coord]any{ Coord{Row: field.RowStart, Col: field.ColStart}: struct{}{}, }) log.Print("reachable after steps : ", steps, len(reachableBySteps)) field.PrintCoord(reachableBySteps, 1) return len(reachableBySteps) } // 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 FieldRow, FieldCol int // 0 by default is good } func (f Field) ReachableBySteps(n int, startingAt map[Coord]any) map[Coord]any { if n % 100 == 0 { log.Println("going step: ", n) } if n == 0 { return startingAt } // else collect directly available oneStepExpanded := make(map[Coord]any) for cur := range startingAt { for _, neighbor := range f.Neighbors(cur) { oneStepExpanded[neighbor] = struct{}{} } } return f.ReachableBySteps(n-1, oneStepExpanded) } func (f Field) Neighbors(c Coord) (resut []Coord) { closeCoords := []Coord{ {Row: c.Row + 1, Col: c.Col, FieldRow: c.FieldRow, FieldCol: c.FieldCol}, {Row: c.Row - 1, Col: c.Col, FieldRow: c.FieldRow, FieldCol: c.FieldCol}, {Row: c.Row, Col: c.Col + 1, FieldRow: c.FieldRow, FieldCol: c.FieldCol}, {Row: c.Row, Col: c.Col - 1, FieldRow: c.FieldRow, FieldCol: c.FieldCol}, } for i, close := range closeCoords { height := len(f.symbols) width := len(f.symbols[0]) if close.Row == height { close.Row = 0 close.FieldRow += 1 } if close.Row == -1 { close.Row = height - 1 close.FieldRow -= 1 } if close.Col == width { close.Col = 0 close.FieldCol += 1 } if close.Col == -1 { // log.Printf("moving COL to lefter field from %d to %d", close.Col, width-1) close.Col = width - 1 close.FieldCol -= 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.Row, close.Col) { symb := f.symbols[close.Row][close.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[Coord]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[Coord{Row: rowNum, Col: colNum, FieldRow: fieldRow, FieldCol: fieldCol}] if marked { lines[rowNum] += "O" } else { lines[rowNum] += string(col) } } } } for _, line := range lines { fmt.Println(line) } } return }