package main import ( "bufio" "fmt" "log" "os" "regexp" "strconv" "strings" ) func proccessLines(path string, f func(string) (stop bool, err error)) error { file, err := os.Open(path) if err != nil { return err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { stop, err := f(scanner.Text()) if stop || err != nil { return err } } if err := scanner.Err(); err != nil { return err } return nil } func between(low, mid, high int) bool { return low <= mid && mid <= high } type validator = func(v string) bool var heightMatcher = regexp.MustCompile(`^([0-9]+)(cm|in)$`) func heightValidator(v string) bool { result := heightMatcher.FindStringSubmatch(v) if result == nil { return false } value, err := strconv.Atoi(result[1]) if err != nil { return false } switch result[2] { case "cm": return between(150, value, 193) case "in": return between(59, value, 76) } return false } func betweenValidator(low, high int) validator { return func(v string) bool { val, err := strconv.Atoi(v) if err != nil { return false } return between(low, val, high) } } func valueInValidator(valid ...string) validator { validValues := map[string]bool{} for _, value := range valid { validValues[value] = true } return func(v string) bool { _, ok := validValues[v] return ok } } var requiredKeys = map[string]validator{ "byr": betweenValidator(1920, 2002), "iyr": betweenValidator(2010, 2020), "eyr": betweenValidator(2020, 2030), "hgt": heightValidator, "hcl": regexp.MustCompile(`^#[0-9a-f]{6}$`).MatchString, "ecl": valueInValidator("amb", "blu", "brn", "gry", "grn", "hzl", "oth"), "pid": regexp.MustCompile(`^[0-9]{9}$`).MatchString, } func parseKeys(text string) map[string]string { d := map[string]string{} for _, kvp := range strings.Split(text, " ") { parts := strings.Split(kvp, ":") d[parts[0]] = parts[1] } return d } func validateKeys(d map[string]string) bool { for key := range requiredKeys { if _, ok := d[key]; !ok { return false } } return true } func validateValues(d map[string]string) bool { for key, f := range requiredKeys { var v string var ok bool if v, ok = d[key]; !ok { return false } if !f(v) { return false } } return true } func main() { validKeys, validValues, total := 0, 0, 0 buffer := []string{} processBuffer := func(buffer []string) { d := parseKeys(strings.Join(buffer, " ")) if validateKeys(d) { validKeys++ } if validateValues(d) { validValues++ } total++ } err := proccessLines("input.txt", func(line string) (stop bool, err error) { if line == "" { processBuffer(buffer) buffer = []string{} } else { buffer = append(buffer, line) } return }) if err != nil { log.Fatal(err) } processBuffer(buffer) fmt.Printf("Total: %d, Valid keys: %d, Valid values: %d\n", total, validKeys, validValues) }