day 4, with an arbitrary constraint: I didn't want to use regexes.
This commit is contained in:
parent
8a9cb3291a
commit
d0a11045fd
247
04/main.go
Normal file
247
04/main.go
Normal file
@ -0,0 +1,247 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
partOne()
|
||||
partTwo()
|
||||
}
|
||||
|
||||
type passport struct {
|
||||
byr string //(Birth Year)
|
||||
iyr string //(Issue Year)
|
||||
eyr string //(Expiration Year)
|
||||
hgt string //(Height)
|
||||
hcl string //(Hair Color)
|
||||
ecl string //(Eye Color)
|
||||
pid string //(Passport ID)
|
||||
cid string //(Country ID)
|
||||
}
|
||||
|
||||
func addToPassport(p *passport, i []string) {
|
||||
if i[0] == "byr" {
|
||||
p.byr = i[1]
|
||||
}
|
||||
if i[0] == "iyr" {
|
||||
p.iyr = i[1]
|
||||
}
|
||||
if i[0] == "eyr" {
|
||||
p.eyr = i[1]
|
||||
}
|
||||
if i[0] == "hgt" {
|
||||
p.hgt = i[1]
|
||||
}
|
||||
if i[0] == "hcl" {
|
||||
p.hcl = i[1]
|
||||
}
|
||||
if i[0] == "ecl" {
|
||||
p.ecl = i[1]
|
||||
}
|
||||
if i[0] == "pid" {
|
||||
p.pid = i[1]
|
||||
}
|
||||
if i[0] == "cid" {
|
||||
p.cid = i[1]
|
||||
}
|
||||
}
|
||||
|
||||
func (p passport) IsPassport() bool {
|
||||
return p.byr != "" && p.iyr != "" && p.eyr != "" && p.hgt != "" && p.hcl != "" && p.ecl != "" && p.pid != "" && p.cid != ""
|
||||
}
|
||||
|
||||
func (p passport) IsCredentials() bool {
|
||||
return p.byr != "" && p.iyr != "" && p.eyr != "" && p.hgt != "" && p.hcl != "" && p.ecl != "" && p.pid != "" && p.cid == ""
|
||||
}
|
||||
|
||||
func (p passport) IsValid() bool {
|
||||
// byr (Birth Year) - four digits; at least 1920 and at most 2002.
|
||||
i, err := strconv.Atoi(p.byr)
|
||||
if err != nil || i < 1920 || i > 2002 {
|
||||
return false
|
||||
}
|
||||
// iyr (Issue Year) - four digits; at least 2010 and at most 2020.
|
||||
i, err = strconv.Atoi(p.iyr)
|
||||
if err != nil || i < 2010 || i > 2020 {
|
||||
return false
|
||||
}
|
||||
// eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
|
||||
i, err = strconv.Atoi(p.eyr)
|
||||
if err != nil || i < 2020 || i > 2030 {
|
||||
return false
|
||||
}
|
||||
// hgt (Height) - a number followed by either cm or in:
|
||||
// If cm, the number must be at least 150 and at most 193.
|
||||
// If in, the number must be at least 59 and at most 76.
|
||||
i, j, err := parseHeight(p.hgt)
|
||||
if err != nil || (j != "in" && j != "cm") || (j == "in" && (i < 59 || i > 76)) || (j == "cm" && (i < 150 || i > 193)) {
|
||||
return false
|
||||
}
|
||||
// hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f.
|
||||
j, err = parseColor(p.hcl)
|
||||
if err != nil || len(j) > 6 || len(j) < 1 {
|
||||
return false
|
||||
}
|
||||
// ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth.
|
||||
if !(p.ecl == "amb" || p.ecl == "blu" || p.ecl == "brn" || p.ecl == "gry" || p.ecl == "grn" || p.ecl == "hzl" || p.ecl == "oth") {
|
||||
return false
|
||||
}
|
||||
// pid (Passport ID) - a nine-digit number, including leading zeroes.
|
||||
j, err = parsePassportID(p.pid)
|
||||
if err != nil || len(j) != 9 {
|
||||
return false
|
||||
}
|
||||
// cid (Country ID) - ignored, missing or not.
|
||||
return true
|
||||
}
|
||||
|
||||
func parseHeight(s string) (int, string, error) {
|
||||
var measurement, unit []rune
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case r == 'c' || r == 'm' || r == 'i' || r == 'n':
|
||||
unit = append(unit, r)
|
||||
case r >= '0' && r <= '9':
|
||||
measurement = append(measurement, r)
|
||||
default:
|
||||
return 0, "", fmt.Errorf("invalid")
|
||||
}
|
||||
}
|
||||
m, err := strconv.Atoi(string(measurement))
|
||||
return m, string(unit), err
|
||||
}
|
||||
|
||||
func parseColor(s string) (string, error) {
|
||||
var color []rune
|
||||
for i, r := range s {
|
||||
switch {
|
||||
case (r == '#' && i != 0) || (r != '#' && i == 0):
|
||||
return "", fmt.Errorf("invalid or missing #")
|
||||
case r == '#' && i == 0:
|
||||
continue
|
||||
case (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f'):
|
||||
color = append(color, r)
|
||||
default:
|
||||
return "", fmt.Errorf("invalid rune")
|
||||
}
|
||||
}
|
||||
return string(color), nil
|
||||
}
|
||||
|
||||
func parsePassportID(s string) (string, error) {
|
||||
var number []rune
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case (r >= '0' && r <= '9'):
|
||||
number = append(number, r)
|
||||
default:
|
||||
return "", fmt.Errorf("invalid")
|
||||
}
|
||||
}
|
||||
return string(number), nil
|
||||
}
|
||||
|
||||
// Passport data is validated in batch files (your puzzle input).
|
||||
// Each passport is represented as a sequence of key:value pairs separated by spaces or newlines.
|
||||
// Passports are separated by blank lines.
|
||||
// Here is an example batch file containing four passports:
|
||||
//
|
||||
// ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
|
||||
// byr:1937 iyr:2017 cid:147 hgt:183cm
|
||||
//
|
||||
// iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
|
||||
// hcl:#cfa07d byr:1929
|
||||
//
|
||||
// hcl:#ae17e1 iyr:2013
|
||||
// eyr:2024
|
||||
// ecl:brn pid:760753108 byr:1931
|
||||
// hgt:179cm
|
||||
//
|
||||
// hcl:#cfa07d eyr:2025 pid:166559648
|
||||
// iyr:2011 ecl:brn hgt:59in
|
||||
//
|
||||
// The first passport is valid - all eight fields are present.
|
||||
// The second passport is invalid - it is missing hgt (the Height field).
|
||||
// The third passport is interesting; the only missing field is cid, so it looks like data from North Pole Credentials, not a passport at all!
|
||||
// Surely, nobody would mind if you made the system temporarily ignore missing cid fields. Treat this "passport" as valid.
|
||||
// The fourth passport is missing two fields, cid and byr.
|
||||
// Missing cid is fine, but missing any other field is not, so this passport is invalid.
|
||||
// According to the above rules, your improved system would report 2 valid passports.
|
||||
// Count the number of valid passports - those that have all required fields.
|
||||
// Treat cid as optional. In your batch file, how many passports are valid?
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
valid := 0
|
||||
current := passport{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
if current.IsCredentials() || current.IsPassport() {
|
||||
valid = valid + 1
|
||||
}
|
||||
current = passport{}
|
||||
} else {
|
||||
for _, entry := range strings.Split(line, " ") {
|
||||
addToPassport(¤t, strings.Split(entry, ":"))
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally, check the final entry
|
||||
if current.IsCredentials() || current.IsPassport() {
|
||||
valid = valid + 1
|
||||
}
|
||||
|
||||
fmt.Println(valid)
|
||||
}
|
||||
|
||||
// You can continue to ignore the cid field, but each other field has strict rules about what values are valid for automatic validation:
|
||||
// byr (Birth Year) - four digits; at least 1920 and at most 2002.
|
||||
// iyr (Issue Year) - four digits; at least 2010 and at most 2020.
|
||||
// eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
|
||||
// hgt (Height) - a number followed by either cm or in:
|
||||
// If cm, the number must be at least 150 and at most 193.
|
||||
// If in, the number must be at least 59 and at most 76.
|
||||
// hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f.
|
||||
// ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth.
|
||||
// pid (Passport ID) - a nine-digit number, including leading zeroes.
|
||||
// cid (Country ID) - ignored, missing or not.
|
||||
// Your job is to count the passports where all required fields are both present and valid according to the above rules.
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
valid := 0
|
||||
current := passport{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
if current.IsValid() {
|
||||
valid = valid + 1
|
||||
}
|
||||
current = passport{}
|
||||
} else {
|
||||
for _, entry := range strings.Split(line, " ") {
|
||||
addToPassport(¤t, strings.Split(entry, ":"))
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally, check the final entry
|
||||
if current.IsValid() {
|
||||
valid = valid + 1
|
||||
}
|
||||
|
||||
fmt.Println(valid)
|
||||
}
|
Loading…
Reference in New Issue
Block a user