Advent-of-Code/year25/day2.go
2025-12-04 00:41:23 +02:00

224 lines
4.3 KiB
Go

package year25
import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"strconv"
"strings"
"git.bizdoc.ro/private/Advent-of-Code.git/pkg/aocutils"
)
type day2Scanner struct {
scanner *bufio.Scanner
sep byte
a, b int
err error
}
func (s *day2Scanner) Err() error {
return s.err
}
func (s *day2Scanner) Scan() bool {
if s.scanner.Scan() {
interval := s.scanner.Bytes()
i := bytes.IndexByte(interval, s.sep)
if i <= 0 {
s.err = fmt.Errorf("interval separator not found in line %s", string(interval))
return false
}
start := interval[:i]
end := interval[i+1:]
s.a, s.err = strconv.Atoi(string(start))
if s.err != nil {
s.err = fmt.Errorf("failed to scann number: %s", s.err)
return false
}
s.b, s.err = strconv.Atoi(string(end))
if s.err != nil {
s.err = fmt.Errorf("failed to scann number: %s", s.err)
return false
}
return true
}
return false
}
func (s *day2Scanner) Bounds() (int, int) {
return s.a, s.b
}
func Day2Part1Simple(r io.Reader, l *log.Logger) (any, error) {
input, err := aocutils.ReadString(r)
if err != nil {
return nil, fmt.Errorf("failed to read input: %s", err)
}
ranges := strings.Split(strings.TrimSpace(input), ",")
isRepeated := func(n int) bool {
str := strconv.Itoa(n)
if len(str)%2 != 0 {
return false
}
half := len(str) / 2
return str[:half] == str[half:]
}
var sum int
for _, r := range ranges {
parts := strings.Split(r, "-")
if len(parts) != 2 {
continue
}
start, err := strconv.Atoi(parts[0])
if err != nil {
return nil, fmt.Errorf("failed to parse number: %s", parts[0])
}
end, err := strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("failed to parse number: %s", parts[1])
}
for v := start; v <= end; v++ {
if isRepeated(v) {
sum += v
}
}
}
return sum, nil
}
func Day2Part2Simple(r io.Reader, l *log.Logger) (any, error) {
input, err := aocutils.ReadString(r)
if err != nil {
return nil, fmt.Errorf("failed to read input: %s", err)
}
ranges := strings.Split(strings.TrimSpace(input), ",")
isRepeated := func(n int) bool {
s := strconv.Itoa(n)
l := len(s)
// Try all possible pattern lengths
for p := 1; p*2 <= l; p++ { // p <= l/2 ensures at least 2 repeats
if l%p != 0 {
continue
}
pat := s[:p]
ok := true
for i := 0; i < l; i += p {
if s[i:i+p] != pat {
ok = false
break
}
}
if ok {
return true
}
}
return false
}
var sum int
for _, r := range ranges {
parts := strings.Split(r, "-")
if len(parts) != 2 {
continue
}
start, err := strconv.Atoi(parts[0])
if err != nil {
return nil, fmt.Errorf("failed to parse number: %s", parts[0])
}
end, err := strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("failed to parse number: %s", parts[1])
}
for v := start; v <= end; v++ {
if isRepeated(v) {
sum += v
}
}
}
return sum, nil
}
func Day2Part1(r io.Reader, logger *log.Logger) (any, error) {
inputScanner := bufio.NewScanner(r)
inputScanner.Split(aocutils.SplitComma)
scanner := &day2Scanner{scanner: inputScanner, sep: '-'}
var sum int
for scanner.Scan() {
start, end := scanner.Bounds()
logger.Println(start, "-", end)
for i := start; i <= end; i++ {
str := []byte(strconv.Itoa(i))
if len(str)%2 == 0 {
firstHalf := str[:len(str)/2]
secondHalf := str[len(str)/2:]
if bytes.Equal(firstHalf, secondHalf) {
logger.Println("found ", i)
sum += i
}
}
}
}
return sum, scanner.Err()
}
func Day2Part2(r io.Reader, logger *log.Logger) (any, error) {
inputScanner := bufio.NewScanner(r)
inputScanner.Split(aocutils.SplitComma)
scanner := &day2Scanner{scanner: inputScanner, sep: '-'}
var sum int
for scanner.Scan() {
start, end := scanner.Bounds()
logger.Println(start, "-", end)
for i := start; i <= end; i++ {
str := []byte(strconv.Itoa(i))
for digitIndex := 1; digitIndex*2 <= len(str); digitIndex++ {
if len(str)%digitIndex != 0 {
// skip cant compare
continue
}
isInvalid := true
for chunkStart := digitIndex; chunkStart+digitIndex <= len(str); chunkStart += digitIndex {
if !bytes.Equal(str[0:digitIndex], str[chunkStart:chunkStart+digitIndex]) {
isInvalid = false
break
}
}
if isInvalid {
logger.Println("found ", i)
sum += i
break
}
}
}
}
return sum, scanner.Err()
}