Skip to content

Instantly share code, notes, and snippets.

@frasertweedale
Last active August 29, 2015 14:06
Show Gist options
  • Save frasertweedale/39514a3c5ff6ff8f80bc to your computer and use it in GitHub Desktop.
Save frasertweedale/39514a3c5ff6ff8f80bc to your computer and use it in GitHub Desktop.
Percolation solution in Haskell

To test this in ghci:

import Percolation
> percolated $ addSites [(2,2),(3,3),(0,1),(0,2),(0,3),(0,4)] $ newGrid 5
False
> percolated $ addSites [(2,2),(3,3),(0,1),(0,2),(0,3),(0,4),(0,0)] $ newGrid 5
True
module Percolation where
import Control.Monad (join)
import Data.Maybe (mapMaybe)
import qualified Data.Map.Lazy as M
import qualified Data.Set as S
-- | Percolation algorithm.
--
-- Important assumptions or implementation details:
--
-- * When sets are unioned, the lowest SetId is used as
-- the SetId for the new set. This allows us to check
-- whether we have percolated by asking whether Set 1
-- (initially the bottom) has been removed due to merging
-- with Set 0 (initially the top).
type SiteId = Int
type SetId = Int
type Coord = (Int,Int)
data Grid =
Grid
Int
-- ^ Dimension of matrix
SiteId
-- ^ Next SiteId to allocate
(M.Map (Int,Int) (Maybe SiteId))
-- ^ Mapping of coordinate to SiteId
(M.Map SiteId SetId)
-- ^ Mapping of SiteId to SetId
(S.Set SetId)
-- ^ Mapping of SetId to set of connected coordinates
deriving (Show)
newGrid :: Int -> Grid
newGrid n =
let
coords = [ (x,y) | x <- [0..n-1], y <- [0..n-1] ]
topCoords = [ (x,-1) | x <- [0..n-1] ]
bottomCoords = [ (x,n) | x <- [0..n-1] ]
coordMap = foldr (`M.insert` Nothing) M.empty coords
coordMap' = foldr (`M.insert` Just 0) coordMap topCoords
coordMap'' = foldr (`M.insert` Just 1) coordMap' bottomCoords
in
Grid n 2 coordMap''
(M.fromList [(0,0),(1,1)])
(S.fromList [0, 1])
percolated :: Grid -> Bool
percolated (Grid _ _ _ _ sets) = S.notMember 1 sets
addSite :: Coord -> Grid -> Grid
addSite xy@(x,y) g@(Grid n nextSiteId siteIds setIds sets) =
case M.lookup xy siteIds of
Just Nothing ->
-- site not open; find surrounding setIds
let
adjSiteIds = mapMaybe (join . (`M.lookup` siteIds))
[(x,y-1),(x-1,y),(x+1,y),(x,y+1)]
adjSetIds = mapMaybe (`M.lookup` setIds) adjSiteIds
siteId = foldr min nextSiteId adjSiteIds
setId = foldr min nextSiteId adjSetIds
in
Grid n
(if setId == nextSiteId then nextSiteId + 1 else nextSiteId)
(M.insert xy (Just siteId) siteIds)
(foldr (`M.insert` setId) setIds (setId : adjSetIds))
(S.insert setId
$ foldr S.delete sets adjSetIds)
_ ->
-- site out of bounds, or already open; return grid unchanged
g
addSites :: [Coord] -> Grid -> Grid
addSites = foldr ((.) . addSite) id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment