package dayfour import ( "advent-of-code/internal/registry" "os" "strconv" "strings" ) func init() { registry.Register("2020D4", ParseInput, PartOne, PartTwo) } func ParseInput(filepath string) []string { content, _ := os.ReadFile(filepath) text := string(content) passports := strings.Split(text, "\n\n") result := make([]string, 0, len(passports)) for _, passport := range passports { passport = strings.TrimSpace(passport) if passport == "" { continue } passport = strings.ReplaceAll(passport, "\n", " ") passport = strings.Join(strings.Fields(passport), " ") result = append(result, passport) } return result } func PartOne(data []string) int { count := 0 required := []string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"} for _, passport := range data { fields := strings.Fields(passport) present := make(map[string]bool) for _, field := range fields { if idx := strings.Index(field, ":"); idx != -1 { present[field[:idx]] = true } } missing := false for _, field := range required { if !present[field] { missing = true break } } if !missing { count++ } } return count } func PartTwo(data []string) int { isNumberInRange := func(value string, min, max int, digits int) bool { if len(value) != digits { return false } n, err := strconv.Atoi(value) if err != nil { return false } return n >= min && n <= max } isValidHeight := func(value string) bool { if len(value) < 3 { return false } unit := value[len(value)-2:] number := value[:len(value)-2] n, err := strconv.Atoi(number) if err != nil { return false } if unit == "cm" { return n >= 150 && n <= 193 } if unit == "in" { return n >= 59 && n <= 76 } return false } isValidHairColor := func(value string) bool { if len(value) != 7 || value[0] != '#' { return false } for _, c := range value[1:] { if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) { return false } } return true } isValidPID := func(value string) bool { if len(value) != 9 { return false } for _, c := range value { if c < '0' || c > '9' { return false } } return true } validECL := map[string]bool{ "amb": true, "blu": true, "brn": true, "gry": true, "grn": true, "hzl": true, "oth": true, } required := []string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"} count := 0 for _, passport := range data { fields := map[string]string{} for field := range strings.FieldsSeq(passport) { parts := strings.SplitN(field, ":", 2) if len(parts) == 2 { fields[parts[0]] = parts[1] } } passportValidators := map[string]func(string) bool{ "byr": func(v string) bool { return isNumberInRange(v, 1920, 2002, 4) }, "iyr": func(v string) bool { return isNumberInRange(v, 2010, 2020, 4) }, "eyr": func(v string) bool { return isNumberInRange(v, 2020, 2030, 4) }, "hgt": isValidHeight, "hcl": isValidHairColor, "ecl": func(v string) bool { return validECL[v] }, "pid": isValidPID, } valid := true for _, key := range required { value, ok := fields[key] if !ok || !passportValidators[key](value) { valid = false break } } if valid { count++ } } return count }