aoc-2020/d04/main.go

150 lines
2.8 KiB
Go

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