Итак, несколько вопросов, давайте ответим на них один за другим:
Первый : Как сгенерировать что-то еще, кроме целых чисел, с помощью функций из System.Random (медленный генератор,но для вашего приложения производительность не является жизненно важной).Есть несколько подходов, с вашим списком, вам придется написать функцию intToColor:
intToColor :: Int -> String
intToColor n = head . filter (\p -> snd p == n) $ [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)]
Не очень приятно.Хотя вы могли бы добиться большего, если бы записали пару в порядке (ключ, значение), поскольку в Data.List есть небольшая поддержка «списка ассоциаций» с функцией поиска:
intToColor n = fromJust . lookup n $ [(1,"Black"),(2,"Green"),(3,"Purple"),(4,"Red"),(5,"White"),(6,"Yellow")]
Иликонечно, вы могли бы просто забыть об этом ключе Int от 1 до 6 в списке, поскольку списки уже проиндексированы по Int:
intToColor n = ["Black","Green","Purple","Red","White","Yellow"] !! n
(обратите внимание, что эта функция немного отличаетсятак как intToColor 0 теперь «черный», а не intToColor 1, но это не очень важно, учитывая вашу цель, если вас действительно шокирует, вы можете написать «!! (n-1)» вместо)
Нопоскольку ваши цвета на самом деле не являются строками и больше похожи на символы, вам, вероятно, следует создать тип цвета:
data Color = Black | Green | Purple | Red | White | Yellow deriving (Eq, Ord, Show, Read, Enum)
Так что теперь черный - это значение типа Color, вы можете использовать его в любом месте вашей программы (и GHCбудет протестовать, если вы напишите Blak), и благодаря магии автоматического вывода вы можете сравнить значения цвета, или показать их, или использовать toEnum, чтобы преобразовать Int в цвет!
Так что теперь вы можетеНапишите:
randColorIO :: IO Color
randColorIO = do
n <- randomRIO (0,5)
return (toEnum n)
Секунда , вы хотите сохранить значения (цвета) игральных костей в структуре данных и дать возможность сохранять идентичные броски.Итак, сначала вы должны сохранить результаты нескольких бросков, учитывая максимальное количество одновременных бросков (5) и сложность ваших данных, простого списка достаточно, и учитывая количество функций для обработки списков в Haskell, это хороший выбор..
Итак, вы хотите бросить несколько кубиков:
nThrows :: Int -> IO [Color]
nThrows 0 = return []
nThrows n = do
c <- randColorIO
rest <- nThrows (n-1)
return (c : rest)
Это хороший первый подход, это то, что вы делаете, более или менее, за исключением того, что вы используете вместо сопоставления с образцом и у вас естьявный аргумент аккумулятора (вы собирались использовать хвостовую рекурсию?), не лучше, если бы не строгий аккумулятор (Int, а не списки).
Конечно, Haskell продвигает функции высшего порядка, а не прямую рекурсию, поэтомудавайте посмотрим на комбинаторы, которые ищут в «Int -> IO a -> IO [a]» с помощью Google:
replicateM :: Monad m => Int -> m a -> m [a]
, который делает именно то, что вы хотите:
nThrows n = replicateM n randColorIO
(Я не уверен, что я бы даже написал это как функцию, поскольку я нахожу явное выражение более ясным и почти таким же коротким)
Как только вы получите результатыиз бросков, вы должны проверить, какие идентичные, я предлагаю вам взглянуть на сортировку, группу, карту и длину для достижения этой цели (преобразование вашего списка результатов в список из списка идентичных результатов, не самая эффективная из структуры данных, нов этом масштабе наиболее подходящий выбор).Тогда сохранение цветов, которые вы получили несколько раз, - это всего лишь вопрос использования фильтра.
Затем вы должны написать еще несколько функций для управления взаимодействием и оценкой:
type Score = Int
yahtzee :: IO Score
yahtzeeStep :: Int -> [[Color]] -> IO [[Color]] -- recursive
scoring :: [[Color]] -> Score
Поэтому я рекомендую сохранить ипередать [[Цвет]], чтобы отслеживать то, что было отложено.Этого должно быть достаточно для ваших нужд.