packages feed

Craft3e-0.2.0.5: Minesweeper/MineRandom.hs

----------------------------------------------------------
--							--
--	MineRandom.hs					--
--							--
--	Simon Thompson					--
--							--
--	2002-2011					--
--							--
----------------------------------------------------------

-- Choosing a random starting configuration for 
-- a minesweeper game.

-- Making dynamic choices: get the seed on each invocation.
-- Have to refactor choices etc. to take the seed as a parameter.

module MineRandom ( randomGrid, randomGridDyn ) where
import System.Random
import System.IO.Unsafe ( unsafePerformIO )
import Data.Time.Clock.POSIX ( getPOSIXTime )
import Data.List ( insert , nub )

-- Generate a random combination of m elements from n 
-- i.e. choice of 0, 1, ..., n-1.
-- The algorithm used makes repeated random choices until m different
-- values are found.
-- Perfectly efficient for n=100, m=40; not for 1000,400.
-- Assumes that m<=n.
-- Postcondition: the result is in ascending order; no duplicates.
-- 16.6.02 seed is made a parameter

choices :: Int -> Int -> Int -> [Int]

choices seed n m
  = fst (choicesAux ([],rands))
    where
    
    choicesAux :: ([Int],[Int]) -> ([Int],[Int])
    choicesAux (cs,(r:rs))
      | length cs >= m 	= (cs,[])
      | otherwise	= choicesAux (nub (insert r cs) , rs)
      
    rands :: [Int]
    rands = randomRs (0::Int,n-1) (mkStdGen seed)
    
-- A random startup

-- A seed for the random numbers is given by system time in seconds.
-- A value is chosen once per session: the value persists through a
-- session.

sessionSeed :: Int

sessionSeed = round (unsafePerformIO getPOSIXTime)

-- A list of n choices from an m*p matrix
-- 	m = row length
--	p = column height
-- Assumes that the postcondition for choices holds.
-- 16.6.02 seed is made a parameter to the old randomGrid, now
-- renamed randomGridMake.

randomGridMake :: Int -> Int -> Int -> Int -> [[Bool]]

randomGridMake seed n m p 
  = pad
    where
    
    makeMatrix :: Int -> [Int] -> [[Bool]]

    makeMatrix i cs
      | cs==[]		= []
      | otherwise
        = convert first : makeMatrix (i+1) rest
	  where
	  (first,rest) = span ((==i).(flip div m)) cs
	  convert ns 
	     = map check [0 .. m-1]
	       where
	       check n = elem n [ x `mod` m | x<-ns ]

    rows = makeMatrix 0 (choices seed (m*p) n)
     
    pad = rows ++ replicate (p - length rows) (replicate m False)

-- Random grid with a per-session seed.

randomGrid :: Int -> Int -> Int -> [[Bool]]

randomGrid = randomGridMake sessionSeed 

-- Random grid with a per-invocation seed.

randomGridDyn :: Int -> Int -> Int -> Int -> [[Bool]]

randomGridDyn 
  = randomGridMake