Skip to content

Commit 55376b4

Browse files
authored
Implement 2021/day01 puzzle solution (#58)
* feat: Add support of 2021 year puzzles * refactor: Propper error handling for input fetch * feat: Initial commit for 2021/day01 puzzle * feat: Implement 2021/day01 part1 * chore: Update regression tests * feat: Add 2021/day01 test and spec * refactor: register puzzles in each year file * docs: Fix package comments * feat: Implement 2021/day01 part2 * docs: Mark 2021/day01 as solved * style: fix code smell
1 parent bc2a4ee commit 55376b4

File tree

19 files changed

+769
-58
lines changed

19 files changed

+769
-58
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ This repository contains solutions for puzzles and cli tool to run solutions to
219219
<details>
220220
<summary>2021</summary>
221221

222-
- [ ] [Day 1: Sonar Sweep](https://adventofcode.com/2021/day/1)
222+
- [x] [Day 1: Sonar Sweep](https://adventofcode.com/2021/day/1)
223223
- [ ] [Day 2: Dive!](https://adventofcode.com/2021/day/2)
224224
- [ ] [Day 3: Binary Diagnostic](https://adventofcode.com/2021/day/3)
225225
- [ ] [Day 4: ?](https://adventofcode.com/2021/day/4)

cmd/aoc-cli/version.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// aoc-cli is a tool to run solutions to get answers for input on advent-of-code site.
21
package main
32

43
import (

internal/puzzles/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const (
5151
Year2018 // 2018
5252
Year2019 // 2019
5353
Year2020 // 2020
54+
Year2021 // 2021
5455

5556
yearSentinel
5657
)

internal/puzzles/day_string_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestDay_String(t *testing.T) {
2929
for _, tt := range tests {
3030
t.Run(tt.name, func(t *testing.T) {
3131
got := tt.i.String()
32+
3233
assert.Equal(t, tt.want, got)
3334
})
3435
}

internal/puzzles/input/content.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package input
33

44
import (
55
"context"
6+
"errors"
67
"fmt"
78
"io"
89
"net/http"
@@ -13,6 +14,14 @@ import (
1314
"github.com/obalunenko/logger"
1415
)
1516

17+
var (
18+
19+
// ErrNotFound returns when puzzle input is not yet unlocked or invalid date passed.
20+
ErrNotFound = errors.New("puzzle inout not found")
21+
// ErrUnauthorized returns when session is empty or invalid.
22+
ErrUnauthorized = errors.New("unauthorized")
23+
)
24+
1625
// Date holds date info.
1726
type Date struct {
1827
Year string
@@ -52,9 +61,9 @@ func Get(ctx context.Context, d Date, session string) ([]byte, error) {
5261
case http.StatusOK:
5362
return body, nil
5463
case http.StatusNotFound:
55-
return nil, fmt.Errorf("[%s] puzzle input not found", d)
64+
return nil, fmt.Errorf("[%s]: %w", d, ErrNotFound)
5665
case http.StatusBadRequest:
57-
return nil, fmt.Errorf("unauthorized")
66+
return nil, ErrUnauthorized
5867
default:
5968
return nil, fmt.Errorf("[%s] failed to get puzzle input[%s]", d, resp.Status)
6069
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Package day01 contains solution for https://adventofcode.com/2021/day/1 puzzle.
2+
package day01
3+
4+
import (
5+
"bufio"
6+
"fmt"
7+
"io"
8+
"strconv"
9+
10+
"github.com/obalunenko/advent-of-code/internal/puzzles"
11+
)
12+
13+
func init() {
14+
puzzles.Register(solution{})
15+
}
16+
17+
type solution struct{}
18+
19+
func (s solution) Year() string {
20+
return puzzles.Year2021.String()
21+
}
22+
23+
func (s solution) Day() string {
24+
return puzzles.Day01.String()
25+
}
26+
27+
func (s solution) Part1(input io.Reader) (string, error) {
28+
list, err := makeMeasurementsList(input)
29+
if err != nil {
30+
return "", fmt.Errorf("make measurements list: %w", err)
31+
}
32+
33+
const (
34+
shift = 1
35+
windowSize = 1
36+
)
37+
38+
increasednum := findIncreased(list, shift, windowSize)
39+
40+
return strconv.Itoa(increasednum), nil
41+
}
42+
43+
func (s solution) Part2(input io.Reader) (string, error) {
44+
list, err := makeMeasurementsList(input)
45+
if err != nil {
46+
return "", fmt.Errorf("make measurements list: %w", err)
47+
}
48+
49+
const (
50+
shift = 1
51+
windowSize = 3
52+
)
53+
54+
increasednum := findIncreased(list, shift, windowSize)
55+
56+
return strconv.Itoa(increasednum), nil
57+
}
58+
59+
func findIncreased(list []int, shift, window int) int {
60+
var increadsed int
61+
62+
for i := 0; i <= len(list)-window; i += shift {
63+
if i == 0 {
64+
continue
65+
}
66+
67+
var m1, m2 int
68+
69+
k := i
70+
for j := window; j > 0; j-- {
71+
m2 += list[k]
72+
m1 += list[k-shift]
73+
74+
k++
75+
}
76+
77+
if m2 > m1 {
78+
increadsed++
79+
}
80+
}
81+
82+
return increadsed
83+
}
84+
85+
func makeMeasurementsList(input io.Reader) ([]int, error) {
86+
scanner := bufio.NewScanner(input)
87+
88+
var measurements []int
89+
90+
for scanner.Scan() {
91+
line := scanner.Text()
92+
93+
n, err := strconv.Atoi(line)
94+
if err != nil {
95+
return nil, fmt.Errorf("parse int: %w", err)
96+
}
97+
98+
measurements = append(measurements, n)
99+
}
100+
101+
if err := scanner.Err(); err != nil {
102+
return nil, fmt.Errorf("scanner error: %w", err)
103+
}
104+
105+
return measurements, nil
106+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package day01
2+
3+
import (
4+
"io"
5+
"strings"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func Test_solution_Year(t *testing.T) {
12+
var s solution
13+
14+
want := "2021"
15+
got := s.Year()
16+
17+
assert.Equal(t, want, got)
18+
}
19+
20+
func Test_solution_Day(t *testing.T) {
21+
var s solution
22+
23+
want := "1"
24+
got := s.Day()
25+
26+
assert.Equal(t, want, got)
27+
}
28+
29+
func Test_solution_Part1(t *testing.T) {
30+
var s solution
31+
32+
type args struct {
33+
input io.Reader
34+
}
35+
36+
tests := []struct {
37+
name string
38+
args args
39+
want string
40+
wantErr bool
41+
}{
42+
{
43+
name: "test example from description",
44+
args: args{
45+
input: strings.NewReader("199\n200\n208\n210\n200\n207\n240\n269\n260\n263\n"),
46+
},
47+
want: "7",
48+
wantErr: false,
49+
},
50+
}
51+
52+
for _, tt := range tests {
53+
t.Run(tt.name, func(t *testing.T) {
54+
got, err := s.Part1(tt.args.input)
55+
if tt.wantErr {
56+
assert.Error(t, err)
57+
58+
return
59+
}
60+
61+
assert.NoError(t, err)
62+
assert.Equal(t, tt.want, got)
63+
})
64+
}
65+
}
66+
67+
func Test_solution_Part2(t *testing.T) {
68+
var s solution
69+
70+
type args struct {
71+
input io.Reader
72+
}
73+
74+
tests := []struct {
75+
name string
76+
args args
77+
want string
78+
wantErr bool
79+
}{
80+
{
81+
name: "test example from description",
82+
args: args{
83+
input: strings.NewReader("199\n200\n208\n210\n200\n207\n240\n269\n260\n263\n"),
84+
},
85+
want: "5",
86+
wantErr: false,
87+
},
88+
}
89+
90+
for _, tt := range tests {
91+
t.Run(tt.name, func(t *testing.T) {
92+
got, err := s.Part2(tt.args.input)
93+
if tt.wantErr {
94+
assert.Error(t, err)
95+
96+
return
97+
}
98+
99+
assert.NoError(t, err)
100+
assert.Equal(t, tt.want, got)
101+
})
102+
}
103+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# --- Day 1: Sonar Sweep ---
2+
3+
## --- Part One ---
4+
5+
You're minding your own business on a ship at sea when the overboard alarm goes off! You rush to see if you can help.
6+
Apparently, one of the Elves tripped and accidentally sent the sleigh keys flying into the ocean!
7+
8+
Before you know it, you're inside a submarine the Elves keep ready for situations like this. It's covered in Christmas
9+
lights (because of course it is), and it even has an experimental antenna that should be able to track the keys if you
10+
can boost its signal strength high enough; there's a little meter that indicates the antenna's signal strength by
11+
displaying 0-50 stars.
12+
13+
Your instincts tell you that in order to save Christmas, you'll need to get all fifty stars by December 25th.
14+
15+
Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent calendar;
16+
the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!
17+
18+
As the submarine drops below the surface of the ocean, it automatically performs a sonar sweep of the nearby sea floor.
19+
On a small screen, the sonar sweep report (your puzzle input) appears: each line is a measurement of the sea floor depth as the sweep looks further and further away from the submarine.
20+
21+
### For example, suppose you had the following report:
22+
23+
```text
24+
199
25+
200
26+
208
27+
210
28+
200
29+
207
30+
240
31+
269
32+
260
33+
263
34+
```
35+
36+
This report indicates that, scanning outward from the submarine, the sonar sweep found depths
37+
of `199, 200, 208, 210`, and so on.
38+
39+
The first order of business is to figure out how quickly the depth increases, just so you know what you're dealing
40+
with - you never know if the keys will get carried into deeper water by an ocean current or a fish or something.
41+
42+
To do this, count the number of times a depth measurement increases from the previous measurement. (
43+
There is no measurement before the first measurement.) In the example above, the changes are as follows:
44+
45+
```text
46+
199 (N/A - no previous measurement)
47+
200 (increased)
48+
208 (increased)
49+
210 (increased)
50+
200 (decreased)
51+
207 (increased)
52+
240 (increased)
53+
269 (increased)
54+
260 (decreased)
55+
263 (increased)
56+
```
57+
58+
In this example, there are `7` measurements that are larger than the previous measurement.
59+
60+
How many measurements are larger than the previous measurement?
61+
62+
## --- Part Two ---
63+
Considering every single measurement isn't as useful as you expected: there's just too much noise in the data.
64+
65+
Instead, consider sums of a three-measurement sliding window. Again considering the above example:
66+
67+
```text
68+
199 A
69+
200 A B
70+
208 A B C
71+
210 B C D
72+
200 E C D
73+
207 E F D
74+
240 E F G
75+
269 F G H
76+
260 G H
77+
263 H
78+
```
79+
80+
Start by comparing the first and second three-measurement windows. The measurements in the first window are
81+
marked `A (199, 200, 208)`; their sum is `199 + 200 + 208 = 607`.
82+
The second window is marked `B (200, 208, 210)`; its sum is `618`.
83+
The sum of measurements in the second window is larger than the sum of the first, so this first comparison `increased`.
84+
85+
Your goal now is to count the number of times the sum of measurements in this sliding window increases from the
86+
previous sum. So, compare `A` with `B`, then compare `B` with `C`, then `C` with `D`, and so on.
87+
Stop when there aren't enough measurements left to create a new three-measurement sum.
88+
89+
In the above example, the sum of each three-measurement window is as follows:
90+
91+
```text
92+
A: 607 (N/A - no previous sum)
93+
B: 618 (increased)
94+
C: 618 (no change)
95+
D: 617 (decreased)
96+
E: 647 (increased)
97+
F: 716 (increased)
98+
G: 769 (increased)
99+
H: 792 (increased)
100+
```
101+
102+
In this example, there are `5` sums that are larger than the previous sum.
103+
104+
Consider sums of a three-measurement sliding window. How many sums are larger than the previous sum?

internal/puzzles/solutions/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// }
2121
//
2222
// Then to register solution in the list of all solutions: make a blank import of package with puzzle solution
23-
// at register.go
23+
// at register_<year>.go
2424
//
2525
// import _ "github.com/obalunenko/advent-of-code/puzzles/solutions/day01"
2626
//

0 commit comments

Comments
 (0)