This repository has been archived by the owner on Nov 22, 2024. It is now read-only.
generated from mazharenko/aoc-agent-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday20.fs
130 lines (118 loc) · 5.16 KB
/
day20.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
module impl.day20
open Farkle
open Farkle.Builder
open Farkle.Builder.Regex
type Pulse = | Low | High
type FlipFlop = { State: bool }
type Conjunction = { Inputs: Map<string, Pulse> }
type ModuleType =
| Broadcast
| FlipFlop of FlipFlop
| Conjunction of Conjunction
type Module = { Name: string; To: string list; Type: ModuleType; }
let parse input =
let moduleName =
regexString "[a-z]+" |> terminal "name" (T(fun _ s -> new string(s)))
let moduleHeader = "header" ||= [
!& "%" .>>. moduleName => fun name -> FlipFlop { State = false }, name
!& "&" .>>. moduleName => fun name -> Conjunction { Inputs = Map.empty }, name
!& "broadcaster" =% (Broadcast, "broadcaster")
]
let ``module`` = "module" ||= [
!@ moduleHeader .>> "->" .>>. (sepBy1 (literal ",") moduleName)
=> fun (t, name) toList -> { Name = name; Type = t; To = toList }
]
let parser = RuntimeFarkle.build ``module``
let modules =
input
|> Pattern1.read (RuntimeFarkle.parseUnsafe parser)
|> Seq.map (fun m -> m.Name, m)
|> Map.ofSeq
modules
|> Map.map (fun name m ->
match m.Type with
| Conjunction _ ->
{
m with Type =
Conjunction {
Inputs = modules
|> Map.filter (fun _ m -> m.To |> List.contains name)
|> Map.map (fun _ _ -> Low)
}
}
| _ -> m
)
type private State = { Modules: Map<string, Module>; Pulses: (string * string * Pulse) list; HighSentTo: string list; LowSentTo: string list }
module private State =
let queuePulses pulses state =
let high, low = List.partition (fun (_, _, pulse) -> pulse = High) pulses
{
Modules = state.Modules
Pulses = state.Pulses @ pulses
HighSentTo = (high |> List.map (fun (_, x, _) -> x)) @ state.HighSentTo
LowSentTo = (low |> List.map (fun (_, x, _) -> x)) @ state.LowSentTo
}
let queuePulsesTo source destinations pulse =
queuePulses (destinations |> List.map(fun dest -> dest, source, pulse))
let wasHighSentTo ``module`` state =
state.HighSentTo |> List.contains ``module``
let rec private run state =
match state.Pulses with
| [] -> state
| (currentModule, moduleFrom, pulse)::ps ->
match state.Modules |> Map.tryFind currentModule with
| None -> run { state with Pulses = ps }
| Some targetModule ->
match targetModule.Type, pulse with
| Broadcast, _ ->
{ state with Pulses = ps }
|> State.queuePulsesTo currentModule targetModule.To pulse
|> run
| FlipFlop _, High -> run { state with Pulses = ps }
| FlipFlop { State = flipFlopState }, Low ->
{
state with Modules = Map.add currentModule { targetModule with Type = FlipFlop { State = not flipFlopState }} state.Modules
Pulses = ps
} |> State.queuePulsesTo currentModule targetModule.To (if not flipFlopState then High else Low)
|> run
| Conjunction { Inputs = inputs }, _ ->
let newInputsCache = inputs |> Map.add moduleFrom pulse
let allHigh = newInputsCache |> Map.forall (fun _ value -> value = High)
{
state with Modules = state.Modules |> Map.add currentModule { targetModule with Type = Conjunction { Inputs = newInputsCache }}
Pulses = ps
} |> State.queuePulsesTo currentModule targetModule.To (if allHigh then Low else High)
|> run
let buttonPulse = "broadcaster", "button", Low
let solve1 input =
let initialState = { Modules = input; Pulses = []; HighSentTo = []; LowSentTo = [] }
let {HighSentTo = highs; LowSentTo = lows} =
seq {1..1000}
|> Seq.fold (fun state _ -> state |> State.queuePulses [buttonPulse] |> run ) initialState
highs.Length * lows.Length
let solve2 input =
let initialState = { Modules = input; Pulses = []; HighSentTo = []; LowSentTo = [] }
// rx receives Low when all rxSources receive High
// assume each of them does that every nth button press, and n is relatively small
let toRx = input |> Map.findKey (fun _ value -> value.To |> List.contains "rx")
let rxSources =
input |> Map.toSeq |> Seq.filter (fun (_, value) -> value.To |> List.contains toRx) |> Seq.map fst
|> Seq.toArray
Seq.initInfinite ((+)1)
|> Seq.scan (fun (_,state) i ->
let newState = {state with HighSentTo = []; LowSentTo = []} |> State.queuePulses [buttonPulse] |> run
i, newState
) (0, initialState)
|> Seq.scan (fun m (i, s) ->
rxSources
|> Seq.fold (fun m source ->
if not <| Map.containsKey source m && State.wasHighSentTo source s
then m |> Map.add source i
else m
) m
) Map.empty
|> Seq.filter (Map.count >> (=)4)
|> Seq.head
|> Map.values
|> Seq.map int64
|> Seq.reduce lcm