From 43b78620ca36245b66c61db6b7e8e17319d1ea2f Mon Sep 17 00:00:00 2001 From: Kharec Date: Mon, 24 Nov 2025 18:49:12 +0100 Subject: [PATCH] feat: add main code --- 2020/day04/main.go | 159 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 2020/day04/main.go diff --git a/2020/day04/main.go b/2020/day04/main.go new file mode 100644 index 0000000..db47628 --- /dev/null +++ b/2020/day04/main.go @@ -0,0 +1,159 @@ +package main + +import ( + "fmt" + "log" + "os" + "strconv" + "strings" +) + +func parseInput(file string) []string { + content, err := os.ReadFile(file) + if err != nil { + log.Fatalf("Failed to read input file: %v", err) + } + + 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 +} + +func main() { + data := parseInput("input.txt") + fmt.Println("Part 1:", PartOne(data)) + fmt.Println("Part 2:", PartTwo(data)) +}