aoc2025/day2/main.go

202 lines
3.9 KiB
Go
Raw Normal View History

2025-12-05 12:48:02 -05:00
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func readLines(filename string) []string {
raw, err := os.ReadFile(filename)
if err != nil {
panic(err)
}
return strings.Split(string(raw), "\n")
}
type id struct {
value uint64
length uint8
}
func (x id) isSilly() bool {
c := uint64(1)
for range x.length / 2 {
c *= 10
}
return x.value/c == x.value%c
}
type pattern struct {
length uint8
repeats uint8
}
var patterns = map[uint8][]pattern{
1: {},
2: {pattern{length: 1, repeats: 2}},
3: {pattern{length: 1, repeats: 3}},
4: {pattern{length: 1, repeats: 4}, pattern{length: 2, repeats: 2}},
5: {pattern{length: 1, repeats: 5}},
6: {pattern{length: 1, repeats: 6}, pattern{length: 2, repeats: 3}, pattern{length: 3, repeats: 2}},
7: {pattern{length: 1, repeats: 7}},
8: {pattern{length: 1, repeats: 8}, pattern{length: 2, repeats: 4}, pattern{length: 4, repeats: 2}},
9: {pattern{length: 1, repeats: 9}, pattern{length: 3, repeats: 3}},
10: {pattern{length: 1, repeats: 10}, pattern{length: 5, repeats: 2}, pattern{length: 2, repeats: 5}},
11: {pattern{length: 1, repeats: 11}},
}
func (x id) isVerySilly() bool {
ps := patterns[x.length]
if ps == nil {
panic(x.length)
}
PATTERNS:
for _, pattern := range ps {
mod := uint64(1)
for range pattern.length {
mod *= 10
}
value := x.value
candidate := value % mod
if candidate == 0 {
continue
}
for range pattern.repeats - 1 {
value /= mod
if value%mod != candidate {
continue PATTERNS
}
}
return true
}
return false
}
type seq struct {
start id
stop id
}
func parseLine(line string) ([]seq, error) {
parts := strings.Split(line, ",")
seqs := make([]seq, len(parts))
for i, part := range strings.Split(line, ",") {
pieces := strings.Split(part, "-")
if len(pieces) != 2 {
return seqs, fmt.Errorf("invalid part")
}
var err error
s := seq{
start: id{length: uint8(len(pieces[0]))},
stop: id{length: uint8(len(pieces[1]))},
}
s.start.value, err = strconv.ParseUint(pieces[0], 10, 64)
if err != nil {
return seqs, err
}
s.stop.value, err = strconv.ParseUint(pieces[1], 10, 64)
if err != nil {
return seqs, err
}
if s.stop.value < s.start.value {
return seqs, fmt.Errorf("invalid range")
}
if !(s.stop.length == s.start.length || s.stop.length == s.start.length+1) {
return seqs, fmt.Errorf("unexpectedly large range %v", s)
}
seqs[i] = s
}
return seqs, nil
}
func findSillyPatterns(s seq) []uint64 {
sillies := []uint64{}
oddStart := s.start.length%2 == 1
oddStop := s.stop.length%2 == 1
if oddStart && oddStop {
return sillies
}
if oddStart {
s.start.value = 1
s.start.length++
for range s.start.length - 1 {
s.start.value *= 10
}
}
if oddStop {
s.stop.value = 0
s.stop.length--
c := 1
for range s.stop.length {
s.stop.value += uint64(9 * c)
c *= 10
}
}
if s.stop.value < s.start.value {
return sillies
}
for {
if s.start.value > s.stop.value {
break
}
if s.start.isSilly() {
sillies = append(sillies, s.start.value)
}
s.start.value++
}
return sillies
}
func findVerySillyPatterns(s seq) []uint64 {
sillies := []uint64{}
pivot := uint64(1)
for range s.stop.length - 1 {
pivot *= 10
}
for {
if s.start.value > s.stop.value {
break
}
if s.start.isVerySilly() {
sillies = append(sillies, s.start.value)
}
s.start.value++
if s.start.value == pivot {
s.start.length++
}
}
return sillies
}
func main() {
filename := "part0.txt"
if len(os.Args) > 1 {
filename = os.Args[1]
}
lines := readLines(filename)
if len(lines) != 1 {
panic("Invalid lines")
}
seqs, err := parseLine(lines[0])
if err != nil {
panic(err)
}
total := uint64(0)
for _, seq := range seqs {
for _, value := range findSillyPatterns(seq) {
total += value
}
}
fmt.Printf("%v\n", total)
total = uint64(0)
for _, seq := range seqs {
for _, value := range findVerySillyPatterns(seq) {
total += value
}
}
fmt.Printf("%v\n", total)
}