This puzzle is called Wait For It.

open System

let sampleText =
    "Time:      7  15   30
Distance:  9  40  200"

Part 1

In this puzzle, I can make use of units of measure. This will allow me to make sure I don’t get my time numbers mixed up with my distance numbers. Because F# does not have built-in units for milliseconds and milliseconds I can very easily define them myself.

[<Measure>] type ms
[<Measure>] type mm

I also want a type to hold the basic data for each rice that is extracted while parsing.

type Race = Race of time: int64<ms> * distance: int64<mm>

I need a couple of functions to help me pass the text into my Race type.

let pasreAs (measure: int64<'a>) (txt: string) =
        txt.Split(':').[1].Trim().Split(' ')
        |> Array.where (fun s -> s <> "")
        |> Array.map int64
        |> Array.map ((*) measure)

let parseRases (text: string) =
    let [|t; d|] = text.Split('\n')
    let times = t |> pasreAs 1L<ms>
    let dists = d |> pasreAs 1L<mm>
    [ for i = 0 to times.Length - 1 do
        yield Race (times[i], dists[i])]

The next function will calculate the distance the boat will travel, given the time it spends accelerating and time is spends moving.

let distanceTraveld accelerate time =
    let speed = accelerate * 1L<mm/ms^2>
    let timeToRace = time - accelerate
    let distance = speed * timeToRace
    distance

I need to calculate each possible travel distance for each race.

let allAcceleratorOptions (time: int64<ms>) =
    seq { for i = 1L to (time * 1L</ms>) do
          yield distanceTraveld (i*1L<ms>) time }

I must count how many of each possible travel distances would win

let winCount (Race (time, distance)) =
    allAcceleratorOptions time
    |> Seq.filter (fun x -> distance < x)
    |> Seq.length

Let me run this, and see what I get.

sampleText
|> parseRases
|> List.map winCount
|> List.fold (*) 1
288

288 is the expected value for the test data. My final answer is also correct, and I get another star.

Part 2

In part two, most calculations are exactly the same. We rather just has to run it as a single race, rather than a collection of races.

I will create a couple of more functions to parse the input text as a single race.

let parseAsOne (measure: int64<'a>) (txt: string) =
    txt.Split(':').[1].Replace(" ", "").Trim()
    |> int64
    |> ((*) measure)

let parseOneRace (text: string) =
    let [|t; d|] = text.Split('\n')
    let time = t |> parseAsOne 1L<ms>
    let dist = d |> parseAsOne 1L<mm>
    Race (time, dist)

I will then run this, and see what I get.

sampleText
|> parseOneRace
|> winCount
71503

71503 is correct, and I get another star for my final answer as well.