package day13 import ( "fmt" "log" "os" "strings" ) func Run() int { filename := "day13/example" fmt.Println("hello day 13.", filename) bytes, err := os.ReadFile(filename) if err != nil { panic(fmt.Sprintf("error reading file %s", filename)) } allText := string(bytes) fieldTexts := strings.Split(allText, "\n\n") result := 0 for _, fieldText := range fieldTexts { field := ReadField(fieldText) result += Calc(field) } return result } func Calc(field Field) int { verticals, horizontals := field.initMirrors() fmt.Println(field.String()) fmt.Printf("field width %d and height %d\n", len(field.Symbols[0]), len(field.Symbols)) for rowNum, row := range field.Symbols { for colNum, symb := range row { for _, horizontalMirrorUnderCheck := range horizontals { mirroredRow, shouldCheck := horizontalMirrorUnderCheck.reflectCoord(rowNum) if shouldCheck { // log.Printf("checking horizontal mirror %+v", horizontalMirrorUnderCheck) // log.Printf("in should check for row %d, col %d, mirrored row %d\n", rowNum, colNum, mirroredRow) mirroredSymb := field.Symbols[mirroredRow][colNum] isMirrored := symb == mirroredSymb if !isMirrored { // log.Printf("found not mirrored : %s != %s\n", string(symb), string(mirroredSymb)) horizontalMirrorUnderCheck.FailedLineChecks[rowNum] = struct{}{} } } } } // whole row got checked. // let's mark successful line check for all that didn't fail this line check for _, horizontalMirror := range horizontals { _, failedCheckReported := horizontalMirror.FailedLineChecks[rowNum] if !failedCheckReported { horizontalMirror.SuccessfulLineChecks[rowNum] = struct{}{} } } } rowsAboveHorizontals := 0 for _, mirr := range horizontals { fmt.Println(mirr.String()) if mirr.isFullMirror() { rowsAboveHorizontals += (mirr.Smaller + 1) } } for colNum, _ := range field.Symbols[0] { for rowNum, row := range field.Symbols { symb := row[colNum] for _, verticalMirrorUnderCheck := range verticals { mirroredCol, shouldCheck := verticalMirrorUnderCheck.reflectCoord(colNum) if shouldCheck { // log.Printf("checking vertical mirror %+v", horizontalMirrorUnderCheck) // log.Printf("in should check for row %d, col %d, mirrored row %d\n", rowNum, colNum, mirroredRow) mirroredSymb := field.Symbols[rowNum][mirroredCol] isMirrored := symb == mirroredSymb if !isMirrored { // log.Printf("found not mirrored : %s != %s\n", string(symb), string(mirroredSymb)) verticalMirrorUnderCheck.FailedLineChecks[rowNum] = struct{}{} } } } } // whole row got checked. // let's mark successful line check for all that didn't fail this line check for _, verticalMirror := range verticals { _, failedCheckReported := verticalMirror.FailedLineChecks[colNum] if !failedCheckReported { verticalMirror.SuccessfulLineChecks[colNum] = struct{}{} } } } colsToLeftOfHorizontals := 0 for _, mirr := range verticals { fmt.Println(mirr.String()) if mirr.isFullMirror() { colsToLeftOfHorizontals += (mirr.Smaller + 1) } } result := colsToLeftOfHorizontals + 100*rowsAboveHorizontals return result } type Field struct { Symbols [][]rune } func ReadField(fieldText string) Field { fieldText = strings.TrimSpace(fieldText) lines := strings.Split(fieldText, "\n") symbols := make([][]rune, len(lines)) for i, line := range lines { symbols[i] = []rune(line) } return Field{ Symbols: symbols, } } func (f *Field) String() string { text := "\n" for _, row := range f.Symbols { text += string(row) text += "\n" } return text } func (f *Field) initMirrors() (vertical []Mirror, horizontal []Mirror) { height := len(f.Symbols) width := len(f.Symbols[0]) amountHorizontal := height - 1 amountVertical := width - 1 horizontal = make([]Mirror, amountHorizontal) vertical = make([]Mirror, amountVertical) for rowNum := 0; rowNum < amountHorizontal; rowNum++ { maxDist := min(rowNum, height - 1 - (rowNum+1)) log.Println("maxDist ", maxDist, "for rowNum ", rowNum) horizontal[rowNum] = Mirror{ Smaller: rowNum, Bigger: rowNum + 1, SuccessfulLineChecks: make(map[int]any), FailedLineChecks: make(map[int]any), MaxDistToCheck: maxDist, } } for colNum := 0; colNum < amountVertical; colNum++ { maxDist := min(colNum, width - 1 - (colNum+1)) vertical[colNum] = Mirror{ Smaller: colNum, Bigger: colNum + 1, SuccessfulLineChecks: make(map[int]any), FailedLineChecks: make(map[int]any), MaxDistToCheck: min(colNum, maxDist), } } return } type Mirror struct { // located between lines Smaller, Bigger int MaxDistToCheck int // how many steps from mirrow have to be checked to confirm // i.e if mirror between 0 and 1 - only rows 0 & 1 have to be checked, row 2 is 'mirrored' outside of the field and 'ok' // value 0 means one step from 'mirror' so rows 0 and 1 SuccessfulLineChecks map[int]any FailedLineChecks map[int]any } func (m *Mirror)isFullMirror() bool { return len(m.FailedLineChecks) == 0 } func (m *Mirror)String() string { return fmt.Sprintf("Mirror between %d and %d. successful lines: %+v ; failed lines: %+v. Max check dist: %d\n", m.Smaller, m.Bigger, m.SuccessfulLineChecks, m.FailedLineChecks, m.MaxDistToCheck) } func (m *Mirror) reflectCoord(coord int) (reflected int, shouldCheck bool) { dist := m.Smaller - coord _, distFailed := m.FailedLineChecks[dist] _, distConfirmed := m.SuccessfulLineChecks[dist] if distFailed || distConfirmed { return 0, false // either line already fully confirmed, or failed. no need for additional checks } reflected = m.Bigger + dist if dist < 0 { dist = coord - m.Bigger reflected = m.Smaller - dist } shouldCheck = dist <= m.MaxDistToCheck return reflected, shouldCheck }