6
\$\begingroup\$

The game of Set involves cards with four properties: number, symbol, shading, and colour.

Three cards make a "Set" if the property is either all the same or all different across the three cards, for each of the four properties.

I wrote some code to verify if three cards constitute a "Set". But I'm not satisfied with the final function isSet. I feel there should be some way to iterate over the four properties instead of writing four separate lines. Any help would be appreciated - I'm fairly new to F#.

type Number = | One | Two | Three
type Symbol = | Round | Wavy | Diamond
type Shading = | Empty | Lines | Full
type Colour = | Red | Green | Blue

type Card = {number: Number;
            symbol: Symbol;
            shading: Shading;
            colour: Colour}

let all =
    List.reduce (=)

let numUnique list =
    list
    |> List.distinct
    |> List.length

let allSame list =
    list
    |> numUnique
    |> (=) 1

let allUnique list =
    let length = List.length list
    list
    |> numUnique
    |> (=) length

let allSameOrUnique list =
    allSame list || allUnique list

let isSet card1 card2 card3 =

    let check1 = allSameOrUnique [card1.number; card2.number; card3.number]
    let check2 = allSameOrUnique [card1.symbol; card2.symbol; card3.symbol]
    let check3 = allSameOrUnique [card1.shading; card2.shading; card3.shading]
    let check4 = allSameOrUnique [card1.colour; card2.colour; card3.colour]

    all [check1; check2; check3; check3; check4]

let card1 = {number = Two; symbol = Round; shading = Lines; colour = Green}
let card2 = {number = One; symbol = Round; shading = Empty; colour = Green}
let card3 = {number = Three; symbol = Round; shading = Full; colour = Green}

The final function returns a boolean for verifying the set:

> isSet card1 card2 card3

val it : bool = true
\$\endgroup\$
5
\$\begingroup\$

I feel there should be some way to iterate over the four properties instead of writing four separate lines.

You can use a higher-order function that takes in a function that returns a card property. Here is an example of how that could look. I've also simplified the code to the extent that none of the helper functions are needed, and changed some formatting to more idiomatic F#:

type Number = One | Two | Three
type Symbol = Round | Wavy | Diamond
type Shading = Empty | Lines | Full
type Colour = Red | Green | Blue

type Card =
    { Number: Number
      Symbol: Symbol
      Shading: Shading
      Colour: Colour}

let isSet card1 card2 card3 =
    let sameOrUniqueBy prop =
        let unique = [ card1; card2; card3 ] |> List.distinctBy prop |> List.length
        unique = 1 || unique = 3

    sameOrUniqueBy (fun c -> c.Number)
    && sameOrUniqueBy (fun c -> c.Symbol)
    && sameOrUniqueBy (fun c -> c.Shading)
    && sameOrUniqueBy (fun c -> c.Colour)

In this code, sameOrUniqueBy is a higher-order function because it takes a function as an argument. It is defined as a local function inside isSet so that the cards don't have to be passed in each time it's used.

Testing:

let card1 = { Number = Two; Symbol = Round; Shading = Lines; Colour = Green }
let card2 = { Number = One; Symbol = Round; Shading = Empty; Colour = Green }
let card3 = { Number = Three; Symbol = Round; Shading = Full; Colour = Green }

isSet card1 card2 card3 = true // ✔

isSet card1 card1 card3 = false // ✔
\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.