summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2023-12-14 15:50:29 +0100
committerMartin Polden <mpolden@mpolden.no>2023-12-14 16:06:00 +0100
commit591632c0ba05e5d9c925f06cbc46c86451e30d61 (patch)
treee0940cfbb6f57c3497e3e9d2002e8d80dde74d1b
parentf4aa7da5f6d60158aad8f487a8c59e612093ef9b (diff)
aoc23: refactor
-rw-r--r--aoc23/day01_test.go2
-rw-r--r--aoc23/day02_test.go6
-rw-r--r--aoc23/day03_test.go2
-rw-r--r--aoc23/day04_test.go13
-rw-r--r--aoc23/util.go182
5 files changed, 110 insertions, 95 deletions
diff --git a/aoc23/day01_test.go b/aoc23/day01_test.go
index e3119ea..21f66ba 100644
--- a/aoc23/day01_test.go
+++ b/aoc23/day01_test.go
@@ -28,7 +28,7 @@ func findDigits(text string, parseWords bool) []int {
func sumCalibrations(r io.Reader, parseWords bool) int {
values := parseLines(r, func(s string) []int { return findDigits(s, parseWords) })
- return sum(map2(values, func(ints []int) int { return (ints[0] * 10) + ints[len(ints)-1] }))
+ return sum(transform(values, func(ints []int) int { return (ints[0] * 10) + ints[len(ints)-1] }))
}
func TestDay01(t *testing.T) {
diff --git a/aoc23/day02_test.go b/aoc23/day02_test.go
index bf7aeda..b34615c 100644
--- a/aoc23/day02_test.go
+++ b/aoc23/day02_test.go
@@ -38,7 +38,7 @@ func parseGame(line string) Game {
}
func validGame(game Game, rgb Rgb) bool {
- return noneMatch(game.cubes, func(cubes Rgb) bool {
+ return none(game.cubes, func(cubes Rgb) bool {
return cubes.r > rgb.r || cubes.g > rgb.g || cubes.b > rgb.b
})
}
@@ -46,7 +46,7 @@ func validGame(game Game, rgb Rgb) bool {
func countValidGames(r io.Reader) int {
games := parseLines(r, parseGame)
valid := filter(games, partial(validGame, Rgb{12, 13, 14}))
- return sum(map2(valid, func(g Game) int { return g.id }))
+ return sum(transform(valid, func(g Game) int { return g.id }))
}
func minCubes(game Game) Rgb {
@@ -61,7 +61,7 @@ func minCubes(game Game) Rgb {
func minCubesProduct(r io.Reader) int {
games := parseLines(r, parseGame)
- products := map2(games, compose(compose(minCubes, func(rgb Rgb) []int {
+ products := transform(games, compose(compose(minCubes, func(rgb Rgb) []int {
return []int{rgb.r, rgb.g, rgb.b}
}), product))
return sum(products)
diff --git a/aoc23/day03_test.go b/aoc23/day03_test.go
index dfbfeb9..6ce0758 100644
--- a/aoc23/day03_test.go
+++ b/aoc23/day03_test.go
@@ -83,7 +83,7 @@ func findNumber(r io.Reader, consumer func(int, []Point, [][]rune)) {
func sumPartNumbers(r io.Reader) int {
sum := 0
findNumber(r, func(n int, points []Point, grid [][]rune) {
- if anyMatch(points, partial(nearSymbol, grid)) {
+ if some(points, partial(nearSymbol, grid)) {
sum += n
}
})
diff --git a/aoc23/day04_test.go b/aoc23/day04_test.go
index e9dd190..08c154f 100644
--- a/aoc23/day04_test.go
+++ b/aoc23/day04_test.go
@@ -2,29 +2,26 @@ package aoc23
import (
"io"
- "regexp"
"strings"
"testing"
)
type Card struct {
id int
- winners Set[int]
+ winners *Set[int]
got []int
}
func parseCard(s string) Card {
- re := regexp.MustCompile(": | \\| ")
- parts := re.Split(s, -1)
+ parts := strings.FieldsFunc(s, func(r rune) bool { return r == ':' || r == '|' })
id := requireInt(strings.Fields(parts[0])[1])
- got := map2(strings.Fields(parts[2]), requireInt)
- winners := Set[int]{}
- winners.AddAll(map2(strings.Fields(parts[1]), requireInt))
+ winners := NewSet(transform(strings.Fields(parts[1]), requireInt))
+ got := transform(strings.Fields(parts[2]), requireInt)
return Card{id, winners, got}
}
func countWinners(card Card) int {
- return frequency(card.got, func(g int) bool { return card.winners.Contains(g) })
+ return quantify(card.got, func(g int) bool { return card.winners.Contains(g) })
}
func cardPoints(r io.Reader) int {
diff --git a/aoc23/util.go b/aoc23/util.go
index 097f413..269a98a 100644
--- a/aoc23/util.go
+++ b/aoc23/util.go
@@ -11,46 +11,6 @@ import (
"testing"
)
-type Set[V comparable] struct{ set map[V]bool }
-
-func (s *Set[V]) Add(v V) bool {
- _, ok := s.set[v]
- if !ok {
- if s.set == nil {
- s.set = make(map[V]bool)
- }
- s.set[v] = true
- }
- return !ok
-}
-
-func (s *Set[V]) AddAll(vs []V) bool {
- changed := false
- for _, v := range vs {
- if s.Add(v) {
- changed = true
- }
- }
- return changed
-}
-
-func (s *Set[V]) Contains(v V) bool {
- _, ok := s.set[v]
- return ok
-}
-
-func (s *Set[V]) Slice() []V {
- values := make([]V, 0, len(s.set))
- for v := range s.set {
- values = append(values, v)
- }
- return values
-}
-
-func (s *Set[V]) Len() int { return len(s.set) }
-
-func (s *Set[V]) Reset() { clear(s.set) }
-
func assert(t *testing.T, want, got int) {
t.Helper()
if got != want {
@@ -65,13 +25,17 @@ func run(f func(r io.Reader) int, r io.Reader) int {
return f(r)
}
-func partial[V1, V2, R any](f func(V1, V2) R, frozenArg V2) func(V1) R {
- return func(v1 V1) R { return f(v1, frozenArg) }
+func inputFile(day int) *os.File {
+ f, err := os.Open(fmt.Sprintf("input/input%02d.txt", day))
+ if err != nil {
+ panic(err)
+ }
+ return f
}
-func compose[V1, R1, R2 any](f1 func(V1) R1, f2 func(R1) R2) func(V1) R2 {
- return func(v V1) R2 { return f2(f1(v)) }
-}
+func inputString(s string) io.Reader { return strings.NewReader(strings.TrimSpace(s)) }
+
+// Parsing
func parseLines[T any](r io.Reader, parser func(line string) T) []T {
scanner := bufio.NewScanner(r)
@@ -80,19 +44,12 @@ func parseLines[T any](r io.Reader, parser func(line string) T) []T {
line := strings.TrimSpace(scanner.Text())
values = append(values, parser(line))
}
- return values
-}
-
-func inputFile(day int) *os.File {
- f, err := os.Open(fmt.Sprintf("input/input%02d.txt", day))
- if err != nil {
+ if err := scanner.Err(); err != nil {
panic(err)
}
- return f
+ return values
}
-func inputString(s string) io.Reader { return strings.NewReader(strings.TrimSpace(s)) }
-
func requireInt(s string) int {
n, err := strconv.Atoi(s)
if err != nil {
@@ -105,25 +62,15 @@ func runes(s string) []rune { return []rune(s) }
func isDigit(r rune) bool { return int(r) >= 48 && int(r) <= 57 }
-func max(a, b int) int {
- if a > b {
- return a
- }
- return b
-}
+// Functional
-func min(a, b int) int {
- if a < b {
- return a
- }
- return b
+func partial[V1, V2, R any](f func(V1, V2) R, frozenArg V2) func(V1) R {
+ return func(v1 V1) R { return f(v1, frozenArg) }
}
-func add(a, b int) int { return a + b }
-
-func mul(a, b int) int { return a * b }
-
-func pow(a, b int) int { return int(math.Pow(float64(a), float64(b))) }
+func compose[V1, R1, R2 any](f1 func(V1) R1, f2 func(R1) R2) func(V1) R2 {
+ return func(v V1) R2 { return f2(f1(v)) }
+}
func reduce[V any](values []V, f func(a, b V) V, initial V) V {
acc := initial
@@ -133,25 +80,26 @@ func reduce[V any](values []V, f func(a, b V) V, initial V) V {
return acc
}
-func map2[V any, R any](values []V, f func(v V) R) []R {
- mapped := make([]R, len(values))
+// map is reserved :(
+func transform[V any, R any](values []V, f func(v V) R) []R {
+ result := make([]R, len(values))
for i, v := range values {
- mapped[i] = f(v)
+ result[i] = f(v)
}
- return mapped
+ return result
}
func filter[V any](values []V, pred func(v V) bool) []V {
- var filtered []V
+ var result []V
for _, v := range values {
if pred(v) {
- filtered = append(filtered, v)
+ result = append(result, v)
}
}
- return filtered
+ return result
}
-func frequency[V any](values []V, pred func(v V) bool) int {
+func quantify[V any](values []V, pred func(v V) bool) int {
n := 0
for _, v := range values {
if pred(v) {
@@ -161,14 +109,84 @@ func frequency[V any](values []V, pred func(v V) bool) int {
return n
}
-func anyMatch[V any](values []V, pred func(v V) bool) bool { return frequency(values, pred) > 0 }
+func some[V any](values []V, pred func(v V) bool) bool { return quantify(values, pred) > 0 }
-func allMatch[V any](values []V, pred func(v V) bool) bool {
- return frequency(values, pred) == len(values)
+func all[V any](values []V, pred func(v V) bool) bool {
+ return quantify(values, pred) == len(values)
}
-func noneMatch[V any](values []V, pred func(v V) bool) bool { return !anyMatch(values, pred) }
+func none[V any](values []V, pred func(v V) bool) bool { return quantify(values, pred) == 0 }
+
+// Math
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func add(a, b int) int { return a + b }
+
+func mul(a, b int) int { return a * b }
+
+func pow(a, b int) int { return int(math.Pow(float64(a), float64(b))) }
func product(ints []int) int { return reduce(ints, mul, 1) }
func sum(ints []int) int { return reduce(ints, add, 0) }
+
+// Collections
+
+type Set[V comparable] struct{ set map[V]bool }
+
+func NewSet[V comparable](vs []V) *Set[V] {
+ set := Set[V]{}
+ set.AddAll(vs)
+ return &set
+}
+
+func (s *Set[V]) Add(v V) bool {
+ _, ok := s.set[v]
+ if !ok {
+ if s.set == nil {
+ s.set = make(map[V]bool)
+ }
+ s.set[v] = true
+ }
+ return !ok
+}
+
+func (s *Set[V]) AddAll(vs []V) bool {
+ changed := false
+ for _, v := range vs {
+ if s.Add(v) {
+ changed = true
+ }
+ }
+ return changed
+}
+
+func (s *Set[V]) Contains(v V) bool {
+ _, ok := s.set[v]
+ return ok
+}
+
+func (s *Set[V]) Slice() []V {
+ values := make([]V, 0, len(s.set))
+ for v := range s.set {
+ values = append(values, v)
+ }
+ return values
+}
+
+func (s *Set[V]) Len() int { return len(s.set) }
+
+func (s *Set[V]) Reset() { clear(s.set) }