1
0
Fork 0

Finally created day 3 part 1 solution

This commit is contained in:
Maxim Lebedev 2023-12-06 08:17:46 +06:00
parent 7e6b0e3bba
commit 1a9e5b9206
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
4 changed files with 369 additions and 1 deletions

41
day3_test.go Normal file
View File

@ -0,0 +1,41 @@
package adventofcode_test
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"source.toby3d.me/toby3d/adventofcode/internal/day3/part1"
)
/*
You and the Elf eventually reach a gondola lift station; he says the gondola
lift will take you up to the water source, but this is as far as he can bring
you. You go inside.
It doesn't take long to find the gondolas, but there seems to be a problem:
they're not moving.
"Aaah!"
You turn around to see a slightly-greasy Elf with a wrench and a look of
surprise. "Sorry, I wasn't expecting anyone! The gondola lift isn't working
right now; it'll still be a while before I can fix it." You offer to help.
The engineer explains that an engine part seems to be missing from the engine,
but nobody can figure out which one. If you can add up all the part numbers in
the engine schematic, it should be easy to work out which part is missing.
*/
func ExampleDay3() {
input, err := os.ReadFile(filepath.Join("internal", "day3", "testdata", "input"))
if err != nil {
log.Fatalln("cannot open input file:", err)
}
result := part1.GetPartNumbersSum(bytes.NewReader(input))
fmt.Println(result)
// Output: 540025
}

2
go.mod
View File

@ -2,4 +2,4 @@ module source.toby3d.me/toby3d/adventofcode
go 1.21.4
require github.com/google/go-cmp v0.6.0 // indirect
require github.com/google/go-cmp v0.6.0

View File

@ -0,0 +1,180 @@
package part1
import (
"bufio"
"io"
"strconv"
)
type (
EngineMap [][]Point
Point byte
Number struct {
value []byte
minX int
maxX int
y int
}
)
func NewNumber(y int) *Number {
return &Number{
minX: 0,
maxX: 0,
y: y,
value: make([]byte, 0),
}
}
func (n *Number) Write(x int, p Point) {
n.value = append(n.value, byte(p))
n.maxX = x
}
func (n Number) IsZero() bool {
return len(n.value) == 0
}
func (n *Number) Move(x int) {
n.minX = x
n.maxX = x
}
func (n Number) Uint() uint {
result, _ := strconv.ParseUint(string(n.value), 10, 64)
return uint(result)
}
func (p Point) IsDigit() bool {
switch p {
default:
return false
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return true
}
}
func (p Point) IsSymbol() bool {
switch p {
default:
return true
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
return false
}
}
func (p Point) IsEmpty() bool {
return p == '.'
}
func (em EngineMap) HasSymbolAround(n Number) bool {
minx := n.minX - 1
if minx < 0 {
minx = 0
}
maxx := n.maxX + 1
if maxx >= len(em[0]) {
maxx = len(em[0]) - 1
}
miny := n.y - 1
if miny < 0 {
miny = 0
}
maxy := n.y + 1
if maxy >= len(em) {
maxy = len(em) - 1
}
for y := miny; y <= maxy; y++ {
for x := minx; x <= maxx; x++ {
if !em[y][x].IsSymbol() {
continue
}
return true
}
}
return false
}
func ParseEngineMap(r io.Reader) EngineMap {
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)
engineMap := make(EngineMap, 0)
y := 0
for scanner.Scan() {
line := scanner.Bytes()
engineMap = append(engineMap, make([]Point, len(line)))
for x := range line {
engineMap[y][x] = Point(line[x])
}
y++
}
return engineMap
}
func GetPartNumbersSum(r io.Reader) uint {
var result uint
for _, number := range GetPartNumbers(r) {
result += number.Uint()
}
return result
}
func GetPartNumbers(r io.Reader) []Number {
engineMap := ParseEngineMap(r)
numbers := make([]Number, 0)
for y := range engineMap {
n := NewNumber(y)
for x := range engineMap[y] {
p := engineMap[y][x]
if p.IsDigit() {
if n.IsZero() {
n.Move(x)
}
n.Write(x, p)
continue
}
if n.IsZero() {
continue
}
numbers = append(numbers, *n)
n = NewNumber(y)
}
if !n.IsZero() {
numbers = append(numbers, *n)
}
}
result := make([]Number, 0, len(numbers))
for i := range numbers {
if !engineMap.HasSymbolAround(numbers[i]) {
continue
}
result = append(result, numbers[i])
}
return result
}

View File

@ -0,0 +1,147 @@
package part1_test
import (
"fmt"
"strings"
"testing"
"source.toby3d.me/toby3d/adventofcode/internal/day3/part1"
)
func TestGetPartNumbersSum(t *testing.T) {
t.Parallel()
for name, tc := range map[string]struct {
input string
expect uint
}{
// NOTE(toby3d): thanks, Calcifer777!
// https://old.reddit.com/r/adventofcode/comments/189m7uu/2023_day_3_part_1_okay_then/kbt4tae/
"center": {
input: "...12\n" +
"12*..",
expect: 24,
},
// NOTE(toby3d): thanks, i_have_no_biscuits!
// https://old.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/
"ultimate1": {
input: "12.......*..\n" +
"+.........34\n" +
".......-12..\n" +
"..78........\n" +
"..*....60...\n" +
"78..........\n" +
".......23...\n" +
"....90*12...\n" +
"............\n" +
"2.2......12.\n" +
".*.........*\n" +
"1.1.......56",
expect: 413,
},
"ultimate2": {
input: "12.......*..\n" +
"+.........34\n" +
".......-12..\n" +
"..78........\n" +
"..*....60...\n" +
"78.........9\n" +
".5.....23..$\n" +
"8...90*12...\n" +
"............\n" +
"2.2......12.\n" +
".*.........*\n" +
"1.1..503+.56",
expect: 925,
},
// NOTE(toby3d): thanks, IsatisCrucifer!
// https://old.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/kbt0vh8/
"search": {
input: "........\n" +
".24..4..\n" +
"......*.",
expect: 4,
},
// NOTE(toby3d): thanks, i_have_no_biscuits!
// https://old.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/kbt2yp5/
"same": {
input: "....................\n" +
"..-52..52-..52..52..\n" +
"..................-.",
expect: 156,
},
// NOTE(toby3d): thanks, musifter!
// https://old.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/kbsrno0/
"gears": {
input: ".......5......\n" +
"..7*..*.......\n" +
"...*13*.......\n" +
".......15.....",
expect: 40,
},
// NOTE(toby3d): thanks, Martins31!
// https://old.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/kbtiucy/
"space": {
input: ".......5......\n" +
"..7*..*.....4*\n" +
"...*13*......9\n" +
".......15.....\n" +
"..............\n" +
"..............\n" +
"..............\n" +
"..............\n" +
"..............\n" +
"..............\n" +
"21............\n" +
"...*9.........",
expect: 62,
},
// NOTE(toby3d): thanks, 1234abcdcba4321!
// https://old.reddit.com/r/adventofcode/comments/18a9pnl/2023_day_3_part_1_go_need_a_fresh_pair_of_eyes/kbwicj0/
"zero": {
input: "...\n" +
"1.*\n" +
"...",
expect: 0,
},
// NOTE(toby3d): thanks, 1234abcdcba4321!
// https://old.reddit.com/r/adventofcode/comments/18a2yir/year_2023_day_3_part_1_are_part_numbers_mutually/kbv5q64/
"ones": {
input: "1..\n" +
"*.1\n" +
"1..",
expect: 2,
},
} {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
if actual := part1.GetPartNumbersSum(strings.NewReader(tc.input)); actual != tc.expect {
t.Errorf("want %d, got %d", tc.expect, actual)
}
})
}
}
func Example() {
// The engine schematic (your puzzle input) consists of a visual
// representation of the engine. There are lots of numbers and symbols
// you don't really understand, but apparently any number adjacent to a
// symbol, even diagonally, is a "part number" and should be included in
// your sum. (Periods (.) do not count as a symbol.)
input := "467..114..\n" +
"...*......\n" +
"..35..633.\n" +
"......#...\n" +
"617*......\n" +
".....+.58.\n" +
"..592.....\n" +
"......755.\n" +
"...$.*....\n" +
".664.598.."
fmt.Println(part1.GetPartNumbersSum(strings.NewReader(input)))
// Output: 4361
}