150 lines
2.8 KiB
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)
|
|
}
|