{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
module Swarm.Game.Recipe (
IngredientList,
Recipe (..),
recipeInputs,
recipeOutputs,
recipeCatalysts,
recipeTime,
recipeWeight,
loadRecipes,
outRecipeMap,
inRecipeMap,
catRecipeMap,
MissingIngredient (..),
MissingType (..),
knowsIngredientsFor,
recipesFor,
make,
make',
findLacking,
) where
import Control.Algebra (Has)
import Control.Arrow (left)
import Control.Effect.Lift (Lift, sendIO)
import Control.Effect.Throw (Throw, liftEither)
import Control.Lens hiding (from, (.=))
import Control.Monad ((<=<))
import Data.Bifunctor (second)
import Data.Either.Validation
import Data.Foldable (Foldable (..))
import Data.IntMap (IntMap)
import Data.IntMap qualified as IM
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Yaml
import GHC.Generics (Generic)
import Swarm.Failure
import Swarm.Game.Entity as E
import Swarm.Game.Ingredients
import Swarm.ResourceLoading (getDataFileNameSafe)
import Swarm.Util.Effect (withThrow)
import Swarm.Util.Lens (makeLensesNoSigs)
import Swarm.Util.Yaml
import Witch
import Prelude hiding (Foldable (..))
data Recipe e = Recipe
{ forall e. Recipe e -> IngredientList e
_recipeInputs :: IngredientList e
, forall e. Recipe e -> IngredientList e
_recipeOutputs :: IngredientList e
, forall e. Recipe e -> IngredientList e
_recipeCatalysts :: IngredientList e
, forall e. Recipe e -> Integer
_recipeTime :: Integer
, forall e. Recipe e -> Integer
_recipeWeight :: Integer
}
deriving (Recipe e -> Recipe e -> Bool
(Recipe e -> Recipe e -> Bool)
-> (Recipe e -> Recipe e -> Bool) -> Eq (Recipe e)
forall e. Eq e => Recipe e -> Recipe e -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall e. Eq e => Recipe e -> Recipe e -> Bool
== :: Recipe e -> Recipe e -> Bool
$c/= :: forall e. Eq e => Recipe e -> Recipe e -> Bool
/= :: Recipe e -> Recipe e -> Bool
Eq, Eq (Recipe e)
Eq (Recipe e) =>
(Recipe e -> Recipe e -> Ordering)
-> (Recipe e -> Recipe e -> Bool)
-> (Recipe e -> Recipe e -> Bool)
-> (Recipe e -> Recipe e -> Bool)
-> (Recipe e -> Recipe e -> Bool)
-> (Recipe e -> Recipe e -> Recipe e)
-> (Recipe e -> Recipe e -> Recipe e)
-> Ord (Recipe e)
Recipe e -> Recipe e -> Bool
Recipe e -> Recipe e -> Ordering
Recipe e -> Recipe e -> Recipe e
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall e. Ord e => Eq (Recipe e)
forall e. Ord e => Recipe e -> Recipe e -> Bool
forall e. Ord e => Recipe e -> Recipe e -> Ordering
forall e. Ord e => Recipe e -> Recipe e -> Recipe e
$ccompare :: forall e. Ord e => Recipe e -> Recipe e -> Ordering
compare :: Recipe e -> Recipe e -> Ordering
$c< :: forall e. Ord e => Recipe e -> Recipe e -> Bool
< :: Recipe e -> Recipe e -> Bool
$c<= :: forall e. Ord e => Recipe e -> Recipe e -> Bool
<= :: Recipe e -> Recipe e -> Bool
$c> :: forall e. Ord e => Recipe e -> Recipe e -> Bool
> :: Recipe e -> Recipe e -> Bool
$c>= :: forall e. Ord e => Recipe e -> Recipe e -> Bool
>= :: Recipe e -> Recipe e -> Bool
$cmax :: forall e. Ord e => Recipe e -> Recipe e -> Recipe e
max :: Recipe e -> Recipe e -> Recipe e
$cmin :: forall e. Ord e => Recipe e -> Recipe e -> Recipe e
min :: Recipe e -> Recipe e -> Recipe e
Ord, Count -> Recipe e -> ShowS
[Recipe e] -> ShowS
Recipe e -> String
(Count -> Recipe e -> ShowS)
-> (Recipe e -> String) -> ([Recipe e] -> ShowS) -> Show (Recipe e)
forall e. Show e => Count -> Recipe e -> ShowS
forall e. Show e => [Recipe e] -> ShowS
forall e. Show e => Recipe e -> String
forall a.
(Count -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall e. Show e => Count -> Recipe e -> ShowS
showsPrec :: Count -> Recipe e -> ShowS
$cshow :: forall e. Show e => Recipe e -> String
show :: Recipe e -> String
$cshowList :: forall e. Show e => [Recipe e] -> ShowS
showList :: [Recipe e] -> ShowS
Show, (forall a b. (a -> b) -> Recipe a -> Recipe b)
-> (forall a b. a -> Recipe b -> Recipe a) -> Functor Recipe
forall a b. a -> Recipe b -> Recipe a
forall a b. (a -> b) -> Recipe a -> Recipe b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> Recipe a -> Recipe b
fmap :: forall a b. (a -> b) -> Recipe a -> Recipe b
$c<$ :: forall a b. a -> Recipe b -> Recipe a
<$ :: forall a b. a -> Recipe b -> Recipe a
Functor, (forall m. Monoid m => Recipe m -> m)
-> (forall m a. Monoid m => (a -> m) -> Recipe a -> m)
-> (forall m a. Monoid m => (a -> m) -> Recipe a -> m)
-> (forall a b. (a -> b -> b) -> b -> Recipe a -> b)
-> (forall a b. (a -> b -> b) -> b -> Recipe a -> b)
-> (forall b a. (b -> a -> b) -> b -> Recipe a -> b)
-> (forall b a. (b -> a -> b) -> b -> Recipe a -> b)
-> (forall a. (a -> a -> a) -> Recipe a -> a)
-> (forall a. (a -> a -> a) -> Recipe a -> a)
-> (forall a. Recipe a -> [a])
-> (forall a. Recipe a -> Bool)
-> (forall a. Recipe a -> Count)
-> (forall a. Eq a => a -> Recipe a -> Bool)
-> (forall a. Ord a => Recipe a -> a)
-> (forall a. Ord a => Recipe a -> a)
-> (forall a. Num a => Recipe a -> a)
-> (forall a. Num a => Recipe a -> a)
-> Foldable Recipe
forall a. Eq a => a -> Recipe a -> Bool
forall a. Num a => Recipe a -> a
forall a. Ord a => Recipe a -> a
forall m. Monoid m => Recipe m -> m
forall a. Recipe a -> Bool
forall a. Recipe a -> Count
forall a. Recipe a -> [a]
forall a. (a -> a -> a) -> Recipe a -> a
forall m a. Monoid m => (a -> m) -> Recipe a -> m
forall b a. (b -> a -> b) -> b -> Recipe a -> b
forall a b. (a -> b -> b) -> b -> Recipe a -> b
forall (t :: * -> *).
(forall m. Monoid m => t m -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. t a -> [a])
-> (forall a. t a -> Bool)
-> (forall a. t a -> Count)
-> (forall a. Eq a => a -> t a -> Bool)
-> (forall a. Ord a => t a -> a)
-> (forall a. Ord a => t a -> a)
-> (forall a. Num a => t a -> a)
-> (forall a. Num a => t a -> a)
-> Foldable t
$cfold :: forall m. Monoid m => Recipe m -> m
fold :: forall m. Monoid m => Recipe m -> m
$cfoldMap :: forall m a. Monoid m => (a -> m) -> Recipe a -> m
foldMap :: forall m a. Monoid m => (a -> m) -> Recipe a -> m
$cfoldMap' :: forall m a. Monoid m => (a -> m) -> Recipe a -> m
foldMap' :: forall m a. Monoid m => (a -> m) -> Recipe a -> m
$cfoldr :: forall a b. (a -> b -> b) -> b -> Recipe a -> b
foldr :: forall a b. (a -> b -> b) -> b -> Recipe a -> b
$cfoldr' :: forall a b. (a -> b -> b) -> b -> Recipe a -> b
foldr' :: forall a b. (a -> b -> b) -> b -> Recipe a -> b
$cfoldl :: forall b a. (b -> a -> b) -> b -> Recipe a -> b
foldl :: forall b a. (b -> a -> b) -> b -> Recipe a -> b
$cfoldl' :: forall b a. (b -> a -> b) -> b -> Recipe a -> b
foldl' :: forall b a. (b -> a -> b) -> b -> Recipe a -> b
$cfoldr1 :: forall a. (a -> a -> a) -> Recipe a -> a
foldr1 :: forall a. (a -> a -> a) -> Recipe a -> a
$cfoldl1 :: forall a. (a -> a -> a) -> Recipe a -> a
foldl1 :: forall a. (a -> a -> a) -> Recipe a -> a
$ctoList :: forall a. Recipe a -> [a]
toList :: forall a. Recipe a -> [a]
$cnull :: forall a. Recipe a -> Bool
null :: forall a. Recipe a -> Bool
$clength :: forall a. Recipe a -> Count
length :: forall a. Recipe a -> Count
$celem :: forall a. Eq a => a -> Recipe a -> Bool
elem :: forall a. Eq a => a -> Recipe a -> Bool
$cmaximum :: forall a. Ord a => Recipe a -> a
maximum :: forall a. Ord a => Recipe a -> a
$cminimum :: forall a. Ord a => Recipe a -> a
minimum :: forall a. Ord a => Recipe a -> a
$csum :: forall a. Num a => Recipe a -> a
sum :: forall a. Num a => Recipe a -> a
$cproduct :: forall a. Num a => Recipe a -> a
product :: forall a. Num a => Recipe a -> a
Foldable, Functor Recipe
Foldable Recipe
(Functor Recipe, Foldable Recipe) =>
(forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Recipe a -> f (Recipe b))
-> (forall (f :: * -> *) a.
Applicative f =>
Recipe (f a) -> f (Recipe a))
-> (forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Recipe a -> m (Recipe b))
-> (forall (m :: * -> *) a.
Monad m =>
Recipe (m a) -> m (Recipe a))
-> Traversable Recipe
forall (t :: * -> *).
(Functor t, Foldable t) =>
(forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> t a -> f (t b))
-> (forall (f :: * -> *) a. Applicative f => t (f a) -> f (t a))
-> (forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> t a -> m (t b))
-> (forall (m :: * -> *) a. Monad m => t (m a) -> m (t a))
-> Traversable t
forall (m :: * -> *) a. Monad m => Recipe (m a) -> m (Recipe a)
forall (f :: * -> *) a.
Applicative f =>
Recipe (f a) -> f (Recipe a)
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Recipe a -> m (Recipe b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Recipe a -> f (Recipe b)
$ctraverse :: forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Recipe a -> f (Recipe b)
traverse :: forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Recipe a -> f (Recipe b)
$csequenceA :: forall (f :: * -> *) a.
Applicative f =>
Recipe (f a) -> f (Recipe a)
sequenceA :: forall (f :: * -> *) a.
Applicative f =>
Recipe (f a) -> f (Recipe a)
$cmapM :: forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Recipe a -> m (Recipe b)
mapM :: forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Recipe a -> m (Recipe b)
$csequence :: forall (m :: * -> *) a. Monad m => Recipe (m a) -> m (Recipe a)
sequence :: forall (m :: * -> *) a. Monad m => Recipe (m a) -> m (Recipe a)
Traversable, (forall x. Recipe e -> Rep (Recipe e) x)
-> (forall x. Rep (Recipe e) x -> Recipe e) -> Generic (Recipe e)
forall x. Rep (Recipe e) x -> Recipe e
forall x. Recipe e -> Rep (Recipe e) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall e x. Rep (Recipe e) x -> Recipe e
forall e x. Recipe e -> Rep (Recipe e) x
$cfrom :: forall e x. Recipe e -> Rep (Recipe e) x
from :: forall x. Recipe e -> Rep (Recipe e) x
$cto :: forall e x. Rep (Recipe e) x -> Recipe e
to :: forall x. Rep (Recipe e) x -> Recipe e
Generic)
deriving instance ToJSON (Recipe Entity)
deriving instance FromJSON (Recipe Entity)
makeLensesNoSigs ''Recipe
recipeInputs :: Lens' (Recipe e) (IngredientList e)
recipeOutputs :: Lens' (Recipe e) (IngredientList e)
recipeTime :: Lens' (Recipe e) Integer
recipeCatalysts :: Lens' (Recipe e) (IngredientList e)
recipeWeight :: Lens' (Recipe e) Integer
instance ToJSON (Recipe Text) where
toJSON :: Recipe Text -> Value
toJSON (Recipe IngredientList Text
ins IngredientList Text
outs IngredientList Text
cats Integer
time Integer
weight) =
[Pair] -> Value
object ([Pair] -> Value) -> [Pair] -> Value
forall a b. (a -> b) -> a -> b
$
[ Key
"in" Key -> IngredientList Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= IngredientList Text
ins
, Key
"out" Key -> IngredientList Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= IngredientList Text
outs
]
[Pair] -> [Pair] -> [Pair]
forall a. [a] -> [a] -> [a]
++ [Key
"required" Key -> IngredientList Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= IngredientList Text
cats | Bool -> Bool
not (IngredientList Text -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null IngredientList Text
cats)]
[Pair] -> [Pair] -> [Pair]
forall a. [a] -> [a] -> [a]
++ [Key
"time" Key -> Integer -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Integer
time | Integer
time Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
1]
[Pair] -> [Pair] -> [Pair]
forall a. [a] -> [a] -> [a]
++ [Key
"weight" Key -> Integer -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Integer
weight | Integer
weight Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
1]
instance FromJSON (Recipe Text) where
parseJSON :: Value -> Parser (Recipe Text)
parseJSON = String
-> (Object -> Parser (Recipe Text))
-> Value
-> Parser (Recipe Text)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"Recipe" ((Object -> Parser (Recipe Text)) -> Value -> Parser (Recipe Text))
-> (Object -> Parser (Recipe Text))
-> Value
-> Parser (Recipe Text)
forall a b. (a -> b) -> a -> b
$ \Object
v -> do
IngredientList Text
_recipeInputs <- Object
v Object -> Key -> Parser (IngredientList Text)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"in"
IngredientList Text
_recipeOutputs <- Object
v Object -> Key -> Parser (IngredientList Text)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"out"
IngredientList Text
_recipeCatalysts <- Object
v Object -> Key -> Parser (Maybe (IngredientList Text))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"required" Parser (Maybe (IngredientList Text))
-> IngredientList Text -> Parser (IngredientList Text)
forall a. Parser (Maybe a) -> a -> Parser a
.!= []
Integer
_recipeTime <- Object
v Object -> Key -> Parser (Maybe Integer)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"time" Parser (Maybe Integer) -> Integer -> Parser Integer
forall a. Parser (Maybe a) -> a -> Parser a
.!= Integer
1
Integer
_recipeWeight <- Object
v Object -> Key -> Parser (Maybe Integer)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"weight" Parser (Maybe Integer) -> Integer -> Parser Integer
forall a. Parser (Maybe a) -> a -> Parser a
.!= Integer
1
Recipe Text -> Parser (Recipe Text)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Recipe {Integer
IngredientList Text
_recipeInputs :: IngredientList Text
_recipeOutputs :: IngredientList Text
_recipeCatalysts :: IngredientList Text
_recipeTime :: Integer
_recipeWeight :: Integer
_recipeInputs :: IngredientList Text
_recipeOutputs :: IngredientList Text
_recipeCatalysts :: IngredientList Text
_recipeTime :: Integer
_recipeWeight :: Integer
..}
resolveRecipes :: EntityMap -> [Recipe Text] -> Validation [Text] [Recipe Entity]
resolveRecipes :: EntityMap -> [Recipe Text] -> Validation [Text] [Recipe Entity]
resolveRecipes EntityMap
em = ((Recipe Text -> Validation [Text] (Recipe Entity))
-> [Recipe Text] -> Validation [Text] [Recipe Entity]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse ((Recipe Text -> Validation [Text] (Recipe Entity))
-> [Recipe Text] -> Validation [Text] [Recipe Entity])
-> ((Text -> Validation [Text] Entity)
-> Recipe Text -> Validation [Text] (Recipe Entity))
-> (Text -> Validation [Text] Entity)
-> [Recipe Text]
-> Validation [Text] [Recipe Entity]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Validation [Text] Entity)
-> Recipe Text -> Validation [Text] (Recipe Entity)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Recipe a -> f (Recipe b)
traverse) (\Text
t -> Validation [Text] Entity
-> (Entity -> Validation [Text] Entity)
-> Maybe Entity
-> Validation [Text] Entity
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([Text] -> Validation [Text] Entity
forall e a. e -> Validation e a
Failure [Text
t]) Entity -> Validation [Text] Entity
forall e a. a -> Validation e a
Success (Text -> EntityMap -> Maybe Entity
lookupEntityName Text
t EntityMap
em))
instance FromJSONE EntityMap (Recipe Entity) where
parseJSONE :: Value -> ParserE EntityMap (Recipe Entity)
parseJSONE Value
v = do
Recipe Text
rt <- Parser (Recipe Text) -> With EntityMap Parser (Recipe Text)
forall (f :: * -> *) a e. Functor f => f a -> With e f a
liftE (Parser (Recipe Text) -> With EntityMap Parser (Recipe Text))
-> Parser (Recipe Text) -> With EntityMap Parser (Recipe Text)
forall a b. (a -> b) -> a -> b
$ forall a. FromJSON a => Value -> Parser a
parseJSON @(Recipe Text) Value
v
EntityMap
em <- With EntityMap Parser EntityMap
forall (f :: * -> *) e. Monad f => With e f e
getE
let erEnt :: Validation [Text] (Recipe Entity)
erEnt :: Validation [Text] (Recipe Entity)
erEnt = (Text -> Validation [Text] Entity)
-> Recipe Text -> Validation [Text] (Recipe Entity)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Recipe a -> f (Recipe b)
traverse (\Text
t -> Validation [Text] Entity
-> (Entity -> Validation [Text] Entity)
-> Maybe Entity
-> Validation [Text] Entity
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([Text] -> Validation [Text] Entity
forall e a. e -> Validation e a
Failure [Text
t]) Entity -> Validation [Text] Entity
forall e a. a -> Validation e a
Success (Text -> EntityMap -> Maybe Entity
lookupEntityName Text
t EntityMap
em)) Recipe Text
rt
case Validation [Text] (Recipe Entity) -> Either [Text] (Recipe Entity)
forall e a. Validation e a -> Either e a
validationToEither Validation [Text] (Recipe Entity)
erEnt of
Right Recipe Entity
rEnt -> Recipe Entity -> ParserE EntityMap (Recipe Entity)
forall a. a -> With EntityMap Parser a
forall (m :: * -> *) a. Monad m => a -> m a
return Recipe Entity
rEnt
Left [Text]
err -> String -> ParserE EntityMap (Recipe Entity)
forall a. String -> With EntityMap Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> ParserE EntityMap (Recipe Entity))
-> ([Text] -> String)
-> [Text]
-> ParserE EntityMap (Recipe Entity)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall source target. From source target => source -> target
from @Text (Text -> String) -> ([Text] -> Text) -> [Text] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Text] -> Text
T.unlines ([Text] -> ParserE EntityMap (Recipe Entity))
-> [Text] -> ParserE EntityMap (Recipe Entity)
forall a b. (a -> b) -> a -> b
$ [Text]
err
loadRecipes ::
(Has (Throw SystemFailure) sig m, Has (Lift IO) sig m) =>
EntityMap ->
m [Recipe Entity]
loadRecipes :: forall (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Has (Throw SystemFailure) sig m, Has (Lift IO) sig m) =>
EntityMap -> m [Recipe Entity]
loadRecipes EntityMap
em = do
String
fileName <- AssetData -> String -> m String
forall (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Has (Throw SystemFailure) sig m, Has (Lift IO) sig m) =>
AssetData -> String -> m String
getDataFileNameSafe AssetData
Recipes String
f
[Recipe Text]
textRecipes <-
(ParseException -> SystemFailure)
-> ThrowC ParseException m [Recipe Text] -> m [Recipe Text]
forall e2 (sig :: (* -> *) -> * -> *) (m :: * -> *) e1 a.
Has (Throw e2) sig m =>
(e1 -> e2) -> ThrowC e1 m a -> m a
withThrow (Asset -> String -> LoadingFailure -> SystemFailure
AssetNotLoaded (AssetData -> Asset
Data AssetData
Recipes) String
fileName (LoadingFailure -> SystemFailure)
-> (ParseException -> LoadingFailure)
-> ParseException
-> SystemFailure
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParseException -> LoadingFailure
CanNotParseYaml)
(ThrowC ParseException m [Recipe Text] -> m [Recipe Text])
-> (IO (Either ParseException [Recipe Text])
-> ThrowC ParseException m [Recipe Text])
-> IO (Either ParseException [Recipe Text])
-> m [Recipe Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Either ParseException [Recipe Text]
-> ThrowC ParseException m [Recipe Text]
forall e (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has (Throw e) sig m =>
Either e a -> m a
liftEither (Either ParseException [Recipe Text]
-> ThrowC ParseException m [Recipe Text])
-> (IO (Either ParseException [Recipe Text])
-> ThrowC ParseException m (Either ParseException [Recipe Text]))
-> IO (Either ParseException [Recipe Text])
-> ThrowC ParseException m [Recipe Text]
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< IO (Either ParseException [Recipe Text])
-> ThrowC ParseException m (Either ParseException [Recipe Text])
forall (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has (Lift IO) sig m =>
IO a -> m a
sendIO)
(IO (Either ParseException [Recipe Text]) -> m [Recipe Text])
-> IO (Either ParseException [Recipe Text]) -> m [Recipe Text]
forall a b. (a -> b) -> a -> b
$ forall a. FromJSON a => String -> IO (Either ParseException a)
decodeFileEither @[Recipe Text] String
fileName
(Text -> SystemFailure)
-> ThrowC Text m [Recipe Entity] -> m [Recipe Entity]
forall e2 (sig :: (* -> *) -> * -> *) (m :: * -> *) e1 a.
Has (Throw e2) sig m =>
(e1 -> e2) -> ThrowC e1 m a -> m a
withThrow (Asset -> String -> LoadingFailure -> SystemFailure
AssetNotLoaded (AssetData -> Asset
Data AssetData
Recipes) String
fileName (LoadingFailure -> SystemFailure)
-> (Text -> LoadingFailure) -> Text -> SystemFailure
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SystemFailure -> LoadingFailure
SystemFailure (SystemFailure -> LoadingFailure)
-> (Text -> SystemFailure) -> Text -> LoadingFailure
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> SystemFailure
CustomFailure)
(ThrowC Text m [Recipe Entity] -> m [Recipe Entity])
-> (Validation [Text] [Recipe Entity]
-> ThrowC Text m [Recipe Entity])
-> Validation [Text] [Recipe Entity]
-> m [Recipe Entity]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either Text [Recipe Entity] -> ThrowC Text m [Recipe Entity]
forall e (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has (Throw e) sig m =>
Either e a -> m a
liftEither
(Either Text [Recipe Entity] -> ThrowC Text m [Recipe Entity])
-> (Validation [Text] [Recipe Entity]
-> Either Text [Recipe Entity])
-> Validation [Text] [Recipe Entity]
-> ThrowC Text m [Recipe Entity]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Text] -> Text)
-> Either [Text] [Recipe Entity] -> Either Text [Recipe Entity]
forall b c d. (b -> c) -> Either b d -> Either c d
forall (a :: * -> * -> *) b c d.
ArrowChoice a =>
a b c -> a (Either b d) (Either c d)
left (Text -> Text -> Text
T.append Text
"Unknown entities in recipe(s): " (Text -> Text) -> ([Text] -> Text) -> [Text] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text] -> Text
T.intercalate Text
", ")
(Either [Text] [Recipe Entity] -> Either Text [Recipe Entity])
-> (Validation [Text] [Recipe Entity]
-> Either [Text] [Recipe Entity])
-> Validation [Text] [Recipe Entity]
-> Either Text [Recipe Entity]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Validation [Text] [Recipe Entity] -> Either [Text] [Recipe Entity]
forall e a. Validation e a -> Either e a
validationToEither
(Validation [Text] [Recipe Entity] -> m [Recipe Entity])
-> Validation [Text] [Recipe Entity] -> m [Recipe Entity]
forall a b. (a -> b) -> a -> b
$ EntityMap -> [Recipe Text] -> Validation [Text] [Recipe Entity]
resolveRecipes EntityMap
em [Recipe Text]
textRecipes
where
f :: String
f = String
"recipes.yaml"
buildRecipeMap ::
Getter (Recipe Entity) (IngredientList Entity) ->
[Recipe Entity] ->
IntMap [Recipe Entity]
buildRecipeMap :: Getter (Recipe Entity) (IngredientList Entity)
-> [Recipe Entity] -> IntMap [Recipe Entity]
buildRecipeMap Getter (Recipe Entity) (IngredientList Entity)
select [Recipe Entity]
recipeList =
([Recipe Entity] -> [Recipe Entity] -> [Recipe Entity])
-> [(Count, [Recipe Entity])] -> IntMap [Recipe Entity]
forall a. (a -> a -> a) -> [(Count, a)] -> IntMap a
IM.fromListWith [Recipe Entity] -> [Recipe Entity] -> [Recipe Entity]
forall a. [a] -> [a] -> [a]
(++) (((Count, Recipe Entity) -> (Count, [Recipe Entity]))
-> [(Count, Recipe Entity)] -> [(Count, [Recipe Entity])]
forall a b. (a -> b) -> [a] -> [b]
map ((Recipe Entity -> [Recipe Entity])
-> (Count, Recipe Entity) -> (Count, [Recipe Entity])
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second (Recipe Entity -> [Recipe Entity] -> [Recipe Entity]
forall a. a -> [a] -> [a]
: [])) ((Recipe Entity -> [(Count, Recipe Entity)])
-> [Recipe Entity] -> [(Count, Recipe Entity)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Recipe Entity -> [(Count, Recipe Entity)]
mk [Recipe Entity]
recipeList))
where
mk :: Recipe Entity -> [(Count, Recipe Entity)]
mk Recipe Entity
r = [(Entity
e Entity -> Getting Count Entity Count -> Count
forall s a. s -> Getting a s a -> a
^. Getting Count Entity Count
Getter Entity Count
entityHash, Recipe Entity
r) | (Count
_, Entity
e) <- Recipe Entity
r Recipe Entity
-> Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
-> IngredientList Entity
forall s a. s -> Getting a s a -> a
^. Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
Getter (Recipe Entity) (IngredientList Entity)
select]
outRecipeMap :: [Recipe Entity] -> IntMap [Recipe Entity]
outRecipeMap :: [Recipe Entity] -> IntMap [Recipe Entity]
outRecipeMap = Getter (Recipe Entity) (IngredientList Entity)
-> [Recipe Entity] -> IntMap [Recipe Entity]
buildRecipeMap (IngredientList Entity -> f (IngredientList Entity))
-> Recipe Entity -> f (Recipe Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
Getter (Recipe Entity) (IngredientList Entity)
recipeOutputs
inRecipeMap :: [Recipe Entity] -> IntMap [Recipe Entity]
inRecipeMap :: [Recipe Entity] -> IntMap [Recipe Entity]
inRecipeMap = Getter (Recipe Entity) (IngredientList Entity)
-> [Recipe Entity] -> IntMap [Recipe Entity]
buildRecipeMap (IngredientList Entity -> f (IngredientList Entity))
-> Recipe Entity -> f (Recipe Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
Getter (Recipe Entity) (IngredientList Entity)
recipeInputs
catRecipeMap :: [Recipe Entity] -> IntMap [Recipe Entity]
catRecipeMap :: [Recipe Entity] -> IntMap [Recipe Entity]
catRecipeMap = Getter (Recipe Entity) (IngredientList Entity)
-> [Recipe Entity] -> IntMap [Recipe Entity]
buildRecipeMap (IngredientList Entity -> f (IngredientList Entity))
-> Recipe Entity -> f (Recipe Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
Getter (Recipe Entity) (IngredientList Entity)
recipeCatalysts
recipesFor :: IntMap [Recipe Entity] -> Entity -> [Recipe Entity]
recipesFor :: IntMap [Recipe Entity] -> Entity -> [Recipe Entity]
recipesFor IntMap [Recipe Entity]
rm Entity
e = [Recipe Entity] -> Maybe [Recipe Entity] -> [Recipe Entity]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [Recipe Entity] -> [Recipe Entity])
-> Maybe [Recipe Entity] -> [Recipe Entity]
forall a b. (a -> b) -> a -> b
$ Count -> IntMap [Recipe Entity] -> Maybe [Recipe Entity]
forall a. Count -> IntMap a -> Maybe a
IM.lookup (Entity
e Entity -> Getting Count Entity Count -> Count
forall s a. s -> Getting a s a -> a
^. Getting Count Entity Count
Getter Entity Count
entityHash) IntMap [Recipe Entity]
rm
data MissingIngredient = MissingIngredient MissingType Count Entity
deriving (Count -> MissingIngredient -> ShowS
[MissingIngredient] -> ShowS
MissingIngredient -> String
(Count -> MissingIngredient -> ShowS)
-> (MissingIngredient -> String)
-> ([MissingIngredient] -> ShowS)
-> Show MissingIngredient
forall a.
(Count -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Count -> MissingIngredient -> ShowS
showsPrec :: Count -> MissingIngredient -> ShowS
$cshow :: MissingIngredient -> String
show :: MissingIngredient -> String
$cshowList :: [MissingIngredient] -> ShowS
showList :: [MissingIngredient] -> ShowS
Show, MissingIngredient -> MissingIngredient -> Bool
(MissingIngredient -> MissingIngredient -> Bool)
-> (MissingIngredient -> MissingIngredient -> Bool)
-> Eq MissingIngredient
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MissingIngredient -> MissingIngredient -> Bool
== :: MissingIngredient -> MissingIngredient -> Bool
$c/= :: MissingIngredient -> MissingIngredient -> Bool
/= :: MissingIngredient -> MissingIngredient -> Bool
Eq)
data MissingType = MissingInput | MissingCatalyst
deriving (Count -> MissingType -> ShowS
[MissingType] -> ShowS
MissingType -> String
(Count -> MissingType -> ShowS)
-> (MissingType -> String)
-> ([MissingType] -> ShowS)
-> Show MissingType
forall a.
(Count -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Count -> MissingType -> ShowS
showsPrec :: Count -> MissingType -> ShowS
$cshow :: MissingType -> String
show :: MissingType -> String
$cshowList :: [MissingType] -> ShowS
showList :: [MissingType] -> ShowS
Show, MissingType -> MissingType -> Bool
(MissingType -> MissingType -> Bool)
-> (MissingType -> MissingType -> Bool) -> Eq MissingType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MissingType -> MissingType -> Bool
== :: MissingType -> MissingType -> Bool
$c/= :: MissingType -> MissingType -> Bool
/= :: MissingType -> MissingType -> Bool
Eq)
findLacking :: Inventory -> [(Count, Entity)] -> [(Count, Entity)]
findLacking :: Inventory -> IngredientList Entity -> IngredientList Entity
findLacking Inventory
robotInventory = ((Count, Entity) -> Bool)
-> IngredientList Entity -> IngredientList Entity
forall a. (a -> Bool) -> [a] -> [a]
filter ((Count -> Count -> Bool
forall a. Ord a => a -> a -> Bool
> Count
0) (Count -> Bool)
-> ((Count, Entity) -> Count) -> (Count, Entity) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Count, Entity) -> Count
forall a b. (a, b) -> a
fst) (IngredientList Entity -> IngredientList Entity)
-> (IngredientList Entity -> IngredientList Entity)
-> IngredientList Entity
-> IngredientList Entity
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Count, Entity) -> (Count, Entity))
-> IngredientList Entity -> IngredientList Entity
forall a b. (a -> b) -> [a] -> [b]
map (Count, Entity) -> (Count, Entity)
countNeeded
where
countNeeded :: (Count, Entity) -> (Count, Entity)
countNeeded (Count
need, Entity
entity) = (Count
need Count -> Count -> Count
forall a. Num a => a -> a -> a
- Entity -> Inventory -> Count
E.lookup Entity
entity Inventory
robotInventory, Entity
entity)
missingIngredientsFor :: (Inventory, Inventory) -> Recipe Entity -> [MissingIngredient]
missingIngredientsFor :: (Inventory, Inventory) -> Recipe Entity -> [MissingIngredient]
missingIngredientsFor (Inventory
inv, Inventory
ins) (Recipe IngredientList Entity
inps IngredientList Entity
_ IngredientList Entity
cats Integer
_ Integer
_) =
MissingType -> IngredientList Entity -> [MissingIngredient]
mkMissing MissingType
MissingInput (Inventory -> IngredientList Entity -> IngredientList Entity
findLacking Inventory
inv IngredientList Entity
inps)
[MissingIngredient] -> [MissingIngredient] -> [MissingIngredient]
forall a. Semigroup a => a -> a -> a
<> MissingType -> IngredientList Entity -> [MissingIngredient]
mkMissing MissingType
MissingCatalyst (Inventory -> IngredientList Entity -> IngredientList Entity
findLacking Inventory
ins (Inventory -> IngredientList Entity -> IngredientList Entity
findLacking Inventory
inv IngredientList Entity
cats))
where
mkMissing :: MissingType -> IngredientList Entity -> [MissingIngredient]
mkMissing MissingType
k = ((Count, Entity) -> MissingIngredient)
-> IngredientList Entity -> [MissingIngredient]
forall a b. (a -> b) -> [a] -> [b]
map ((Count -> Entity -> MissingIngredient)
-> (Count, Entity) -> MissingIngredient
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (MissingType -> Count -> Entity -> MissingIngredient
MissingIngredient MissingType
k))
knowsIngredientsFor :: (Inventory, Inventory) -> Recipe Entity -> Bool
knowsIngredientsFor :: (Inventory, Inventory) -> Recipe Entity -> Bool
knowsIngredientsFor (Inventory
inv, Inventory
ins) Recipe Entity
recipe =
Inventory -> IngredientList Entity -> Bool
forall {t :: * -> *} {a}.
Foldable t =>
Inventory -> t (a, Entity) -> Bool
knowsAll Inventory
inv (Recipe Entity
recipe Recipe Entity
-> Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
-> IngredientList Entity
forall s a. s -> Getting a s a -> a
^. Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
recipeInputs) Bool -> Bool -> Bool
&& Inventory -> IngredientList Entity -> Bool
forall {t :: * -> *} {a}.
Foldable t =>
Inventory -> t (a, Entity) -> Bool
knowsAll Inventory
ins (Recipe Entity
recipe Recipe Entity
-> Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
-> IngredientList Entity
forall s a. s -> Getting a s a -> a
^. Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
recipeCatalysts)
where
knowsAll :: Inventory -> t (a, Entity) -> Bool
knowsAll Inventory
xs = ((a, Entity) -> Bool) -> t (a, Entity) -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Inventory -> Entity -> Bool
E.contains Inventory
xs (Entity -> Bool) -> ((a, Entity) -> Entity) -> (a, Entity) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, Entity) -> Entity
forall a b. (a, b) -> b
snd)
make ::
(Inventory, Inventory) ->
Recipe Entity ->
Either
[MissingIngredient]
(Inventory, IngredientList Entity, Recipe Entity)
make :: (Inventory, Inventory)
-> Recipe Entity
-> Either
[MissingIngredient]
(Inventory, IngredientList Entity, Recipe Entity)
make (Inventory, Inventory)
invs Recipe Entity
r = (Inventory, IngredientList Entity)
-> (Inventory, IngredientList Entity, Recipe Entity)
forall {a} {b}. (a, b) -> (a, b, Recipe Entity)
finish ((Inventory, IngredientList Entity)
-> (Inventory, IngredientList Entity, Recipe Entity))
-> Either [MissingIngredient] (Inventory, IngredientList Entity)
-> Either
[MissingIngredient]
(Inventory, IngredientList Entity, Recipe Entity)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Inventory, Inventory)
-> Recipe Entity
-> Either [MissingIngredient] (Inventory, IngredientList Entity)
make' (Inventory, Inventory)
invs Recipe Entity
r
where
finish :: (a, b) -> (a, b, Recipe Entity)
finish (a
invTaken, b
out) = (a
invTaken, b
out, Recipe Entity
r)
make' ::
(Inventory, Inventory) ->
Recipe Entity ->
Either
[MissingIngredient]
(Inventory, IngredientList Entity)
make' :: (Inventory, Inventory)
-> Recipe Entity
-> Either [MissingIngredient] (Inventory, IngredientList Entity)
make' invs :: (Inventory, Inventory)
invs@(Inventory
inv, Inventory
_) Recipe Entity
r =
case (Inventory, Inventory) -> Recipe Entity -> [MissingIngredient]
missingIngredientsFor (Inventory, Inventory)
invs Recipe Entity
r of
[] ->
let removed :: Inventory
removed = (Inventory -> (Count, Entity) -> Inventory)
-> Inventory -> IngredientList Entity -> Inventory
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (((Count, Entity) -> Inventory -> Inventory)
-> Inventory -> (Count, Entity) -> Inventory
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((Count -> Entity -> Inventory -> Inventory)
-> (Count, Entity) -> Inventory -> Inventory
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Count -> Entity -> Inventory -> Inventory
deleteCount)) Inventory
inv (Recipe Entity
r Recipe Entity
-> Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
-> IngredientList Entity
forall s a. s -> Getting a s a -> a
^. Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
recipeInputs)
in (Inventory, IngredientList Entity)
-> Either [MissingIngredient] (Inventory, IngredientList Entity)
forall a b. b -> Either a b
Right (Inventory
removed, Recipe Entity
r Recipe Entity
-> Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
-> IngredientList Entity
forall s a. s -> Getting a s a -> a
^. Getting
(IngredientList Entity) (Recipe Entity) (IngredientList Entity)
forall e (f :: * -> *).
Functor f =>
(IngredientList e -> f (IngredientList e))
-> Recipe e -> f (Recipe e)
recipeOutputs)
[MissingIngredient]
missing -> [MissingIngredient]
-> Either [MissingIngredient] (Inventory, IngredientList Entity)
forall a b. a -> Either a b
Left [MissingIngredient]
missing