1

I am trying to make Conway's Game of Life in Haskell. I am very new to Haskell and functional programming in general, and having trouble making an array in haskell to represent the game board.

I am trying to initiate a random array to begin the game. To do that I create a function randomBoard which returns '*' or ' ' to represent a space on the game board.

I want to be able to create the game board array with this function. I havent been able to succesfully instantiate an array yet. I am hoping there is a way i can declare an array of say size 100 and use my random function to set each element.

import System.Random
import Data.Array
import Data.List

randomBoard = 
   do 
   f1 <- randomIO :: IO Int
   if(f1 `mod` 2) == 0
     then return  '*'
     else return  ' '

boardArray :: Array Int Char
boardArray = listArray (0, 100) . map randomBoard $ 100 (0,100)

This obviously doesnt work or even compile. I am sure there are a couple things wrong with it, as I am not sure really how to even work with IO in haskell and produce this outcome. Any guidance is much appreciated...

1
  • 2
    I made Game of Life as one of my first projects in Haskell. I would advise you to forget about Data.Array and just use a nested [[Bool]] for the game board. Data.Array is much more efficient, but a nested list is much easier to use especially for beginners. Additionally it is easier to use a Bool as the state type rather than a Char, as a Bool naturally maps to the on/off nature of the cell state. Commented May 27, 2019 at 0:05

1 Answer 1

7

This should work for you:

import Control.Monad
import System.Random
import Data.Array
import Data.List

randomBoard :: IO Char
randomBoard =
   do
   f1 <- randomIO :: IO Int
   if(f1 `mod` 2) == 0
     then return  '*'
     else return  ' '

boardArray :: IO (Array Int Char)
boardArray = listArray (0, 99) <$> replicateM 100 randomBoard

Here's what I changed:

  1. I added the type signature randomBoard :: IO Char for clarity. (The code would still work without it, as Haskell correctly infers this type if you don't supply it.)
  2. I changed the type of boardArray to use IO. Anything that uses IO, no matter how indirectly, need to be in IO itself.
  3. I changed listArray (0, 100) to listArray (0, 99), as the former would actually be 101 elements.
  4. map randomBoard $ 100 (0,100) isn't right at all. To get a list of several of the same thing, you'd usually use replicate, but since the thing you care about here is in the IO monad, you use replicateM instead. replicateM 100 randomBoard gives an IO [Char] with 100 random elements of either '*' or ' '.
  5. I added an import of Control.Monad, which is needed to use replicateM.
  6. I use <$> in boardArray. Since you want to call listArray with a [Char] and get an Array Int Char, but replicateM 100 randomBoard is an IO [Char], you can't just apply the argument directly. Using <$> (which is also called fmap) applies it "inside" of IO, giving you back an IO (Array Int Char).
Sign up to request clarification or add additional context in comments.

3 Comments

This is awesome and makes a lot of sense. Thank you so much. Follow up question, what is the best way to print boardArray to screen? Previously i was using putStr.
@Anon. For debugging, you could just print it and get something that looks like array (0,99) [(0,' '),(1,' '),(2,'*'),...,(98,'*'),(99,'*')]. If you want something prettier, then a custom function with putStr probably is your best bet.
@Anon. The best way is probably to define your own custom function showBoard :: Array Int Char -> String which should iterate over the array and convert it to a String; this allows you to format it nicely as well. Then you can print that with putStrLN.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.