packages feed

fused-effects-1.0.2.2: benchmark/Bench/NonDet/NQueens.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- Based largely on the implementation by Sreekar Shastry,
-- available at https://github.com/sshastry/queenslogic

module Bench.NonDet.NQueens (benchmark) where

import Control.Applicative
import Control.Monad (guard)
import Data.Foldable
import qualified Data.List as List
import Gauge hiding (benchmark)

type Square = (Int,Int)
type Board = [Int]

data Diagonal = Row Int
              | Col Int
              | Backslash Int
              | Forwardslash Int
              deriving (Eq, Show)

diags :: Square -> [Diagonal]
diags (i,j) = [ Row i
              , Col j
              , Backslash (j-i)
              , Forwardslash (i+j) ]

isSafeIn :: Square -> Board -> Bool
isSafeIn (i, j) qs = null (diags (i, j) `List.intersect` underThreat)
  where
    qs' = zip [1..length qs] qs
    underThreat = qs' >>= diags

addOne :: (Alternative m, Monad m) => Int -> Board -> m Board
addOne n curr = do
  let i = length curr + 1
  let choose = asum . fmap pure
  j <- choose [1..n]
  guard ((i, j) `isSafeIn` curr)
  pure (curr ++ [j])

queens :: (Alternative m, Monad m) => Int -> m Board
queens n = foldl' (>>=) (pure empty) (replicate n (addOne n))

benchmark :: (Alternative m, Monad m) => String -> (m Board -> [Board]) -> Gauge.Benchmark
benchmark title runQueens = bgroup title
  [ bench "4"  $ whnf (runQueens . queens) 4
  , bench "8"  $ whnf (runQueens . queens) 8
  , bench "16" $ whnf (runQueens . queens) 16
  ]
{-# INLINE benchmark #-}