123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- package main
- import (
- "bufio"
- "fmt"
- "log"
- "os"
- "strconv"
- "strings"
- )
- type rule struct {
- value string
- mapping [][]int
- }
- var rules map[int]rule
- func init() {
- rules = make(map[int]rule)
- }
- func readRule(line string) {
- var newRule rule
- parts := strings.Split(line, ": ")
- if len(parts) != 2 {
- log.Fatalf("Invalid line: %s", line)
- }
- id, err := strconv.Atoi(parts[0])
- if err != nil {
- log.Fatalf("Error processing rule id for %s: %s", line, err)
- }
- var currentMapping []int
- for _, item := range strings.Split(parts[1], " ") {
- if strings.Contains(item, "\"") {
- newRule.value = strings.ReplaceAll(item, "\"", "")
- break
- }
- if item == "|" {
- newRule.mapping = append(newRule.mapping, currentMapping)
- currentMapping = []int{}
- continue
- }
- itemID, err := strconv.Atoi(item)
- if err != nil {
- log.Fatalf("Error processing id for %s: %s", item, err)
- }
- currentMapping = append(currentMapping, itemID)
- }
- newRule.mapping = append(newRule.mapping, currentMapping)
- rules[id] = newRule
- }
- var messages []string
- func readFile(file *os.File) {
- scanner := bufio.NewScanner(file)
- currentFunction := readRule
- changed := false
- for scanner.Scan() {
- line := scanner.Text()
- if line == "" {
- if changed {
- break
- }
- currentFunction = func(line string) { messages = append(messages, line) }
- changed = true
- continue
- }
- currentFunction(line)
- }
- if err := scanner.Err(); err != nil {
- log.Fatalf("Scanner error: %s", err)
- }
- }
- func buildStringsForRule(ruleID int, stringsSoFar []string) []string {
- var newStrings []string
- if rules[ruleID].value != "" {
- for _, item := range stringsSoFar {
- item += rules[ruleID].value
- newStrings = append(newStrings, item)
- }
- return newStrings
- }
- for _, item := range stringsSoFar {
- for _, array := range rules[ruleID].mapping {
- partial := []string{item}
- for _, id := range array {
- partial = buildStringsForRule(id, partial)
- }
- newStrings = append(newStrings, partial...)
- }
- }
- return newStrings
- }
- var notMatched []string
- func part1(validStrings []string) int {
- valid := 0
- for _, message := range messages {
- matched := false
- for _, item := range validStrings {
- if message == item {
- valid++
- matched = true
- break
- }
- }
- if !matched {
- notMatched = append(notMatched, message)
- }
- }
- return valid
- }
- func startsWith(toCheck []string, message string) bool {
- for _, item := range toCheck {
- if strings.HasPrefix(message, item) {
- return true
- }
- }
- return false
- }
- func endsWith(toCheck []string, message string) bool {
- for _, item := range toCheck {
- if strings.HasSuffix(message, item) {
- return true
- }
- }
- return false
- }
- func part2(max int) int {
- valid := 0
- thirtyOne := buildStringsForRule(31, []string{""})
- fortyTwo := buildStringsForRule(42, []string{""})
- length := len(thirtyOne[0])
- currentLength := 3 * length
- for len(notMatched) > 0 && currentLength <= max {
- currentLength += length
- var toCheck []string
- for _, message := range notMatched {
- if len(message)%currentLength == 0 {
- if startsWith(fortyTwo, message) && endsWith(thirtyOne, message) {
- valid++
- } else {
- toCheck = append(toCheck, message)
- }
- } else {
- toCheck = append(toCheck, message)
- }
- }
- notMatched = toCheck
- }
- return valid
- }
- func longest() int {
- max := 0
- for _, message := range notMatched {
- length := len(message)
- if length > max {
- max = length
- }
- }
- return max
- }
- func main() {
- if len(os.Args) < 2 {
- log.Fatal("You need to specify a file!")
- }
- filePath := os.Args[1]
- file, err := os.Open(filePath)
- if err != nil {
- log.Fatalf("Failed to open %s!\n", filePath)
- }
- readFile(file)
- if err := file.Close(); err != nil {
- log.Fatalf("Failed to close file: %s", err)
- }
- validStrings := buildStringsForRule(0, []string{""})
- resultPart1 := part1(validStrings)
- fmt.Println("Part1:", resultPart1)
- max := longest()
- fmt.Println("Part2:", resultPart1+part2(max))
- }
|