Skip to content

Commit

Permalink
Implement 2021/day01 puzzle solution (#58)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
obalunenko authored Dec 3, 2021
1 parent bc2a4ee commit 55376b4
Show file tree
Hide file tree
Showing 19 changed files with 769 additions and 58 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ This repository contains solutions for puzzles and cli tool to run solutions to
<details>
<summary>2021</summary>

- [ ] [Day 1: Sonar Sweep](https://adventofcode.com/2021/day/1)
- [x] [Day 1: Sonar Sweep](https://adventofcode.com/2021/day/1)
- [ ] [Day 2: Dive!](https://adventofcode.com/2021/day/2)
- [ ] [Day 3: Binary Diagnostic](https://adventofcode.com/2021/day/3)
- [ ] [Day 4: ?](https://adventofcode.com/2021/day/4)
Expand Down
1 change: 0 additions & 1 deletion cmd/aoc-cli/version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// aoc-cli is a tool to run solutions to get answers for input on advent-of-code site.
package main

import (
Expand Down
1 change: 1 addition & 0 deletions internal/puzzles/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
Year2018 // 2018
Year2019 // 2019
Year2020 // 2020
Year2021 // 2021

yearSentinel
)
Expand Down
1 change: 1 addition & 0 deletions internal/puzzles/day_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestDay_String(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.i.String()

assert.Equal(t, tt.want, got)
})
}
Expand Down
13 changes: 11 additions & 2 deletions internal/puzzles/input/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package input

import (
"context"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -13,6 +14,14 @@ import (
"github.com/obalunenko/logger"
)

var (

// ErrNotFound returns when puzzle input is not yet unlocked or invalid date passed.
ErrNotFound = errors.New("puzzle inout not found")
// ErrUnauthorized returns when session is empty or invalid.
ErrUnauthorized = errors.New("unauthorized")
)

// Date holds date info.
type Date struct {
Year string
Expand Down Expand Up @@ -52,9 +61,9 @@ func Get(ctx context.Context, d Date, session string) ([]byte, error) {
case http.StatusOK:
return body, nil
case http.StatusNotFound:
return nil, fmt.Errorf("[%s] puzzle input not found", d)
return nil, fmt.Errorf("[%s]: %w", d, ErrNotFound)
case http.StatusBadRequest:
return nil, fmt.Errorf("unauthorized")
return nil, ErrUnauthorized
default:
return nil, fmt.Errorf("[%s] failed to get puzzle input[%s]", d, resp.Status)
}
Expand Down
106 changes: 106 additions & 0 deletions internal/puzzles/solutions/2021/day01/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Package day01 contains solution for https://adventofcode.com/2021/day/1 puzzle.
package day01

import (
"bufio"
"fmt"
"io"
"strconv"

"github.com/obalunenko/advent-of-code/internal/puzzles"
)

func init() {
puzzles.Register(solution{})
}

type solution struct{}

func (s solution) Year() string {
return puzzles.Year2021.String()
}

func (s solution) Day() string {
return puzzles.Day01.String()
}

func (s solution) Part1(input io.Reader) (string, error) {
list, err := makeMeasurementsList(input)
if err != nil {
return "", fmt.Errorf("make measurements list: %w", err)
}

const (
shift = 1
windowSize = 1
)

increasednum := findIncreased(list, shift, windowSize)

return strconv.Itoa(increasednum), nil
}

func (s solution) Part2(input io.Reader) (string, error) {
list, err := makeMeasurementsList(input)
if err != nil {
return "", fmt.Errorf("make measurements list: %w", err)
}

const (
shift = 1
windowSize = 3
)

increasednum := findIncreased(list, shift, windowSize)

return strconv.Itoa(increasednum), nil
}

func findIncreased(list []int, shift, window int) int {
var increadsed int

for i := 0; i <= len(list)-window; i += shift {
if i == 0 {
continue
}

var m1, m2 int

k := i
for j := window; j > 0; j-- {
m2 += list[k]
m1 += list[k-shift]

k++
}

if m2 > m1 {
increadsed++
}
}

return increadsed
}

func makeMeasurementsList(input io.Reader) ([]int, error) {
scanner := bufio.NewScanner(input)

var measurements []int

for scanner.Scan() {
line := scanner.Text()

n, err := strconv.Atoi(line)
if err != nil {
return nil, fmt.Errorf("parse int: %w", err)
}

measurements = append(measurements, n)
}

if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("scanner error: %w", err)
}

return measurements, nil
}
103 changes: 103 additions & 0 deletions internal/puzzles/solutions/2021/day01/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package day01

import (
"io"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_solution_Year(t *testing.T) {
var s solution

want := "2021"
got := s.Year()

assert.Equal(t, want, got)
}

func Test_solution_Day(t *testing.T) {
var s solution

want := "1"
got := s.Day()

assert.Equal(t, want, got)
}

func Test_solution_Part1(t *testing.T) {
var s solution

type args struct {
input io.Reader
}

tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "test example from description",
args: args{
input: strings.NewReader("199\n200\n208\n210\n200\n207\n240\n269\n260\n263\n"),
},
want: "7",
wantErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := s.Part1(tt.args.input)
if tt.wantErr {
assert.Error(t, err)

return
}

assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

func Test_solution_Part2(t *testing.T) {
var s solution

type args struct {
input io.Reader
}

tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "test example from description",
args: args{
input: strings.NewReader("199\n200\n208\n210\n200\n207\n240\n269\n260\n263\n"),
},
want: "5",
wantErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := s.Part2(tt.args.input)
if tt.wantErr {
assert.Error(t, err)

return
}

assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
104 changes: 104 additions & 0 deletions internal/puzzles/solutions/2021/day01/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# --- Day 1: Sonar Sweep ---

## --- Part One ---

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.
Apparently, one of the Elves tripped and accidentally sent the sleigh keys flying into the ocean!

Before you know it, you're inside a submarine the Elves keep ready for situations like this. It's covered in Christmas
lights (because of course it is), and it even has an experimental antenna that should be able to track the keys if you
can boost its signal strength high enough; there's a little meter that indicates the antenna's signal strength by
displaying 0-50 stars.

Your instincts tell you that in order to save Christmas, you'll need to get all fifty stars by December 25th.

Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent calendar;
the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!

As the submarine drops below the surface of the ocean, it automatically performs a sonar sweep of the nearby sea floor.
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.

### For example, suppose you had the following report:

```text
199
200
208
210
200
207
240
269
260
263
```

This report indicates that, scanning outward from the submarine, the sonar sweep found depths
of `199, 200, 208, 210`, and so on.

The first order of business is to figure out how quickly the depth increases, just so you know what you're dealing
with - you never know if the keys will get carried into deeper water by an ocean current or a fish or something.

To do this, count the number of times a depth measurement increases from the previous measurement. (
There is no measurement before the first measurement.) In the example above, the changes are as follows:

```text
199 (N/A - no previous measurement)
200 (increased)
208 (increased)
210 (increased)
200 (decreased)
207 (increased)
240 (increased)
269 (increased)
260 (decreased)
263 (increased)
```

In this example, there are `7` measurements that are larger than the previous measurement.

How many measurements are larger than the previous measurement?

## --- Part Two ---
Considering every single measurement isn't as useful as you expected: there's just too much noise in the data.

Instead, consider sums of a three-measurement sliding window. Again considering the above example:

```text
199 A
200 A B
208 A B C
210 B C D
200 E C D
207 E F D
240 E F G
269 F G H
260 G H
263 H
```

Start by comparing the first and second three-measurement windows. The measurements in the first window are
marked `A (199, 200, 208)`; their sum is `199 + 200 + 208 = 607`.
The second window is marked `B (200, 208, 210)`; its sum is `618`.
The sum of measurements in the second window is larger than the sum of the first, so this first comparison `increased`.

Your goal now is to count the number of times the sum of measurements in this sliding window increases from the
previous sum. So, compare `A` with `B`, then compare `B` with `C`, then `C` with `D`, and so on.
Stop when there aren't enough measurements left to create a new three-measurement sum.

In the above example, the sum of each three-measurement window is as follows:

```text
A: 607 (N/A - no previous sum)
B: 618 (increased)
C: 618 (no change)
D: 617 (decreased)
E: 647 (increased)
F: 716 (increased)
G: 769 (increased)
H: 792 (increased)
```

In this example, there are `5` sums that are larger than the previous sum.

Consider sums of a three-measurement sliding window. How many sums are larger than the previous sum?
2 changes: 1 addition & 1 deletion internal/puzzles/solutions/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// }
//
// Then to register solution in the list of all solutions: make a blank import of package with puzzle solution
// at register.go
// at register_<year>.go
//
// import _ "github.com/obalunenko/advent-of-code/puzzles/solutions/day01"
//
Expand Down
Loading

0 comments on commit 55376b4

Please sign in to comment.