From b4c1118e88a5585570ed25693b90f57821c8123b Mon Sep 17 00:00:00 2001 From: Christopher De Vries Date: Sun, 3 Dec 2023 07:40:52 -0500 Subject: [PATCH] Added day 3 --- README.md | 15 ++++++- day03p1/solution.go | 84 ++++++++++++++++++++++++++++++++++++ day03p1/solution_test.go | 42 ++++++++++++++++++ day03p2/solution.go | 92 ++++++++++++++++++++++++++++++++++++++++ day03p2/solution_test.go | 42 ++++++++++++++++++ inputs | 2 +- 6 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 day03p1/solution.go create mode 100644 day03p1/solution_test.go create mode 100644 day03p2/solution.go create mode 100644 day03p2/solution_test.go diff --git a/README.md b/README.md index da57d6d..5c9081f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Advent of Code 2023 [![Tests](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml/badge.svg)](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml) -[![Stars: 4](https://img.shields.io/badge/⭐_Stars-4-yellow)](https://adventofcode.com/2023) +[![Stars: 6](https://img.shields.io/badge/⭐_Stars-6-yellow)](https://adventofcode.com/2023) ## Plan for This Year @@ -38,10 +38,21 @@ opportunity to experiment a bit with code generation. have some indexing issues which I had to debug. I later realized I could use `strings.LastIndex` to make this much easier. -- [Day 2:Cube Conundrum ](https://adventofcode.com/2023/day/2) - [part 1](day02p1/solution.go), [part 2](day02p2/solution.go) +- [Day 2: Cube Conundrum ](https://adventofcode.com/2023/day/2) - [part 1](day02p1/solution.go), [part 2](day02p2/solution.go) Most of this problem was parsing. I just did a lot of splitting on substrings. First I split on ": " to separate the game id from the draws, then I split on "; " to separate the individual draws, then on ", " to split to the colors. After that it was straightforward. I missed an opportunity to use the debugger to check my parsing and instead put in a `Println`. + +- [Day 3: Gear Ratios ](https://adventofcode.com/2023/day/3) - [part 1](day03p1/solution.go), [part 2](day03p2/solution.go) + + The key here was just figuring out how to store the schematic data so it would + be useful for answering the questions. During parsing I recorded the positions + of the symbols in a map indexed by position. I recorded the numbers as an + array of structs including the value and the start and end position. Then I + could interate over all the numbers, find the surrounding points, and see what + symbols were around them. I created an array of numbers for each gear object + I found and then could iterate through those arrays to find gears with + exactly two adjacent numbers. diff --git a/day03p1/solution.go b/day03p1/solution.go new file mode 100644 index 0000000..f25481f --- /dev/null +++ b/day03p1/solution.go @@ -0,0 +1,84 @@ +package day03p1 + +import ( + "io" + + "aoc/utils" +) + +func Solve(r io.Reader) any { + lines := utils.ReadLines(r) + + symbols := make(map[utils.Point]rune) + + numbers := []Number{} + + for j, ln := range lines { + innumber := false + + for i, r := range ln { + switch { + case r == '.': + // do nothing + innumber = false + case r >= '0' && r <= '9': + // number + switch innumber { + case false: + innumber = true + var newNumber Number + newNumber.Start = utils.Point{X: i, Y: j} + newNumber.End = newNumber.Start + newNumber.Value = int64(r - '0') + numbers = append(numbers, newNumber) + case true: + idx := len(numbers) - 1 + numbers[idx].End = utils.Point{X: i, Y: j} + numbers[idx].Value = numbers[idx].Value*10 + int64(r-'0') + } + default: + // symbol + symbols[utils.Point{X: i, Y: j}] = r + innumber = false + } + } + } + + var sum int64 + + for _, n := range numbers { + surroundingPoints := pointsAround(n.Start, n.End) + for _, p := range surroundingPoints { + if symbols[p] != 0 { + sum += n.Value + break + } + } + } + + return sum +} + +type Number struct { + Value int64 + Start utils.Point + End utils.Point +} + +func pointsAround(start utils.Point, end utils.Point) []utils.Point { + yupper := start.Y - 1 + ylower := start.Y + 1 + yline := start.Y + + xmin := start.X - 1 + xmax := end.X + 1 + + result := []utils.Point{{X: xmin, Y: yline}, {X: xmax, Y: yline}} + + for i := xmin; i <= xmax; i++ { + result = append(result, utils.Point{X: i, Y: yupper}) + result = append(result, utils.Point{X: i, Y: ylower}) + } + + return result +} diff --git a/day03p1/solution_test.go b/day03p1/solution_test.go new file mode 100644 index 0000000..12d3dae --- /dev/null +++ b/day03p1/solution_test.go @@ -0,0 +1,42 @@ +package day03p1 + +import ( + "strings" + "testing" + + "aoc/utils" +) + +var testInput = `467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598..` + +func TestSolve(t *testing.T) { + tests := []struct { + input string + answer int64 + }{ + {testInput, 4361}, + } + + if testing.Verbose() { + utils.Verbose = true + } + + for _, test := range tests { + r := strings.NewReader(test.input) + + result := Solve(r).(int64) + + if result != test.answer { + t.Errorf("Expected %d, got %d", test.answer, result) + } + } +} diff --git a/day03p2/solution.go b/day03p2/solution.go new file mode 100644 index 0000000..d00373e --- /dev/null +++ b/day03p2/solution.go @@ -0,0 +1,92 @@ +package day03p2 + +import ( + "io" + + "aoc/utils" +) + +func Solve(r io.Reader) any { + lines := utils.ReadLines(r) + + symbols := make(map[utils.Point]rune) + + numbers := []Number{} + + for j, ln := range lines { + innumber := false + + for i, r := range ln { + switch { + case r == '.': + // do nothing + innumber = false + case r >= '0' && r <= '9': + // number + switch innumber { + case false: + innumber = true + var newNumber Number + newNumber.Start = utils.Point{X: i, Y: j} + newNumber.End = newNumber.Start + newNumber.Value = int64(r - '0') + numbers = append(numbers, newNumber) + case true: + idx := len(numbers) - 1 + numbers[idx].End = utils.Point{X: i, Y: j} + numbers[idx].Value = numbers[idx].Value*10 + int64(r-'0') + } + default: + // symbol + symbols[utils.Point{X: i, Y: j}] = r + innumber = false + } + } + } + + var sum int64 + + gearAdjacents := make(map[utils.Point][]int64) + + for _, n := range numbers { + surroundingPoints := pointsAround(n.Start, n.End) + for _, p := range surroundingPoints { + if symbols[p] == '*' { + gearAdjacents[p] = append(gearAdjacents[p], n.Value) + break + } + } + } + + for _, nums := range gearAdjacents { + if len(nums) == 2 { + sum += nums[0] * nums[1] + } + } + + return sum +} + +type Number struct { + Value int64 + Start utils.Point + End utils.Point +} + +func pointsAround(start utils.Point, end utils.Point) []utils.Point { + yupper := start.Y - 1 + ylower := start.Y + 1 + yline := start.Y + + xmin := start.X - 1 + xmax := end.X + 1 + + result := []utils.Point{{X: xmin, Y: yline}, {X: xmax, Y: yline}} + + for i := xmin; i <= xmax; i++ { + result = append(result, utils.Point{X: i, Y: yupper}) + result = append(result, utils.Point{X: i, Y: ylower}) + } + + return result +} diff --git a/day03p2/solution_test.go b/day03p2/solution_test.go new file mode 100644 index 0000000..e9944d1 --- /dev/null +++ b/day03p2/solution_test.go @@ -0,0 +1,42 @@ +package day03p2 + +import ( + "strings" + "testing" + + "aoc/utils" +) + +var testInput = `467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598..` + +func TestSolve(t *testing.T) { + tests := []struct { + input string + answer int64 + }{ + {testInput, 467835}, + } + + if testing.Verbose() { + utils.Verbose = true + } + + for _, test := range tests { + r := strings.NewReader(test.input) + + result := Solve(r).(int64) + + if result != test.answer { + t.Errorf("Expected %d, got %d", test.answer, result) + } + } +} diff --git a/inputs b/inputs index 7b4edb5..9dab284 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 7b4edb501ae13ede33a73dfe41a9ce5fc3df02d7 +Subproject commit 9dab284aee297b017537bec4e9c12a649edb6764