day 5
This commit is contained in:
parent
ce0ccaaafa
commit
ab99f807e8
@ -1,8 +1,11 @@
|
|||||||
package aocutils
|
package aocutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"iter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SplitComma(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
func SplitComma(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
@ -18,6 +21,22 @@ func SplitComma(data []byte, atEOF bool) (advance int, token []byte, err error)
|
|||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SplitString(s string) bufio.SplitFunc {
|
||||||
|
sep := []byte(s)
|
||||||
|
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
|
if atEOF && len(data) == 0 {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
if i := bytes.Index(data, sep); i >= 0 {
|
||||||
|
return i + len(sep), data[0:i], nil
|
||||||
|
}
|
||||||
|
if atEOF {
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ReadString(r io.Reader) (string, error) {
|
func ReadString(r io.Reader) (string, error) {
|
||||||
buff, err := io.ReadAll(r)
|
buff, err := io.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -25,3 +44,95 @@ func ReadString(r io.Reader) (string, error) {
|
|||||||
}
|
}
|
||||||
return string(buff), nil
|
return string(buff), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
Println(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
Body io.Reader
|
||||||
|
Logger Logger
|
||||||
|
Context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Scanner(split ...bufio.SplitFunc) *bufio.Scanner {
|
||||||
|
s := bufio.NewScanner(c.Body)
|
||||||
|
if len(split) == 1 {
|
||||||
|
s.Split(split[0])
|
||||||
|
}
|
||||||
|
if len(split) > 2 {
|
||||||
|
panic("too many split line handlers")
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func (c *Context) BodyString() (string, error) {
|
||||||
|
return ReadString(c.Body)
|
||||||
|
}
|
||||||
|
func (c *Context) BodyBytes() ([]byte, error) {
|
||||||
|
return io.ReadAll(c.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Printf(format string, args ...interface{}) {
|
||||||
|
if c.Logger != nil {
|
||||||
|
c.Logger.Printf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (c *Context) Println(args ...interface{}) {
|
||||||
|
if c.Logger != nil {
|
||||||
|
c.Logger.Println(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomScanner[T scannable, K any] struct {
|
||||||
|
val K
|
||||||
|
err error
|
||||||
|
|
||||||
|
inner scannable
|
||||||
|
mapFunc func(T) (K, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomScanner[T, K]) From(t T) {
|
||||||
|
s.inner = t
|
||||||
|
}
|
||||||
|
func (s *CustomScanner[T, K]) MapFunc(f func(t T) (K, error)) {
|
||||||
|
s.mapFunc = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomScanner[T, K]) Value() K {
|
||||||
|
return s.val
|
||||||
|
}
|
||||||
|
func (s *CustomScanner[T, K]) Values() iter.Seq[K] {
|
||||||
|
return func(yield func(K) bool) {
|
||||||
|
for s.Scan() {
|
||||||
|
if !yield(s.Value()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type scannable interface {
|
||||||
|
Scan() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomScanner[T, K]) Scan() bool {
|
||||||
|
if s.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.inner.Scan() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.val, s.err = s.mapFunc(s.inner.(T))
|
||||||
|
if s.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomScanner[T, K]) Err() error {
|
||||||
|
return s.err
|
||||||
|
}
|
||||||
|
|||||||
@ -6,12 +6,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"iter"
|
"iter"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aocutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Day1Part1(r io.Reader, l Logger) (any, error) {
|
func Day1Part1(ctx aocutils.Context) (any, error) {
|
||||||
dial := 50
|
dial := 50
|
||||||
solution := 0
|
solution := 0
|
||||||
for pair := range day1GetPairs(r) {
|
for pair := range day1GetPairs(ctx.Body) {
|
||||||
dial += pair.MustValue()
|
dial += pair.MustValue()
|
||||||
dial %= 100
|
dial %= 100
|
||||||
if dial < 0 {
|
if dial < 0 {
|
||||||
@ -20,34 +22,34 @@ func Day1Part1(r io.Reader, l Logger) (any, error) {
|
|||||||
if dial == 0 {
|
if dial == 0 {
|
||||||
solution += 1
|
solution += 1
|
||||||
}
|
}
|
||||||
l.Printf("The dial is rotated %s(%d) to point at 0 `once` \n", pair, pair.MustValue())
|
ctx.Printf("The dial is rotated %s(%d) to point at 0 `once` \n", pair, pair.MustValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
return solution, nil
|
return solution, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day1Part2Slow(r io.Reader, l Logger) (any, error) {
|
func Day1Part2Slow(ctx aocutils.Context) (any, error) {
|
||||||
dial := 50
|
dial := 50
|
||||||
solution := 0
|
solution := 0
|
||||||
for pair := range day1GetPairs(r) {
|
for pair := range day1GetPairs(ctx.Body) {
|
||||||
nextDial, count := day1AdvanceSimulation(dial, pair.MustValue())
|
nextDial, count := day1AdvanceSimulation(dial, pair.MustValue())
|
||||||
dial = nextDial
|
dial = nextDial
|
||||||
solution += count
|
solution += count
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
l.Printf("The dial is rotated %s(%d) to point at %d, during this points to 0 (%d)\n", pair, pair.MustValue(), dial, count)
|
ctx.Printf("The dial is rotated %s(%d) to point at %d, during this points to 0 (%d)\n", pair, pair.MustValue(), dial, count)
|
||||||
} else {
|
} else {
|
||||||
l.Printf("The dial is rotated %s(%d) to point at %d\n", pair, pair.MustValue(), dial)
|
ctx.Printf("The dial is rotated %s(%d) to point at %d\n", pair, pair.MustValue(), dial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return solution, nil
|
return solution, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day1Part2Fast(r io.Reader, l Logger) (any, error) {
|
func Day1Part2Fast(ctx aocutils.Context) (any, error) {
|
||||||
dial := 50
|
dial := 50
|
||||||
solution := 0
|
solution := 0
|
||||||
for pair := range day1GetPairs(r) {
|
for pair := range day1GetPairs(ctx.Body) {
|
||||||
value := pair.MustValue()
|
value := pair.MustValue()
|
||||||
|
|
||||||
nextDial, count := day1Advance(dial, value)
|
nextDial, count := day1Advance(dial, value)
|
||||||
@ -56,9 +58,9 @@ func Day1Part2Fast(r io.Reader, l Logger) (any, error) {
|
|||||||
solution += count
|
solution += count
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
l.Printf("The dial is rotated %s(%d) to point at %d, during this points to 0 (%d)\n", pair, pair.MustValue(), dial, count)
|
ctx.Printf("The dial is rotated %s(%d) to point at %d, during this points to 0 (%d)\n", pair, pair.MustValue(), dial, count)
|
||||||
} else {
|
} else {
|
||||||
l.Printf("The dial is rotated %s(%d) to point at %d\n", pair, pair.MustValue(), dial)
|
ctx.Printf("The dial is rotated %s(%d) to point at %d\n", pair, pair.MustValue(), dial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -50,8 +49,8 @@ func (s *day2Scanner) Bounds() (int, int) {
|
|||||||
return s.a, s.b
|
return s.a, s.b
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day2Part1Simple(r io.Reader, l Logger) (any, error) {
|
func Day2Part1Simple(ctx aocutils.Context) (any, error) {
|
||||||
input, err := aocutils.ReadString(r)
|
input, err := ctx.BodyString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read input: %s", err)
|
return nil, fmt.Errorf("failed to read input: %s", err)
|
||||||
}
|
}
|
||||||
@ -94,8 +93,8 @@ func Day2Part1Simple(r io.Reader, l Logger) (any, error) {
|
|||||||
return sum, nil
|
return sum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day2Part2Simple(r io.Reader, l Logger) (any, error) {
|
func Day2Part2Simple(ctx aocutils.Context) (any, error) {
|
||||||
input, err := aocutils.ReadString(r)
|
input, err := ctx.BodyString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read input: %s", err)
|
return nil, fmt.Errorf("failed to read input: %s", err)
|
||||||
}
|
}
|
||||||
@ -154,16 +153,14 @@ func Day2Part2Simple(r io.Reader, l Logger) (any, error) {
|
|||||||
return sum, nil
|
return sum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day2Part1(r io.Reader, logger Logger) (any, error) {
|
func Day2Part1(ctx aocutils.Context) (any, error) {
|
||||||
inputScanner := bufio.NewScanner(r)
|
scanner := &day2Scanner{scanner: ctx.Scanner(aocutils.SplitComma), sep: '-'}
|
||||||
inputScanner.Split(aocutils.SplitComma)
|
|
||||||
scanner := &day2Scanner{scanner: inputScanner, sep: '-'}
|
|
||||||
|
|
||||||
var sum int
|
var sum int
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
start, end := scanner.Bounds()
|
start, end := scanner.Bounds()
|
||||||
logger.Println(start, "-", end)
|
ctx.Println(start, "-", end)
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
str := []byte(strconv.Itoa(i))
|
str := []byte(strconv.Itoa(i))
|
||||||
@ -171,7 +168,7 @@ func Day2Part1(r io.Reader, logger Logger) (any, error) {
|
|||||||
firstHalf := str[:len(str)/2]
|
firstHalf := str[:len(str)/2]
|
||||||
secondHalf := str[len(str)/2:]
|
secondHalf := str[len(str)/2:]
|
||||||
if bytes.Equal(firstHalf, secondHalf) {
|
if bytes.Equal(firstHalf, secondHalf) {
|
||||||
logger.Println("found ", i)
|
ctx.Println("found ", i)
|
||||||
sum += i
|
sum += i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,16 +178,13 @@ func Day2Part1(r io.Reader, logger Logger) (any, error) {
|
|||||||
return sum, scanner.Err()
|
return sum, scanner.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day2Part2(r io.Reader, logger Logger) (any, error) {
|
func Day2Part2(ctx aocutils.Context) (any, error) {
|
||||||
inputScanner := bufio.NewScanner(r)
|
scanner := &day2Scanner{scanner: ctx.Scanner(aocutils.SplitComma), sep: '-'}
|
||||||
inputScanner.Split(aocutils.SplitComma)
|
|
||||||
|
|
||||||
scanner := &day2Scanner{scanner: inputScanner, sep: '-'}
|
|
||||||
|
|
||||||
var sum int
|
var sum int
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
start, end := scanner.Bounds()
|
start, end := scanner.Bounds()
|
||||||
logger.Println(start, "-", end)
|
ctx.Println(start, "-", end)
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
str := []byte(strconv.Itoa(i))
|
str := []byte(strconv.Itoa(i))
|
||||||
@ -210,7 +204,7 @@ func Day2Part2(r io.Reader, logger Logger) (any, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isInvalid {
|
if isInvalid {
|
||||||
logger.Println("found ", i)
|
ctx.Println("found ", i)
|
||||||
sum += i
|
sum += i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,23 +3,24 @@ package year25
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aocutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Day3Part1(r io.Reader, l Logger) (any, error) {
|
func Day3Part1(ctx aocutils.Context) (any, error) {
|
||||||
return Day3(r, l, 2)
|
return Day3(ctx, 2)
|
||||||
}
|
}
|
||||||
func Day3Part2(r io.Reader, l Logger) (any, error) {
|
func Day3Part2(ctx aocutils.Context) (any, error) {
|
||||||
return Day3(r, l, 12)
|
return Day3(ctx, 12)
|
||||||
}
|
}
|
||||||
func Day3(r io.Reader, logger Logger, size int) (int64, error) {
|
func Day3(ctx aocutils.Context, size int) (int64, error) {
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := ctx.Scanner(bufio.ScanLines)
|
||||||
scanner.Split(bufio.ScanLines)
|
|
||||||
var sum int64
|
var sum int64
|
||||||
|
|
||||||
findLargetDigit := func(digits []rune) (rune, int) {
|
findLargetDigit := func(digits []rune) (rune, int) {
|
||||||
largestDigit := rune(0)
|
var largestDigit rune
|
||||||
index := 0
|
index := 0
|
||||||
|
|
||||||
for i := len(digits) - 1; i >= 0; i-- {
|
for i := len(digits) - 1; i >= 0; i-- {
|
||||||
@ -28,7 +29,7 @@ func Day3(r io.Reader, logger Logger, size int) (int64, error) {
|
|||||||
index = i
|
index = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rune(largestDigit) - '0', index
|
return largestDigit, index
|
||||||
}
|
}
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
@ -42,23 +43,46 @@ func Day3(r io.Reader, logger Logger, size int) (int64, error) {
|
|||||||
for index := range parts {
|
for index := range parts {
|
||||||
digit, start := findLargetDigit(line[:len(line)+index-len(parts)+1])
|
digit, start := findLargetDigit(line[:len(line)+index-len(parts)+1])
|
||||||
line = line[start+1:]
|
line = line[start+1:]
|
||||||
if digit < 0 || digit > 9 {
|
if digit < '0' || digit > '9' {
|
||||||
return 0, fmt.Errorf("ivalid char '%c' in line %s", digit, text)
|
return 0, fmt.Errorf("ivalid char '%c' in line %s", digit, text)
|
||||||
} else {
|
} else {
|
||||||
parts[index] = byte(digit)
|
parts[index] = byte(digit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.Println("line: ", text)
|
|
||||||
|
|
||||||
var partSum int64
|
partSum, err := strconv.Atoi(string(parts))
|
||||||
for _, part := range parts {
|
if err != nil {
|
||||||
partSum *= 10
|
return 0, fmt.Errorf("invalid result %s for line %s", string(parts), text)
|
||||||
partSum += int64(part)
|
|
||||||
}
|
}
|
||||||
logger.Println("got: ", partSum)
|
|
||||||
logger.Println()
|
ctx.Println("line: ", text)
|
||||||
sum += partSum
|
ctx.Println("got: ", string(parts))
|
||||||
|
ctx.Println()
|
||||||
|
sum += int64(partSum)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum, scanner.Err()
|
return sum, scanner.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Day3Compact(input string, size int) (total int64, _ error) {
|
||||||
|
findLargetDigit := func(digits string, skip int) (digit byte, idx int, next string) {
|
||||||
|
for i := len(digits) - 1 - skip; i >= 0; i-- {
|
||||||
|
if digits[i] >= digit {
|
||||||
|
digit, idx = digits[i], i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return digit, idx, digits[idx+1:]
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(input, "\n") {
|
||||||
|
var parts = make([]byte, size)
|
||||||
|
for index := range size {
|
||||||
|
parts[index], _, line = findLargetDigit(line, size-1-index)
|
||||||
|
}
|
||||||
|
if partSum, err := strconv.ParseInt(string(parts), 10, 64); err != nil {
|
||||||
|
return 0, fmt.Errorf("invalid result %s", string(parts))
|
||||||
|
} else {
|
||||||
|
total += partSum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -2,21 +2,21 @@ package year25
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
|
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aocutils"
|
||||||
"git.bizdoc.ro/private/devkit.git/collections/geometry/v2"
|
"git.bizdoc.ro/private/devkit.git/collections/geometry/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Day4PaperRoll = '@'
|
const Day4PaperRoll = '@'
|
||||||
|
|
||||||
func Day4Part1(r io.Reader, l Logger) (any, error) {
|
func Day4Part1(ctx aocutils.Context) (any, error) {
|
||||||
// 4 rolls in 8 adjacent positions
|
// 4 rolls in 8 adjacent positions
|
||||||
const maxAdjacentRolls = 4
|
const maxAdjacentRolls = 4
|
||||||
|
|
||||||
var accessibleRolls int
|
var accessibleRolls int
|
||||||
g, err := geometry.ReaderToByteGrid(r)
|
g, err := geometry.ReaderToByteGrid(ctx.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("day4: failed to read grid: %w", err)
|
return nil, fmt.Errorf("day4 part1: failed to read grid: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for c := range g.Points() {
|
for c := range g.Points() {
|
||||||
@ -39,14 +39,14 @@ func Day4Part1(r io.Reader, l Logger) (any, error) {
|
|||||||
return accessibleRolls, nil
|
return accessibleRolls, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Day4Part2(r io.Reader, l Logger) (any, error) {
|
func Day4Part2(ctx aocutils.Context) (any, error) {
|
||||||
// 4 rolls in 8 adjacent positions
|
// 4 rolls in 8 adjacent positions
|
||||||
const maxAdjacentRolls = 4
|
const maxAdjacentRolls = 4
|
||||||
|
|
||||||
var totalRemovedRolls int
|
var totalRemovedRolls int
|
||||||
g, err := geometry.ReaderToByteGrid(r)
|
g, err := geometry.ReaderToByteGrid(ctx.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("day4: failed to read grid: %w", err)
|
return nil, fmt.Errorf("day4 part2: failed to read grid: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -65,7 +65,7 @@ func Day4Part2(r io.Reader, l Logger) (any, error) {
|
|||||||
|
|
||||||
if count <= maxAdjacentRolls {
|
if count <= maxAdjacentRolls {
|
||||||
removedRolls += 1
|
removedRolls += 1
|
||||||
g.Set(c, '.') // remove grid
|
g.Set(c, '.') // remove roll from grid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
140
year25/day5.go
Normal file
140
year25/day5.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package year25
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aocutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Day5Interval struct {
|
||||||
|
Start, End int64
|
||||||
|
isRemoved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type MergedIntervals struct {
|
||||||
|
intervals []*Day5Interval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergedIntervals) Put(newInterval Day5Interval) {
|
||||||
|
for item := range m.Items() {
|
||||||
|
if newInterval.Start <= item.End && newInterval.End >= item.Start {
|
||||||
|
newInterval.Start = min(newInterval.Start, item.Start)
|
||||||
|
newInterval.End = max(newInterval.End, item.End)
|
||||||
|
|
||||||
|
item.isRemoved = true
|
||||||
|
m.Put(newInterval)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// just in case there are too many intervals
|
||||||
|
m.intervals = slices.DeleteFunc(m.intervals, func(i *Day5Interval) bool {
|
||||||
|
return i.isRemoved
|
||||||
|
})
|
||||||
|
|
||||||
|
m.intervals = append(m.intervals, &newInterval)
|
||||||
|
}
|
||||||
|
func (m *MergedIntervals) Put2(newInterval Day5Interval) {
|
||||||
|
// fast search start, end
|
||||||
|
for item := range m.Items() {
|
||||||
|
if newInterval.Start <= item.End && newInterval.End >= item.Start {
|
||||||
|
newInterval.Start = min(newInterval.Start, item.Start)
|
||||||
|
newInterval.End = max(newInterval.End, item.End)
|
||||||
|
|
||||||
|
item.isRemoved = true
|
||||||
|
m.Put(newInterval)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert in order
|
||||||
|
m.intervals = append(m.intervals, &newInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergedIntervals) CheckItem(itemId int64) (*Day5Interval, bool) {
|
||||||
|
for interval := range m.Items() {
|
||||||
|
if interval.Start <= itemId && itemId <= interval.End {
|
||||||
|
return interval, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergedIntervals) Items() iter.Seq[*Day5Interval] {
|
||||||
|
return func(yield func(*Day5Interval) bool) {
|
||||||
|
for _, interval := range m.intervals {
|
||||||
|
if !interval.isRemoved {
|
||||||
|
if !yield(interval) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Day5ParseIntervals(s *bufio.Scanner) (intervals *MergedIntervals, err error) {
|
||||||
|
intervals = new(MergedIntervals)
|
||||||
|
for s.Scan() {
|
||||||
|
line := strings.TrimSpace(s.Text())
|
||||||
|
if line == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var newInterval Day5Interval
|
||||||
|
dashIndex := strings.Index(line, "-")
|
||||||
|
newInterval.Start, err = strconv.ParseInt(line[:dashIndex], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return intervals, fmt.Errorf("day5: failed to parse line %s. invalid start index: %w", line, err)
|
||||||
|
}
|
||||||
|
newInterval.End, err = strconv.ParseInt(line[dashIndex+1:], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return intervals, fmt.Errorf("day5: failed to parse line %s. invalid end index: %w", line, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
intervals.Put(newInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return intervals, s.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Day5Part1(ctx aocutils.Context) (int, error) {
|
||||||
|
scanner := ctx.Scanner()
|
||||||
|
intervals, err := Day5ParseIntervals(scanner)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("day5: failed to parse intervals %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var freshIngredients int
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
itemId, err := strconv.ParseInt(line, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("day5: failed to parse itemId in line %s. %w", line, err)
|
||||||
|
}
|
||||||
|
if interval, ok := intervals.CheckItem(itemId); ok {
|
||||||
|
freshIngredients += 1
|
||||||
|
ctx.Println(itemId, "is fresh because it was found in", interval)
|
||||||
|
} else {
|
||||||
|
ctx.Println(itemId, "is spoiled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return freshIngredients, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Day5Part2(ctx aocutils.Context) (solution int64, _ error) {
|
||||||
|
intervals, err := Day5ParseIntervals(ctx.Scanner())
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("day5: failed to parse intervals %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for interval := range intervals.Items() {
|
||||||
|
solution += interval.End - interval.Start + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return solution, nil
|
||||||
|
}
|
||||||
35
year25/day5.ts
Normal file
35
year25/day5.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const input = ""
|
||||||
|
|
||||||
|
console.log(part1(input))
|
||||||
|
console.log(part2(input))
|
||||||
|
|
||||||
|
type Item = [number, number]
|
||||||
|
const [start, end] = [0, 1]
|
||||||
|
|
||||||
|
function part1(input: string): number {
|
||||||
|
const [db, ids] = parseInput(input)
|
||||||
|
return ids.filter(currentId => {
|
||||||
|
return db.find(id => id[start] <= currentId && id[end] >= currentId)
|
||||||
|
}).length
|
||||||
|
}
|
||||||
|
|
||||||
|
function part2(input: string): number {
|
||||||
|
const [db, _] = parseInput(input)
|
||||||
|
return db.reduce((sum, i) => sum + (i[end] - i[start] + 1), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseInput(input: string): [Item[], number[]] {
|
||||||
|
const [dbLines, itemsLines] = input.trim().split('\n\n')
|
||||||
|
.map(n => n.trim().split('\n'))
|
||||||
|
|
||||||
|
const db = dbLines.map(line => line.split('-').map(n => parseInt(n, 10)) as Item)
|
||||||
|
.reduce((db: Item[], newItem: Item) => {
|
||||||
|
const mergedInterval = db
|
||||||
|
.filter(item => newItem[start] <= item[end] && newItem[end] >= item[start])
|
||||||
|
.reduce((acc, item) => [Math.min(acc[start], item[start]), Math.max(acc[end], item[end])], newItem)
|
||||||
|
const remaining = db.filter(item => (item[end] < newItem[start] || item[start] > newItem[end]))
|
||||||
|
return [...remaining, mergedInterval] as Item[]
|
||||||
|
}, [] as Item[])
|
||||||
|
|
||||||
|
return [db, itemsLines.map(Number)]
|
||||||
|
}
|
||||||
@ -1,6 +0,0 @@
|
|||||||
package year25
|
|
||||||
|
|
||||||
type Logger interface {
|
|
||||||
Printf(string, ...interface{})
|
|
||||||
Println(...interface{})
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user