Skip to content

Commit 2ab8bae

Browse files
authored
Implement 2021/day03 puzzle solution (#60)
* feat: Add 2021/day03 puzzle specs and test * feat: Register 2021/day03 puzzle * fix: Register issue * feat: Implement 2021/day03 part1 * tests: Update regression test for 2021/day03 part1 * feat: Implement 2021/day03 part2 * tests: Add regression tests for 2021/day03 * docs: Mark 2021/day03 as solved * fix: Sonar code smells * style: Gofmt
1 parent cfb579b commit 2ab8bae

File tree

6 files changed

+560
-4
lines changed

6 files changed

+560
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ This repository contains solutions for puzzles and cli tool to run solutions to
221221

222222
- [x] [Day 1: Sonar Sweep](https://adventofcode.com/2021/day/1)
223223
- [x] [Day 2: Dive!](https://adventofcode.com/2021/day/2)
224-
- [ ] [Day 3: Binary Diagnostic](https://adventofcode.com/2021/day/3)
224+
- [x] [Day 3: Binary Diagnostic](https://adventofcode.com/2021/day/3)
225225
- [ ] [Day 4: Giant Squid](https://adventofcode.com/2021/day/4)
226226
- [ ] [Day 5: ?](https://adventofcode.com/2021/day/5)
227227
- [ ] [Day 6: ?](https://adventofcode.com/2021/day/6)
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Package day03 contains solution for https://adventofcode.com/2021/day/3 puzzle.
2+
package day03
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.Day03.String()
25+
}
26+
27+
func (s solution) Part1(input io.Reader) (string, error) {
28+
diagnostic, err := makeDiagnostic(input)
29+
if err != nil {
30+
return "", fmt.Errorf("make diagnostic report: %w", err)
31+
}
32+
33+
r := findPowerConsumptionRates(diagnostic)
34+
35+
consumption, err := r.consumption()
36+
if err != nil {
37+
return "", fmt.Errorf("consumption calculate: %w", err)
38+
}
39+
40+
return consumption, nil
41+
}
42+
43+
func (s solution) Part2(input io.Reader) (string, error) {
44+
diagnostic, err := makeDiagnostic(input)
45+
if err != nil {
46+
return "", fmt.Errorf("make diagnostic report: %w", err)
47+
}
48+
49+
r := lifeSupportRate(diagnostic)
50+
51+
consumption, err := r.consumption()
52+
if err != nil {
53+
return "", fmt.Errorf("consumption calculate: %w", err)
54+
}
55+
56+
return consumption, nil
57+
}
58+
59+
func makeDiagnostic(input io.Reader) ([]string, error) {
60+
scanner := bufio.NewScanner(input)
61+
62+
var report []string
63+
64+
for scanner.Scan() {
65+
line := scanner.Text()
66+
67+
report = append(report, line)
68+
}
69+
70+
if err := scanner.Err(); err != nil {
71+
return nil, fmt.Errorf("scanner error: %w", err)
72+
}
73+
74+
return report, nil
75+
}
76+
77+
type bitrates struct {
78+
first string
79+
second string
80+
}
81+
82+
func (b bitrates) consumption() (string, error) {
83+
const (
84+
baseBin = 2
85+
baseDec = 10
86+
bitsize = 64
87+
)
88+
89+
g, err := strconv.ParseInt(b.first, baseBin, bitsize)
90+
if err != nil {
91+
return "", fmt.Errorf("parse first: %w", err)
92+
}
93+
94+
e, err := strconv.ParseInt(b.second, baseBin, bitsize)
95+
if err != nil {
96+
return "", fmt.Errorf("parse second: %w", err)
97+
}
98+
99+
c := g * e
100+
101+
return strconv.FormatInt(c, baseDec), nil
102+
}
103+
104+
type (
105+
bit string
106+
pos int
107+
)
108+
109+
const (
110+
bit0 bit = "0"
111+
bit1 bit = "1"
112+
)
113+
114+
func findPowerConsumptionRates(diagnostic []string) bitrates {
115+
var result bitrates
116+
117+
bitsStat := make(map[pos]map[bit]int)
118+
119+
for _, s := range diagnostic {
120+
for p, r := range []rune(s) {
121+
if bitsStat[pos(p)] == nil {
122+
bitsStat[pos(p)] = make(map[bit]int)
123+
}
124+
125+
bitsStat[pos(p)][bit(r)]++
126+
}
127+
}
128+
129+
for i := 0; i < len(bitsStat); i++ {
130+
m := bitsStat[pos(i)]
131+
132+
if m[bit1] > m[bit0] {
133+
result.first += string(bit1)
134+
result.second += string(bit0)
135+
} else {
136+
result.first += string(bit0)
137+
result.second += string(bit1)
138+
}
139+
}
140+
141+
return result
142+
}
143+
144+
func lifeSupportRate(diagnostic []string) bitrates {
145+
o2 := lifeRate(diagnostic, o2Criteria)
146+
co2 := lifeRate(diagnostic, co2Criteria)
147+
148+
return bitrates{
149+
first: o2,
150+
second: co2,
151+
}
152+
}
153+
154+
func lifeRate(diagnostic []string, criteriaFunc bitCriteriaFunc) string {
155+
var (
156+
result string
157+
idx pos
158+
)
159+
160+
for len(diagnostic) != 1 {
161+
bitstat := make(map[bit]int)
162+
163+
for i := range diagnostic {
164+
runes := []rune(diagnostic[i])
165+
166+
b := bit(runes[idx])
167+
168+
bitstat[b]++
169+
}
170+
171+
diagnostic = filterDiagnostic(diagnostic, idx, bitstat, criteriaFunc)
172+
173+
idx++
174+
}
175+
176+
result = diagnostic[0]
177+
178+
return result
179+
}
180+
181+
type bitCriteriaFunc func(bitstat map[bit]int) bit
182+
183+
func o2Criteria(bitstat map[bit]int) bit {
184+
common := bit1
185+
186+
if bitstat[bit0] > bitstat[bit1] {
187+
common = bit0
188+
}
189+
190+
return common
191+
}
192+
193+
func co2Criteria(bitstat map[bit]int) bit {
194+
common := bit0
195+
196+
if bitstat[bit1] < bitstat[bit0] {
197+
common = bit1
198+
}
199+
200+
return common
201+
}
202+
203+
func filterDiagnostic(diagnostic []string, idx pos, bitstat map[bit]int, bfunc bitCriteriaFunc) []string {
204+
common := bfunc(bitstat)
205+
206+
filtered := make([]string, 0, len(diagnostic))
207+
208+
for i := range diagnostic {
209+
runes := []rune(diagnostic[i])
210+
211+
if bit(runes[idx]) == common {
212+
filtered = append(filtered, diagnostic[i])
213+
}
214+
}
215+
216+
return filtered
217+
}

0 commit comments

Comments
 (0)