Reputation: 1395
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
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:
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
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