224 lines
4.3 KiB
Go
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()
|
|
}
|