packages feed

apecs-0.10.0: src/Apecs/Tags.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

module Apecs.Tags where

import Apecs.Core (Entity (..), SystemT)
import qualified Data.IntSet as IS
import qualified Data.Map.Strict as M
import qualified Data.Set as S

{- | The type of tags for a world, e.g. @WTag MyWorld = MyWorldTag@.
  Standalone so that multiple @HasTags w m@ instances share one equation.
-}
type family WTag w

{- | @HasTags w m@ means that world @w@ has a tag system generated by @makeTaggedComponents@.
  Provides a way to query component tags for entities, dispatching on the world type @w@.
-}
class (Monad m) => HasTags w m where
  entityTags :: Entity -> SystemT w m [WTag w]

{- | Count entities grouped by their distinct component combination.

Takes the world's entity member set and uses 'entityTags' from the
'HasTags' class to query each entity's tags. Returns a map from tag sets
to entity counts.
-}
countCombinations
  :: (HasTags w m, Enum (WTag w), Ord (WTag w))
  => IS.IntSet
  -- ^ Entity IDs to census
  -> SystemT w m (M.Map (S.Set (WTag w)) Int)
countCombinations entities = do
  tagSets <- mapM poll (IS.toList entities)
  let counts = M.fromListWith (+) [(ts, 1 :: Int) | ts <- tagSets]
  pure $ M.mapKeysMonotonic (S.fromList . map toEnum . IS.toList) counts
  where
    poll eid = do
      tags <- entityTags (Entity eid)
      pure $! IS.fromList (map fromEnum tags)