Advent-of-Code/year25/day2.go

217 lines
4.1 KiB
Go

package year25
import (
"bufio"
"bytes"
"fmt"
"strconv"
"strings"
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aoc"
)
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(ctx aoc.Context) (any, error) {
input, err := ctx.BodyString()
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(ctx aoc.Context) (any, error) {
input, err := ctx.BodyString()
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(ctx aoc.Context) (any, error) {
scanner := &day2Scanner{scanner: ctx.Scanner(aoc.SplitComma), sep: '-'}
var sum int
for scanner.Scan() {
start, end := scanner.Bounds()
ctx.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) {
ctx.Println("found ", i)
sum += i
}
}
}
}
return sum, scanner.Err()
}
func Day2Part2(ctx aoc.Context) (any, error) {
scanner := &day2Scanner{scanner: ctx.Scanner(aoc.SplitComma), sep: '-'}
var sum int
for scanner.Scan() {
start, end := scanner.Bounds()
ctx.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 {
ctx.Println("found ", i)
sum += i
break
}
}
}
}
return sum, scanner.Err()
}