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) }