Today’s puzzle is Cube Conundrum.

Part 1

I will start by defining a few types, cube, game.

type Set = Set of red: int * green: int *  blue: int
type Game = Game of no: int * sets: Set list

I need a way to see if a given game is valid (a game is valid if all sets in the game are valid).

let validSet (Set (bagRed, bagGreen, bagBlue)) (Set (setRed, setGreen, setBlue)) =
    setRed <= bagRed && setGreen <= bagGreen && setBlue <= bagBlue

let validGame bag (Game (no, sets)) =
    if Seq.forall (validSet bag) sets then Some no else None

I need to be able to read the input text

let readText (fullText: string) =
    let readCubes (Set (red,green,blue)) (cubeText: string) =
        match cubeText.Split(' ') with
        | [| x; "red" |] -> (Set (int x, green, blue))
        | [| x; "green" |] -> (Set (red, int x, blue))
        | [| x; "blue" |] -> (Set (red, green, int x))
        | _ -> (Set (red, green, blue))

    let readSet (setText: string) =
        setText.Split(',')
        |> Seq.map (fun x -> x.Trim())
        |> Seq.fold readCubes (Set (0,0,0))

    let readGameId (game: string) = game.Split(' ')[1] |> int
    let readGame (gameText: string) =
        match gameText.Split(':') with
        | [| game; sets |] -> Some (Game (readGameId game, sets.Split(';') |> Seq.map readSet |> Seq.toList))
        | _ -> None

    fullText.Split("\n") |> Seq.choose readGame

I will now create a bag (which is just a set).

let bag = Set (12, 13, 14)

And then read the example text, convert it to a list of games, validate each one, and then get a sum of the numbers.

let sampleText =
    "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"

sampleText
|> readText
|> Seq.choose (validGame bag)
|> Seq.sum
8

The result is correct for the example, and my answer for the puzzle is correct as well.

Part 2

In Part 2 I need to find the minimum bag needed for each game. This can be done by fining the maximum of each cube colour in each game.

let minSet (Game (_, sets)) =
     let getRed (Set(r,_,_)) = r
     let getGreen (Set(_,g,_)) = g
     let getBlue (Set(_,_,b)) = b
     let maxBy g = sets |> Seq.map g |> Seq.max
     Set (maxBy getRed, maxBy getGreen, maxBy getBlue)

I need a small function to calculate the power of each games minimum bag.

let powerCube (Set (red, green, blue)) = red * green * blue

Now I put this all together and see if my results are close.

sampleText
|> readText
|> Seq.map minSet
|> Seq.map powerCube
|> Seq.sum
2286

`2286` is the result I am expecting with the example input. My final answer is also correct, I have one more gold star.