Advent-of-Code-2023/day13/dayThirteen.go

227 lines
6.8 KiB
Go

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
}