-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day19.fs
112 lines (97 loc) · 2.73 KB
/
Day19.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
module Day19
open FSharpx.Collections
type Id = int
type Rule =
| Character of char
| Sequence of Rule LazyList
| Disjunction of Rule LazyList
type Rules = (Id, Rule) Map
type CheckResult =
| Success
| More of Rule
let rec check char rule =
seq {
match rule with
| Character c ->
if char = c then yield Success
| Sequence none when none.IsEmpty ->
yield Success
| Sequence rule when rule.Tail.IsEmpty ->
yield! check char rule.Head
| Sequence rules ->
yield! check char rules.Head
|> Seq.map (
function
| Success -> More (Sequence rules.Tail)
| More r -> More (Sequence (rules.Tail |> LazyList.cons r)))
| Disjunction rules ->
yield! rules |> Seq.collect (check char)
}
let isMatch rule =
let rec loop results =
function
| [] ->
results |> Seq.contains Success
| char :: rest ->
results |> Seq.exists (
function
| More rule -> loop (check char rule) rest
| _ -> false)
loop (Seq.singleton (More rule))
module Parse =
open FSharpPlus
type Definition =
| Rule of Rule
| RefSequence of int list
| DefDisjunction of Definition * Definition
let rec walk env =
let find r = Map.find r env
function
| Rule x ->
x
| DefDisjunction (r1, r2) ->
Disjunction (seq { walk env r1; walk env r2 } |> LazyList.ofSeq)
| RefSequence [ref] ->
find ref |> walk env
| RefSequence refs ->
refs
|> Seq.map (find >> walk env)
|> LazyList.ofSeq
|> Sequence
let parseSequence =
String.split (Seq.singleton " ")
>> Seq.map int
>> Seq.toList
>> RefSequence
open System.Text.RegularExpressions
let parseRuleLine input =
let regex = Regex.Match (input, "^(\d+)\: (\"([a-z])\"|(((\d+ ?)+)( \| ((\d+ ?)+))*))$")
let id = regex.Groups.[1].Value |> int
let char = regex.Groups.[3].Value
let first = regex.Groups.[5].Value
let second = regex.Groups.[8].Value
id,
let (isInt, _) = System.Int32.TryParse char
if not isInt && char.Length > 0 then Rule (Character char.[0])
elif second = "" then parseSequence first
else DefDisjunction (parseSequence first, parseSequence second)
let parseRules : _ seq -> _ =
Seq.map parseRuleLine
>> Map.ofSeq
>> fun m -> m |> Map.find 0 |> walk m
let parseMessage = Seq.toList
let parse input =
let sets = Regex.Split (input, "\r\n\r\n")
Regex.Split (sets.[0], "\r\n") |> parseRules,
Regex.Split (sets.[1], "\r\n") |> Seq.map parseMessage
let solve (rule, messages) =
messages
|> Seq.filter (isMatch rule)
|> Seq.length
open System
open System.IO
let run () =
File.ReadAllText "input/Day19.txt"
|> Parse.parse
|> solve
|> Console.WriteLine