package day13 import ( "fmt" "log" "os" "strings" ) func Run() int { filename := "day13/input" 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 { // if horizontalMirrorUnderCheck.Smaller != 4 { // continue // } mirroredRow, shouldCheck := horizontalMirrorUnderCheck.reflectCoord(rowNum) // log.Println("for mirror", horizontalMirrorUnderCheck.String()) // log.Printf("> checking row %d and mirrored %d; should %t\n", rowNum, mirroredRow, shouldCheck) 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] += 1 } } } } // 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("horizontal: ", mirr.String()) if mirr.isFullMirror() { log.Printf(">> found perfect Horizontal %+v\n", mirr) rowsAboveHorizontals += (mirr.Smaller + 1) } } for colNum, _ := range field.Symbols[0] { for rowNum, row := range field.Symbols { symb := row[colNum] for _, verticalMirrorUnderCheck := range verticals { // if verticalMirrorUnderCheck.Smaller != 8 { // continue // } mirroredCol, shouldCheck := verticalMirrorUnderCheck.reflectCoord(colNum) if shouldCheck { // log.Printf("checking vertical mirror %+v", verticalMirrorUnderCheck) // log.Printf("in should check for row %d, col %d, mirrored col %d\n", rowNum, colNum, mirroredCol) mirroredSymb := field.Symbols[rowNum][mirroredCol] isMirrored := symb == mirroredSymb if !isMirrored { // log.Printf("found not mirrored : %s != %s\n", string(symb), string(mirroredSymb)) verticalMirrorUnderCheck.FailedLineChecks[colNum] += 1 } } } } // 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("vertical: ", mirr.String()) if mirr.isFullMirror() { log.Printf(">> found perfect Vertical %+v\n", mirr) 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]int), 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]int), 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]int // from line num, to amount of errors in that line } func (m *Mirror)isFullMirror() bool { correctFailedLinesCount := len(m.FailedLineChecks) == 2 if correctFailedLinesCount { for failedLine, failedSymbols := range m.FailedLineChecks { reflectedLine, _ := m.reflectCoord(failedLine) doublyReflected, _ := m.reflectCoord(reflectedLine) // log.Printf(">>>> checking failed line %d, reflected is %d; doubly %d. amount failed is %d\n", failedLine, reflectedLine, doublyReflected, failedSymbols) if failedSymbols == 1 && (doublyReflected == failedLine) { return true } } } return false } func (m *Mirror)String() string { return fmt.Sprintf("Mirror (full %t) between %d and %d. successful lines: %+v ; failed lines: %+v. Max check dist: %d\n", m.isFullMirror(), m.Smaller, m.Bigger, m.SuccessfulLineChecks, m.FailedLineChecks, m.MaxDistToCheck) } func (m *Mirror) reflectCoord(coord int) (reflected int, shouldCheck bool) { dist := m.Smaller - coord // _, distConfirmed := m.SuccessfulLineChecks[dist] // if distConfirmed { // // log.Printf("> getting dist confirmed for coord %d ; dist %d\n", coord, dist) // 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 }