Storing values in a Haskell data structure
I'm trying to store randomly generated bone values in some data structure, but don't know exactly how to do it in Haskell. I could generate random ints so far, but I want to be able to compare them with the corresponding color values and instead preserve the colors (can't imagine what this function would look like). Here is the code I have -
module Main where import System.IO import System.Random import Data.List diceColor = [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)] diceRoll =  rand :: Int -> [Int] -> IO () rand n rlst = do num <- randomRIO (1::Int, 6) if n == 0 then printList rlst -- here is where I need to do something to store the values else rand (n-1) (num:rlst) printList x = putStrLn (show (sort x)) --matchColor x = doSomething() main :: IO () main = do --hSetBuffering stdin LineBuffering putStrLn "roll, keep, score?" cmd <- getLine doYahtzee cmd --rand (read cmd)  doYahtzee :: String -> IO () doYahtzee cmd = do if cmd == "roll" then do rand 5  else putStrLn "Whatever"
After that, I want to give the user the ability to keep the same dice (as in the case of accumulating points), and give them the ability to re-roll the dice from the left - I think this can be done by moving the data structure (with the values in the dice) and counting the duplicate dice as points and store them in yet another data structure. If the user chooses to re-roll, he should be able to trigger random again and replace the values in the original data structure.
I come from OOP and Haskell is new territory for me. Help is greatly appreciated.
source to share
So, a few questions, let's take them one by one:
First . How to generate something other than integers with functions from System.Random (this is a slow generator, but performance is not vital for your application). There are several approaches, with your list, you need to write an intToColor function:
intToColor :: Int -> String intToColor n = head . filter (\p -> snd p == n) $ [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)]
Not very nice. Although you could do better if you wrote the pair in order (key, value), as there is little support for a "list of associations" in Data.List with a lookup function:
intToColor n = fromJust . lookup n $ [(1,"Black"),(2,"Green"),(3,"Purple"),(4,"Red"),(5,"White"),(6,"Yellow")]
Or, of course, you could just forget this operation with the Int key from 1 to 6 in the list, since the lists are already indexed by Int:
intToColor n = ["Black","Green","Purple","Red","White","Yellow"] !! n
(note that this function is slightly different as intToColor 0 is now "black" and not intToColor 1, but this is not very important given your purpose, if this really shocks you you can write "!! (n- 1) "instead of)
But since your colors are not actually strings and look like characters, you should probably create a color type:
data Color = Black | Green | Purple | Red | White | Yellow deriving (Eq, Ord, Show, Read, Enum)
So now Black is a Color value, you can use it anywhere in your program (and GHC will protest if you write Blak), and thanks to the magic of automatic derivation, you can compare color values or show them, or use toEnum to convert Int in colour!
So now you can write:
randColorIO :: IO Color randColorIO = do n <- randomRIO (0,5) return (toEnum n)
Second , you want to store the values (colors) in the dice in a data structure and be able to keep the same rolls. Therefore, you must first store the results of multiple throws, given the maximum number of simultaneous throws (5) and the complexity of your data, a simple list is sufficient and given the number of functions for processing lists in Haskell, this is a good choice.
So, you want to roll some dice:
nThrows :: Int -> IO [Color] nThrows 0 = return  nThrows n = do c <- randColorIO rest <- nThrows (n-1) return (c : rest)
This is a good first approach to what you are doing, more or less, except what you use, if instead of pattern matching and you have an explicit accumulator argument (have you been for tail recursion?), It is not better if do not count strict (Int, not lists).
Of course, Haskell promotes higher-order functions rather than forward recursion, so let's take a look at combinators looking for "Int -> IO a -> IO [a]" with Hoogle:
replicateM :: Monad m => Int -> m a -> m
What exactly do you want:
nThrows n = replicateM n randColorIO
(I'm not sure if I would even write this as a function, since I found the explicit expression clearer and almost as short)
Once you have the results of the throws you have to check which ones are identical, I suggest you look at the sort, group, map and length to achieve this (converting a list of results to a list of a list of identical results is not the most efficient data structure, but in this scale is the most appropriate choice). Then saving the colors you've received multiple times is just a matter of using a filter.
Then you have to write a few more functions to handle interaction and scoring:
type Score = Int yahtzee :: IO Score yahtzeeStep :: Int -> [[Color]] -> IO [[Color]] -- recursive scoring :: [[Color]] -> Score
Therefore, I recommend storing and transmitting [[Color]] to keep track of what has been pending. This should be enough for your needs.
source to share
Basically you are asking two different questions here. The first question can be answered with a type function
getColor n = fst . head $ filter (\x -> snd x == n) diceColor
The second question is, however, much more interesting. You cannot replace items. You need a function that can call itself recursively, and that function will control your game. It must accept the current score and the list of saved cubes as parameters. Upon entry, the score will be zero, and the list of those remaining in the dice will remain empty. It will then roll as many cubes as needed to fill the list (I'm not familiar with Yahtzee's rules), presents it to the user, and prompts for a choice. If the user decides to end the game, the function will return the score. If he decides to keep some dice, the function calls itself with the current score and a list of dice remaining. So, to sum up
playGame :: Score -> [Dice] -> IO Score
Disclaimer: I am a very beginner in Haskell too.
source to share