master
Raw Download raw file
  1package main
  2
  3import (
  4	"advent2023/internal/aoc"
  5	"advent2023/internal/grid"
  6
  7	"strconv"
  8	"strings"
  9	"unicode"
 10
 11	"github.com/google/uuid"
 12)
 13
 14
 15type board struct {
 16	grid     map[grid.Coord]uuid.UUID
 17	parts    map[uuid.UUID]part
 18	included map[uuid.UUID]part
 19	symbols  []symbol
 20	gears    []gear
 21}
 22
 23type part struct {
 24	id     uuid.UUID
 25	number int
 26}
 27
 28type symbol struct {
 29	grid.Coord
 30	repr string
 31}
 32
 33type gear struct {
 34	symbol
 35	ratio int
 36}
 37
 38
 39func parseBoard(input []string) (b board) {
 40	b = board{
 41		grid:     map[grid.Coord]uuid.UUID{},
 42		parts:    map[uuid.UUID]part{},
 43		included: map[uuid.UUID]part{},
 44		symbols:  []symbol{},
 45		gears:    []gear{},
 46	}
 47
 48	for y, row := range input {
 49		var partSB strings.Builder
 50		partID := uuid.New()
 51		for x, col := range row {
 52
 53			if unicode.IsDigit(col) {
 54				partSB.WriteRune(col)
 55				b.grid[grid.Coord{X:x, Y:y}] = partID
 56				// only continue if not at end of row
 57				if x < len(row)-1 {
 58					continue
 59				}
 60			}
 61
 62			// non-digit or end or row after digits parsed
 63			// part number completed, store it
 64			if partSB.Len() > 0 {
 65				partNumber, _ := strconv.Atoi(partSB.String())
 66				b.parts[partID] = part{id: partID, number: partNumber}
 67				partID = uuid.New()
 68				partSB.Reset()
 69				if x == len(row)-1 {
 70					continue
 71				}
 72			}
 73
 74			if col == '.' {
 75				continue
 76			}
 77
 78			// symbol remaning, store it
 79			s := symbol{
 80				repr: string(col), 
 81				Coord: grid.Coord{X:x, Y:y},
 82			}
 83			b.symbols = append(b.symbols, s)
 84		}
 85	}
 86
 87	// find included numbers via symbols
 88	// find gears via "*" symbols
 89	for _, s := range b.symbols {
 90		neighbors := map[uuid.UUID]part{}
 91		for _, c := range s.Coord.Neighbors() {
 92			if id, ok := b.grid[c]; ok {
 93				b.included[id] = b.parts[id]
 94				neighbors[id] = b.parts[id]
 95			}
 96		}
 97		if s.repr == "*" && len(neighbors) == 2 {
 98			ratio := 1
 99			for _, p := range neighbors {
100				ratio = ratio * p.number
101			}
102			b.gears = append(b.gears, gear{
103				symbol: s,
104				ratio:  ratio,
105			})
106		}
107	}
108	return b
109}
110
111func solvePart1(input []string) (solution int) {
112	b := parseBoard(input)
113	solution = 0
114	for _, p := range b.included {
115		solution += p.number
116	}
117	return solution
118}
119
120func solvePart2(input []string) (solution int) {
121	b := parseBoard(input)
122	solution = 0
123	for _, s := range b.gears {
124		solution += s.ratio
125	}
126	return solution
127}
128
129func main() {
130	day := aoc.Day3
131	aoc.SolveExample(day, aoc.Part1, solvePart1, 4361)
132	aoc.SolvePuzzle(day, aoc.Part1, solvePart1, 533784)
133	aoc.SolveExample(day, aoc.Part2, solvePart2, 467835)
134	aoc.SolvePuzzle(day, aoc.Part2, solvePart2, 78826761)
135}