From 591632c0ba05e5d9c925f06cbc46c86451e30d61 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Thu, 14 Dec 2023 15:50:29 +0100 Subject: aoc23: refactor --- aoc23/day01_test.go | 2 +- aoc23/day02_test.go | 6 +- aoc23/day03_test.go | 2 +- aoc23/day04_test.go | 13 ++-- aoc23/util.go | 182 +++++++++++++++++++++++++++++----------------------- 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) } -- cgit v1.2.3