Esten
Esten

Reputation: 1395

How do you model 5 dice values with Elm Custom Types?

I am trying to produce a simple dice game in Elm. I will have a "dice cup" that contains 5, 6 sided dice. When you "roll" the dice, you get 5 random dice values. Lastly, a "1" value is considered "wild", and can be used in place of any other die value.

So currently, I've got this working using a Random.generate command to produce a List of 5 Int between the values of 1 and 6:

Roll ->
  ( model
  , Random.generate NewRoll (Random.list 5 (Random.int 1 6))
  )

So, I could model my cup like this: type alias Cup = List Int

But I'm thinking it would probably be valuable to add some more formalization around my data. Especially after reading this article which felt encouraging that I should model my data more like this:

type alias Cup = List Die

type Die
  = One
  | Two
  | Three
  | Four
  | Five
  | Six

I'm struggling with a few things though. First, while this Die type feels like a more explicit and accurate way of modeling what a Cup would consist of, I'm struggling to determine how I would then associate these type variant values with the actually integer values. When I use my random number generator, it's going to produce integers.

How would I reconcile those with my named types? Even if I could, how do I go the other direction? I'll eventually need to perform some logic on my cup that determines how "good" the roll is, and that will involve assessing which values are greater than others, and how many of each value I have.

My inclination is that there should be an integer value associated with each variant to form a data constructor One Int, but that's not right. This would mean that any integer could be associated with the variant One which isn't what I want. I only want the integer: 1.

My second inclination is for my Die type to use a type argument like Die int, and forget about the variants, instead associating the die values directly with a Die type. This would allow other types of data to be assicated with the Die type, like Bool that represents if the die is wild. However, it occurs to me that this isn't really how things work. The type argument is something that seems to be used to provide an ambiguous data type to a variant of type Die. I can't make a Die Int type, standalone.

So I'm not sure where I go from here. Custom types feel like the answer to my data modeling, but at this point I'm wondering if I should actually be using a record?

I'm also wondering if there is some way to lean all the way into my type variants. Is it possible to randomly generate these type variants? But then how could I compare these variant values with logic?

Upvotes: 2

Views: 124

Answers (2)

edgerunner
edgerunner

Reputation: 14973

I will go a step further and consider that you want to encode a wild die, which may be used as any value. Some assumptions:

  • The literal values are 2 to 6. 1 is a placeholder that may take any literal value.
  • The wild dice produced in the roll do not count as any value until they are assigned later.
  • You want to be aware that a wild die is a wild die after it is assigned a value. (ie. the wild status isn't forgotten after assigning)
  • Once assigned, a wild die can't be reassigned

So let's go with our literal values first:

type LiteralValue
  = Two
  | Three
  | Four
  | Five
  | Six

A die as rolled is either a literal value, or a wild die that may or may not be assigned a literal value.

type Die
  = Literal LiteralValue
  | Wild (Maybe LiteralValue)

When rolled, a die is one of six values

generator : Random.Generator Die
generator =
  Random.uniform
    ( Wild Nothing ) -- Wild is always rolled unassigned
    [ Literal Two
    , Literal Three
    , Literal Four
    , Literal Five
    , Literal Six
    ]

You may later assign a value to unassigned wild dice. Literal dice and assigned wild dice are unaffected.

assign : LiteralValue -> Die -> Die
assign value die =
  case die of
    Wild Nothing ->
      Wild (Just value)
    _ ->
      die

Upvotes: 2

Matthew James Kraai
Matthew James Kraai

Reputation: 121

How about the following?

type Die
    = One
    | Two
    | Three
    | Four
    | Five
    | Six


type Cup
    = Cup Die Die Die Die Die


die : Random.Generator Die
die =
    Random.uniform One [ Two, Three, Four, Five, Six ]


cup : Random.Generator Cup
cup =
    Random.map5 Cup die die die die die


toInt : Die -> Int
toInt d =
    case d of
        One ->
            1

        Two ->
            2

        Three ->
            3

        Four ->
            4

        Five ->
            5

        Six ->
            6

Upvotes: 5

Related Questions