Skip to content

Commit 4f6b4a8

Browse files
authored
feat: Implement 2024 day01 solution (#334)
* feat: Add 2024 year to constants * feat: Add 2024/day01 initial code * feat: Implement solution for 2024 day 1 * fix: Linter warnings in 2017 day 02
1 parent 66bb133 commit 4f6b4a8

File tree

11 files changed

+742
-10
lines changed

11 files changed

+742
-10
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ SHELL := env APP_NAME=$(APP_NAME) $(SHELL)
88

99
SHELL := env GOTOOLS_IMAGE_TAG=$(GOTOOLS_IMAGE_TAG) $(SHELL)
1010

11+
AOC_PUZZLE_URL=
12+
SHELL := env AOC_PUZZLE_URL=$(AOC_PUZZLE_URL) $(SHELL)
13+
1114
COMPOSE_TOOLS_FILE=deployments/docker-compose/go-tools-docker-compose.yml
1215
COMPOSE_TOOLS_CMD_BASE=docker compose -f $(COMPOSE_TOOLS_FILE)
1316
COMPOSE_TOOLS_CMD_UP=$(COMPOSE_TOOLS_CMD_BASE) up --exit-code-from

internal/puzzles/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const (
5454
Year2021 // 2021
5555
Year2022 // 2022
5656
Year2023 // 2023
57+
Year2024 // 2024
5758

5859
yearSentinel
5960
)

internal/puzzles/solutions/2017/day02/solution.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func (s solution) Year() string {
2828

2929
func (s solution) Part1(input io.Reader) (string, error) {
3030
var f checksumFunc = func(row []string) (int, error) {
31-
var min, max int
31+
var minVal, maxVal int
3232

3333
for i, number := range row {
3434
d, err := strconv.Atoi(number)
@@ -37,19 +37,19 @@ func (s solution) Part1(input io.Reader) (string, error) {
3737
}
3838

3939
if i == 0 {
40-
min, max = d, d
40+
minVal, maxVal = d, d
4141
}
4242

43-
if d < min {
44-
min = d
43+
if d < minVal {
44+
minVal = d
4545
}
4646

47-
if d > max {
48-
max = d
47+
if d > maxVal {
48+
maxVal = d
4949
}
5050
}
5151

52-
return max - min, nil
52+
return maxVal - minVal, nil
5353
}
5454

5555
return findChecksum(input, f)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Package day01 contains solution for https://adventofcode.com/2024/day/1 puzzle.
2+
package day01
3+
4+
import (
5+
"bufio"
6+
"fmt"
7+
"io"
8+
"slices"
9+
"strconv"
10+
"strings"
11+
12+
"github.com/obalunenko/advent-of-code/internal/puzzles"
13+
)
14+
15+
func init() {
16+
puzzles.Register(solution{})
17+
}
18+
19+
type solution struct{}
20+
21+
func (s solution) Year() string {
22+
return puzzles.Year2024.String()
23+
}
24+
25+
func (s solution) Day() string {
26+
return puzzles.Day01.String()
27+
}
28+
29+
func (s solution) Part1(input io.Reader) (string, error) {
30+
l, err := parseInput(input)
31+
if err != nil {
32+
return "", fmt.Errorf("failed to parse input: %w", err)
33+
}
34+
35+
slices.Sort(l.itemsA)
36+
slices.Sort(l.itemsB)
37+
38+
var sum int
39+
40+
for i := 0; i < len(l.itemsA); i++ {
41+
d := l.itemsA[i] - l.itemsB[i]
42+
if d < 0 {
43+
d = -d
44+
}
45+
46+
sum += d
47+
}
48+
49+
return strconv.Itoa(sum), nil
50+
}
51+
52+
func (s solution) Part2(input io.Reader) (string, error) {
53+
l, err := parseInput(input)
54+
if err != nil {
55+
return "", fmt.Errorf("failed to parse input: %w", err)
56+
}
57+
58+
seenA := make(map[int]int)
59+
60+
for _, a := range l.itemsA {
61+
seenA[a] = 0
62+
63+
for _, b := range l.itemsB {
64+
if a == b {
65+
seenA[a]++
66+
}
67+
}
68+
}
69+
70+
var sum int
71+
72+
for _, a := range l.itemsA {
73+
sum += a * seenA[a]
74+
}
75+
76+
return strconv.Itoa(sum), nil
77+
}
78+
79+
type lists struct {
80+
itemsA []int
81+
itemsB []int
82+
}
83+
84+
func parseInput(input io.Reader) (lists, error) {
85+
const (
86+
listsNum = 2
87+
listAIdx = 0
88+
listBIdx = 1
89+
)
90+
91+
l := lists{
92+
itemsA: make([]int, 0),
93+
itemsB: make([]int, 0),
94+
}
95+
96+
scanner := bufio.NewScanner(input)
97+
for scanner.Scan() {
98+
line := scanner.Text()
99+
100+
parts := strings.Split(line, " ")
101+
if len(parts) != listsNum {
102+
return lists{}, fmt.Errorf("invalid input line: %s", line)
103+
}
104+
105+
// Parse parts[0] and parts[1] to integers and append them to l.itemsA and l.itemsB respectively.
106+
a, err := strconv.Atoi(parts[listAIdx])
107+
if err != nil {
108+
return lists{}, fmt.Errorf("failed to parse int: %w", err)
109+
}
110+
111+
b, err := strconv.Atoi(parts[listBIdx])
112+
if err != nil {
113+
return lists{}, fmt.Errorf("failed to parse int: %w", err)
114+
}
115+
116+
l.itemsA = append(l.itemsA, a)
117+
118+
l.itemsB = append(l.itemsB, b)
119+
}
120+
121+
if scanner.Err() != nil {
122+
return lists{}, fmt.Errorf("scanner error: %w", scanner.Err())
123+
}
124+
125+
return l, nil
126+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package day01
2+
3+
import (
4+
"errors"
5+
"io"
6+
"path/filepath"
7+
"testing"
8+
"testing/iotest"
9+
10+
"github.com/stretchr/testify/assert"
11+
12+
"github.com/obalunenko/advent-of-code/internal/puzzles/common/utils"
13+
)
14+
15+
func Test_solution_Year(t *testing.T) {
16+
var s solution
17+
18+
want := "2024"
19+
got := s.Year()
20+
21+
assert.Equal(t, want, got)
22+
}
23+
24+
func Test_solution_Day(t *testing.T) {
25+
var s solution
26+
27+
want := "1"
28+
got := s.Day()
29+
30+
assert.Equal(t, want, got)
31+
}
32+
33+
func Test_solution_Part1(t *testing.T) {
34+
var s solution
35+
36+
type args struct {
37+
input io.Reader
38+
}
39+
40+
tests := []struct {
41+
name string
42+
args args
43+
want string
44+
wantErr assert.ErrorAssertionFunc
45+
}{
46+
{
47+
name: "test example from description",
48+
args: args{
49+
input: utils.ReaderFromFile(t, filepath.Join("testdata", "input.txt")),
50+
},
51+
want: "11",
52+
wantErr: assert.NoError,
53+
},
54+
{
55+
name: "",
56+
args: args{
57+
input: iotest.ErrReader(errors.New("custom error")),
58+
},
59+
want: "",
60+
wantErr: assert.Error,
61+
},
62+
}
63+
64+
for _, tt := range tests {
65+
t.Run(tt.name, func(t *testing.T) {
66+
got, err := s.Part1(tt.args.input)
67+
if !tt.wantErr(t, err) {
68+
return
69+
}
70+
71+
assert.Equal(t, tt.want, got)
72+
})
73+
}
74+
}
75+
76+
func Test_solution_Part2(t *testing.T) {
77+
var s solution
78+
79+
type args struct {
80+
input io.Reader
81+
}
82+
83+
tests := []struct {
84+
name string
85+
args args
86+
want string
87+
wantErr assert.ErrorAssertionFunc
88+
}{
89+
{
90+
name: "test example from description",
91+
args: args{
92+
input: utils.ReaderFromFile(t, filepath.Join("testdata", "input.txt")),
93+
},
94+
want: "31",
95+
wantErr: assert.NoError,
96+
},
97+
{
98+
name: "",
99+
args: args{
100+
input: iotest.ErrReader(errors.New("custom error")),
101+
},
102+
want: "",
103+
wantErr: assert.Error,
104+
},
105+
}
106+
107+
for _, tt := range tests {
108+
t.Run(tt.name, func(t *testing.T) {
109+
got, err := s.Part2(tt.args.input)
110+
if !tt.wantErr(t, err) {
111+
return
112+
}
113+
114+
assert.Equal(t, tt.want, got)
115+
})
116+
}
117+
}

0 commit comments

Comments
 (0)