master
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}