Init commit

This commit is contained in:
2025-12-03 22:38:57 +02:00
commit 600b41ba7a
19 changed files with 5365 additions and 0 deletions

22
cmd/day2/digits.go Normal file
View File

@@ -0,0 +1,22 @@
package main
func digits(n uint) []uint8 {
if n == 0 {
return []uint8{0}
}
var digits []uint8
for ; n != 0; n /= 10 {
digits = append(digits, uint8(n%10))
}
return digits
}
func number(digits []uint8) uint {
var n uint
base := uint(1)
for _, d := range digits {
n += uint(d) * base
base *= 10
}
return n
}

30
cmd/day2/digits_test.go Normal file
View File

@@ -0,0 +1,30 @@
package main
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_digits_number(t *testing.T) {
tests := []struct {
n uint
d []uint8
}{
{n: 0, d: []uint8{0}},
{n: 1234, d: []uint8{4, 3, 2, 1}},
{n: 12345, d: []uint8{5, 4, 3, 2, 1}},
}
for _, tt := range tests {
name := strconv.FormatUint(uint64(tt.n), 10)
t.Run(name, func(t *testing.T) {
t.Run("digits", func(t *testing.T) {
assert.Equal(t, tt.d, digits(tt.n))
})
t.Run("number", func(t *testing.T) {
assert.Equal(t, tt.n, number(tt.d))
})
})
}
}

1
cmd/day2/example.txt Normal file
View File

@@ -0,0 +1 @@
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124

1
cmd/day2/input.txt Normal file
View File

@@ -0,0 +1 @@
959516-995437,389276443-389465477,683-1336,15687-26722,91613-136893,4-18,6736-12582,92850684-93066214,65-101,6868676926-6868700146,535033-570760,826141-957696,365650-534331,1502-2812,309789-352254,79110404-79172400,18286593-18485520,34376-65398,26-63,3333208697-3333457635,202007-307147,1859689-1936942,9959142-10053234,2318919-2420944,5142771457-5142940464,1036065-1206184,46314118-46413048,3367-6093,237-481,591751-793578

97
cmd/day2/main.go Normal file
View File

@@ -0,0 +1,97 @@
package main
import (
"flag"
"fmt"
"log"
"math"
"os"
"slices"
)
func main() {
debug := flag.Bool("debug", false, "Debug.")
flag.Parse()
ranges, err := readRanges(os.Stdin)
if err != nil {
log.Fatal(err)
}
var sum1, sum2 uint
for _, r := range ranges {
invalid1 := handleRange(r, scanPart1)
invalid2 := handleRange(r, scanPart2)
if *debug {
fmt.Printf("range: %d-%d\n", r.from, r.to)
}
if *debug && len(invalid1) > 0 {
fmt.Println("invalid (part 1):")
for _, id := range invalid1 {
fmt.Printf(" %d\n", id)
}
}
if *debug && len(invalid2) > 0 {
fmt.Println("invalid (part 2):")
for _, id := range invalid2 {
fmt.Printf(" %d\n", id)
}
}
for _, id := range invalid1 {
sum1 += id
}
for _, id := range invalid2 {
sum2 += id
}
}
fmt.Println("sum (part 1):", sum1)
fmt.Println("sum (part 2):", sum2)
}
func handleRange(r idRange, scan func(id uint) (advance uint, ok bool)) []uint {
var invalid []uint
for id := r.from; id <= r.to; {
advance, ok := scan(id)
if !ok {
invalid = append(invalid, id)
}
id += advance
}
return invalid
}
func scanPart1(id uint) (advance uint, ok bool) {
digits := digits(id)
if len(digits)%2 == 1 { // odd is always valid
nextSmallestEven := uint(math.Pow10(len(digits)))
return nextSmallestEven - id, true
}
var (
x = uint(math.Pow10(len(digits) / 2))
left = id / x
right = id % x
)
return 1, left != right
}
func scanPart2(id uint) (advance uint, ok bool) {
digits := digits(id)
if len(digits) <= 1 {
return 1, true
}
loop:
for i := 1; i <= len(digits)/2; i++ {
ref := digits[:i]
for chunk := range slices.Chunk(digits, i) {
if !slices.Equal(chunk, ref) {
continue loop
}
}
return 1, false
}
return 1, true
}

77
cmd/day2/main_test.go Normal file
View File

@@ -0,0 +1,77 @@
package main
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_scanPart1(t *testing.T) {
tests := []struct {
id uint
advance uint
ok bool
}{
// {
// id: 9,
// advance: 1,
// ok: true,
// },
// {
// id: 10,
// advance: 1,
// ok: true,
// },
// {
// id: 11,
// advance: 1,
// ok: false,
// },
// {
// id: 99,
// advance: 1,
// ok: false,
// },
// {
// id: 100,
// advance: 900,
// ok: true,
// },
{
id: 1234,
advance: 1,
ok: true,
},
}
for _, tt := range tests {
name := strconv.FormatUint(uint64(tt.id), 10)
t.Run(name, func(t *testing.T) {
advance, ok := scanPart1(tt.id)
assert.Equal(t, tt.advance, advance, "advance")
assert.Equal(t, tt.ok, ok, "ok")
})
}
}
func Test_scanPart2(t *testing.T) {
tests := []struct {
id uint
advance uint
ok bool
}{
{
id: 111,
advance: 1,
ok: false,
},
}
for _, tt := range tests {
name := strconv.FormatUint(uint64(tt.id), 10)
t.Run(name, func(t *testing.T) {
advance, ok := scanPart2(tt.id)
assert.Equal(t, tt.advance, advance, "advance")
assert.Equal(t, tt.ok, ok, "ok")
})
}
}

62
cmd/day2/range.go Normal file
View File

@@ -0,0 +1,62 @@
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"strconv"
"strings"
)
type idRange struct {
from, to uint
}
func readRanges(r io.Reader) ([]idRange, error) {
sc := bufio.NewScanner(r)
sc.Split(scanSimpleCSV)
var ranges []idRange
for sc.Scan() {
before, after, ok := strings.Cut(sc.Text(), "-")
if !ok {
return nil, fmt.Errorf("range %q: missing dash", sc.Text())
}
from, err := strconv.ParseUint(before, 10, 0)
if err != nil {
return nil, fmt.Errorf("range %q: %w", sc.Text(), err)
}
to, err := strconv.ParseUint(after, 10, 0)
if err != nil {
return nil, fmt.Errorf("range %q: %w", sc.Text(), err)
}
if from > to {
return nil, fmt.Errorf("range %q: refers from higher to lower", sc.Text())
}
ranges = append(ranges, idRange{
from: uint(from),
to: uint(to),
})
}
return ranges, sc.Err()
}
func scanSimpleCSV(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, ','); i >= 0 {
return i + 1, data[:i], nil
}
if atEOF {
if trimmed, ok := bytes.CutSuffix(data, []byte("\r\n")); ok {
return len(data), trimmed, nil
}
if trimmed, ok := bytes.CutSuffix(data, []byte{'\n'}); ok {
return len(data), trimmed, nil
}
return len(data), data, nil
}
return 0, nil, nil
}