-
Notifications
You must be signed in to change notification settings - Fork 0
/
day17.fsx
149 lines (119 loc) · 4.11 KB
/
day17.fsx
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#time
let input = System.IO.File.ReadAllText("day17.input")
let jets =
input
|> Seq.map (function
| '<' -> (-1, 0)
| '>' -> (1, 0))
|> Seq.toArray
let jetsMod i = i % jets.Length
let jetsInfinite i = jets[i % jets.Length]
let blocks =
[| set [ 0, 0; 1, 0; 2, 0; 3, 0 ]
set [ 1, 0; 0, 1; 1, 1; 2, 1; 1, 2 ]
set [ 2, 0; 2, 1; 0, 2; 1, 2; 2, 2 ]
set [ 0, 0; 0, 1; 0, 2; 0, 3 ]
set [ 0, 0; 1, 0; 0, 1; 1, 1 ] |]
let blocksMod i = i % blocks.Length
let blocksInfinite i = blocks[i % blocks.Length]
let posPlus (a, b) (c, d) = a + c, b + d
let moveBlock dir block = block |> Set.map (posPlus dir)
let minX xs = xs |> Seq.map fst |> Seq.min
let maxX xs = xs |> Seq.map fst |> Seq.max
let minY xs = xs |> Seq.map snd |> Seq.min
let maxY xs = xs |> Seq.map snd |> Seq.max
let initBlock b cave =
let height = if Seq.isEmpty cave then 0 else minY cave
moveBlock (2, height - 4 - maxY b) b
type State =
{ Step: int
BlockNumber: int
Block: Set<int * int> option
Cave: Set<int * int> }
let simStep
endCondition
({ Step = i
BlockNumber = j
Block = block
Cave = cave } as s)
=
if endCondition s then
None
else
match block with
| None ->
let topRowToKeep =
[ 0..6 ]
|> Seq.map (fun i -> cave |> Seq.filter (fun (x, _) -> x = i) |> Seq.map snd |> Seq.min)
|> Seq.max
let topRow = cave |> Seq.map snd |> Seq.min
let i = s.Step % jets.Length
let j = s.BlockNumber % blocks.Length
if i = 2 && j = 0 then
printfn
"%A"
(topRow - topRowToKeep,
topRowToKeep,
s.Step,
s.Step % jets.Length,
s.BlockNumber,
s.BlockNumber % blocks.Length)
let cave = cave |> Set.filter (fun (_, y) -> y <= topRowToKeep)
let b = initBlock (blocksInfinite j) cave
Some({ s with Block = Some b; Cave = cave })
| Some b ->
let jet = jetsInfinite i
let b =
let b' = moveBlock jet b
if minX b' < 0 || maxX b' > 6 || Set.intersect b' cave |> Seq.isEmpty |> not then
b
else
b'
let b' = moveBlock (0, 1) b
if Set.intersect b' cave |> Seq.isEmpty then
Some({ s with Block = Some b'; Step = i + 1 })
else
let s' =
{ s with
Block = None
BlockNumber = j + 1
Step = i + 1
Cave = Set.union cave b }
Some(s')
|> Option.map (fun x -> x, x)
let initState =
{ Step = 0
BlockNumber = 0
Block = None
Cave = set [ for i in 0..6 -> i, 0 ] }
let sim endCondition =
Seq.unfold (simStep endCondition) initState
let part1 =
sim (fun s -> s.BlockNumber >= 2022) |> Seq.last |> (fun s -> minY s.Cave)
printfn $"PART1: {-part1}"
let part2 () =
let target = 1000000000000L
let groups =
sim (fun s -> s.BlockNumber >= 20000)
|> Seq.filter (fun s -> s.BlockNumber >= 1000 && blocksMod s.BlockNumber = 0)
|> Seq.groupBy (fun s -> jetsMod s.Step)
|> Seq.map snd
|> Seq.map (Seq.distinctBy (fun s -> s.BlockNumber))
let g =
groups
|> Seq.filter (fun g ->
(g
|> Seq.pairwise
|> Seq.last
|> fun (a, b) -> (target - int64 b.BlockNumber) % (int64 b.BlockNumber - int64 a.BlockNumber) = 0L))
|> Seq.head
let (step, blockStep) =
g
|> Seq.map (fun s -> minY s.Cave, s.BlockNumber)
|> Seq.pairwise
|> Seq.map (fun ((y1, b1), (y2, b2)) -> y1 - y2, b2 - b1)
|> Seq.last
let (startBlocks, startY) =
g |> Seq.last |> (fun s -> (int64 s.BlockNumber, int64 (minY s.Cave)))
-startY + ((target - startBlocks) / int64 blockStep) * int64 step
printfn $"PART2: {part2 ()}"