\$\begingroup\$

This is a Haskell implementation of Conway's Game of Life.

It plays on a console. It should be able to play a field of any size, but we only give it a glider on a small field to run at this point.

Using a file named GameOfLife.hs :

import Control.Concurrent import Text.Printf main :: IO () main = gameOfLife ( [ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ] )

The main function only calls gameOfLife with some startup state - a list of lists of 0s and 1s:

gameOfLife :: [[Integer]] -> IO () gameOfLife state = do pretty_print state let new_state = transition state sleep printf "\ESC[%dA" $ length state -- move cursor to beginning. gameOfLife new_state

First we pretty print the state, define the new state as the transition of the original state, sleep (to slow it down enough to visualize the changes), move the cursor to the beginning using an escape code so we can draw the new state over the old, and then recursively call gameOfLife with the new state.

The pretty print function is as follows - it uses a sprite function to map the 0's and 1's to a viewable field of characters:

pretty_print :: [[Integer]] -> IO () pretty_print state = do mapM_ print_row state where print_row linestate = do putStrLn $ concat $ map sprite linestate sprite :: Integer -> String sprite 0 = "." sprite _ = "*"

"Sprite," is probably the wrong word. Maybe, "char," would be better?

When printed - it looks like this - here's sample output:

$ ./GameOfLife .*.......... ..*......... ***......... ............ ............ ............ ............ ............ ............ ............

Then the new state is defined as the transition of the old state.

We start with the row of interest, and get the rows before it and after it. Then we go element by element. When we would get an index that would be out of bounds, I get the index from the other end of the field. This adds some time to the algorithm, I'm sure, but it has the nice effect of allowing the field to wrap around.

-- get new state row by row. transition :: [[Integer]] -> [[Integer]] transition state = do process 0 where last_i = ((length state) - 1) process n = process_row n : (if n == last_i then [] else process (n + 1)) process_row i = process_rows prev_row this_row next_row where prev_row = state !! (if i == 0 then last_i else (i - 1)) this_row = state !! i next_row = state !! (if i == last_i then 0 else (i + 1)) process_rows prev row next = do proc 0 where last_j = ((length row) - 1) proc m = proc_col m : (if m == last_j then [] else proc (m + 1)) proc_col j = live_die (row !! j) ( -- column to left (if j == 0 then (last prev + last row + last next) else (prev !! (j - 1) + row !! (j - 1) + next !! (j - 1))) -- above & below + prev !! j + next !! j -- column to right + (if j == last_j then (prev !! 0 + row !! 0 + next !! 0) else (prev !! (j + 1) + row !! (j + 1) + next !! (j + 1))) )

The logic for does the cell live or die is if the cell is currently alive, then it stays alive if 2 or 3 surrounding cells are alive, else it dies. If the current cell isn't alive, it only comes to life if there are 3 cells alive next to it. I believe I succinctly cover this logic with pattern matching:

live_die :: Integer -> Integer -> Integer live_die _ 3 = 1 live_die 1 2 = 1 live_die _ _ = 0

Finally, each transition is followed by some sleeping to avoid having a crazy-looking blur on the screen - I have no problems with the animation with a sleep of 1/10th of a second per transition:

sleep :: IO () sleep = threadDelay 100000

I have stack installed, so I built it with:

$ stack ghc GameOfLife.hs

Make the executable file actually executable (only had to do this the first time.)

$ chmod -x GameOfLife

and execute it like so:

$ ./GameOfLife .*.......... ..*......... ***......... ............ ............ ............ ............ ............ ............ ............

Ideally, the executable could take a value to seed a pseudo-random number generator (PRNG) or a filename with a user-created image. For those goals, I suppose it's obvious that main shouldn't unconditionally start gameOfLife like it does.

Probably the printing should be further separated from the transition .

Maybe we could use data types to represent the cells (0's and 1's) and the field (a list of lists of Integers). But we would need (+) implemented for Cell values.

I think I can see a few other ways to slightly reduce redundant function calls, but I'm not sure I'll save any lines of code that way.