# Advent of Code Day 15–Outside In

Today’s Advent of Code challenge was a relatively kind one, and I’m pretty sure if I tried hard enough I could get this down to a single line of code. But a great article from Pierre Irrmann reminded me that with F# it can be all too easy to go overboard on pipelines and composition and end up with clever code that is incomprehensible.

So I decided to take an “outside” in approach. I needed a `solve`

function that took a sequence defining the “disks” and would return an integer by finding the first time at which all the disks were aligned. The disk definitions were tuples of number of positions and starting position (if I’d been following all of Pierre’s advice these would have been types instead). And the `isSolution`

function I stubbed out to return true. Obviously this meant my test case would return 0 initially.

```
let solve disks =
Seq.initInfinite id
|> Seq.find (isSolution disks)
solve [(5,4);(2,1)] |> printfn "Test: %d"
```

So next was to implement `isSolution`

. If start time is 5, then the first disk must be open at 6, the second disk must be open at 7 and so on. So I used `Seq.indexed`

to get a tuple of the disk definition and an incrementing number, and then tested whether for each disk and time offset the slot was open. Again `isOpenAtT`

had not been implemented yet so was hard-coded to return true.

```
let isSolution disks startTime =
disks
|> Seq.indexed
|> Seq.forall (fun (n,disk) -> isOpenAtT disk (startTime+1+n))
```

Finally, my outside in approach led me to create `isOpenAtT`

, which is simple to calculate:

```
let isOpenAtT (positions,startPos) time =
((startPos + time) % positions) = 0
```

I then used regular expressions to parse my input data:

```
open System.Text.RegularExpressions
let parseInput input =
let parts = Regex.Matches(input,@"\d+")
|> Seq.cast<Match>
|> Seq.map (fun m-> int m.Value)
|> Seq.toList
match parts with | [_;pos;_;start] -> (pos,start) | _ -> failwith ("parse error" + input)
let discs = System.IO.File.ReadAllLines (__SOURCE_DIRECTORY__ + "\\input.txt") |> Array.map parseInput
```

And now could pass that input data into the solve function to solve parts a and b:

```
solve discs |> printfn "Part a: %d"
solve (Seq.append discs [(11,0)]) |> printfn "Part b: %d"
```

As I said, there’s no doubt the solution could be made much more succinct, but the outside-in approach of starting with the `solve`

function worked very well and is one I will have to try again in future. Full code is available on GitHub as usual.