Skip to content

Instantly share code, notes, and snippets.

@object
Created December 4, 2020 06:23
Show Gist options
  • Select an option

  • Save object/c51799df803e8f4210a0d1a2bc435cb7 to your computer and use it in GitHub Desktop.

Select an option

Save object/c51799df803e8f4210a0d1a2bc435cb7 to your computer and use it in GitHub Desktop.
AdventOfCode 2020, December 4
open System
open System.IO
let input =
File.ReadAllLines("input04.txt")
|> Seq.toList
let expected_fields_without_country = ["byr"; "iyr"; "eyr"; "hgt"; "hcl"; "ecl"; "pid"] |> Set.ofList
let expected_fields = expected_fields_without_country |> Set.add "cid"
let isNumberBetween min max (str:string) =
match Int32.TryParse str with
| true, n -> n >= min && n <= max
| _ -> false
let isValidField name (value:string) =
match name with
| "byr" -> isNumberBetween 1920 2002 value
| "iyr" -> isNumberBetween 2010 2020 value
| "eyr" -> isNumberBetween 2020 2030 value
| "hgt" -> if value.EndsWith "cm" then isNumberBetween 150 193 (value.Substring(0,value.Length-2)) else if value.EndsWith "in" then isNumberBetween 59 76 (value.Substring(0,value.Length-2)) else false
| "hcl" -> value.Length = 7 && value.[0] = '#' && value |> Seq.skip 1 |> Seq.forall (fun x -> x >= '0' && x <= '9' || x >= 'a' && x <= 'f')
| "ecl" -> ["amb"; "blu"; "brn"; "gry"; "grn"; "hzl"; "oth"] |> List.contains value
| "pid" -> value.Length = 9 && value |> Seq.forall Char.IsDigit
| "cid" -> true
| _ -> false
let passports =
input
|> List.fold (fun (all_passports,current_passport) (line:string) ->
if String.IsNullOrEmpty line then
current_passport :: all_passports,Map.empty
else
let fields = line.Split(' ') |> Array.map (fun x -> x.Split(':'))
let current_passport = fields |> Array.fold (fun acc field -> acc |> Map.add field.[0] field.[1]) current_passport
all_passports,current_passport
) (List.empty,Map.empty)
|> fun (xs,x) -> if not (x |> Map.isEmpty) then x :: xs else xs
let hasRequiredFields passport =
passport
|> Map.toList
|> List.map fst
|> Set.ofList
|> fun x -> x = expected_fields || x = expected_fields_without_country
let hasValidFields passport =
passport
|> Map.toList
|> List.fold (fun acc field -> if acc then acc && isValidField (fst field) (snd field) else false) true
let answer =
passports
|> List.filter (fun passport -> hasRequiredFields passport && hasValidFields passport)
|> List.length
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment