2023-12-04 01:26:50 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"strconv"
|
|
|
|
"unicode"
|
2023-12-05 19:38:55 +00:00
|
|
|
|
|
|
|
"codeflow.dananglin.me.uk/apollo/advent-of-code/internal/common"
|
2023-12-04 01:26:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
dot = '.'
|
|
|
|
potentialGear = '*'
|
|
|
|
)
|
|
|
|
|
2023-12-04 13:10:03 +00:00
|
|
|
type solution struct {
|
2023-12-04 01:26:50 +00:00
|
|
|
sumOfValidEnginePartNumbers int
|
|
|
|
sumOfGearRatios int
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2023-12-05 19:38:55 +00:00
|
|
|
schematic, err := common.ReadFile("2023/day-3/files/input")
|
2023-12-04 01:26:50 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("ERROR: Unable to retrieve the engine schematic; %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
answer, err := fixTheGondola(schematic)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("ERROR: Unable to calculate the sum of all valid engine part numbers; %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("[Part 1] The sum of all valid engine part numbers: %d\n", answer.sumOfValidEnginePartNumbers)
|
|
|
|
fmt.Printf("[Part 2] The sum of all Gear Ratios: %d\n", answer.sumOfGearRatios)
|
|
|
|
}
|
|
|
|
|
2023-12-04 13:10:03 +00:00
|
|
|
func fixTheGondola(schematic []string) (solution, error) {
|
2023-12-04 01:26:50 +00:00
|
|
|
var (
|
|
|
|
partNumSum = 0
|
|
|
|
partNum = ""
|
|
|
|
validPartNum = false
|
|
|
|
maxY = len(schematic) - 1
|
|
|
|
maxX = len(schematic[0]) - 1
|
|
|
|
mapOfPotentialGears = make(map[string][]int)
|
|
|
|
potentialGearPos = ""
|
|
|
|
)
|
|
|
|
|
|
|
|
for y := range schematic {
|
|
|
|
for x, v := range schematic[y] {
|
|
|
|
if unicode.IsDigit(v) {
|
|
|
|
partNum = partNum + string(v)
|
|
|
|
if !validPartNum {
|
|
|
|
switch {
|
|
|
|
// check above left
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((y - 1) >= 0) && ((x - 1) >= 0) && isSymbol(rune(schematic[y-1][x-1])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y-1][x-1] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y-1, x-1)
|
|
|
|
}
|
|
|
|
// check straight above
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((y - 1) >= 0) && isSymbol(rune(schematic[y-1][x])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y-1][x] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y-1, x)
|
|
|
|
}
|
|
|
|
// check above right
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((y - 1) >= 0) && ((x + 1) <= maxX) && isSymbol(rune(schematic[y-1][x+1])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y-1][x+1] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y-1, x+1)
|
|
|
|
}
|
|
|
|
// check left
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((x - 1) >= 0) && isSymbol(rune(schematic[y][x-1])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y][x-1] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y, x-1)
|
|
|
|
}
|
|
|
|
// check right
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((x + 1) <= maxX) && isSymbol(rune(schematic[y][x+1])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y][x+1] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y, x+1)
|
|
|
|
}
|
|
|
|
// check below left
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((y + 1) <= maxY) && ((x - 1) >= 0) && isSymbol(rune(schematic[y+1][x-1])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y+1][x-1] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y+1, x-1)
|
|
|
|
}
|
|
|
|
// check straight below
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((y + 1) <= maxY) && isSymbol(rune(schematic[y+1][x])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y+1][x] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y+1, x)
|
|
|
|
}
|
|
|
|
// check below right
|
2023-12-04 13:10:03 +00:00
|
|
|
case ((y + 1) <= maxY) && ((x + 1) <= maxX) && isSymbol(rune(schematic[y+1][x+1])):
|
2023-12-04 01:26:50 +00:00
|
|
|
validPartNum = true
|
|
|
|
if schematic[y+1][x+1] == potentialGear {
|
|
|
|
potentialGearPos = fmt.Sprintf("%d-%d", y+1, x+1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if validPartNum {
|
|
|
|
value, err := strconv.Atoi(partNum)
|
|
|
|
if err != nil {
|
2023-12-04 13:10:03 +00:00
|
|
|
return solution{}, fmt.Errorf("unable to convert %q to int; %w", partNum, err)
|
2023-12-04 01:26:50 +00:00
|
|
|
}
|
|
|
|
partNumSum = partNumSum + value
|
|
|
|
|
|
|
|
if len(potentialGearPos) > 0 {
|
|
|
|
mapOfPotentialGears[potentialGearPos] = append(mapOfPotentialGears[potentialGearPos], value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
partNum = ""
|
|
|
|
validPartNum = false
|
|
|
|
potentialGearPos = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the sum of all gear ratios
|
|
|
|
sumGearRatios := 0
|
|
|
|
|
|
|
|
for _, v := range mapOfPotentialGears {
|
|
|
|
if len(v) == 2 {
|
|
|
|
sumGearRatios = sumGearRatios + (v[0] * v[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-04 13:10:03 +00:00
|
|
|
answer := solution{
|
2023-12-04 01:26:50 +00:00
|
|
|
sumOfValidEnginePartNumbers: partNumSum,
|
|
|
|
sumOfGearRatios: sumGearRatios,
|
|
|
|
}
|
|
|
|
|
2023-12-04 13:10:03 +00:00
|
|
|
return answer, nil
|
2023-12-04 01:26:50 +00:00
|
|
|
}
|
|
|
|
|
2023-12-04 13:10:03 +00:00
|
|
|
func isSymbol(r rune) bool {
|
|
|
|
return !unicode.IsDigit(r) && (r != dot)
|
|
|
|
}
|