feat: basic rendering
This commit is contained in:
parent
fe0816d037
commit
0bb81bfc46
|
@ -2,39 +2,34 @@ module Main where
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
import Data.Compactable (compact)
|
|
||||||
import Data.Either (Either(..))
|
import Data.Either (Either(..))
|
||||||
import Data.Foldable (for_)
|
|
||||||
import Data.HashMap as HashMap
|
|
||||||
import Data.String (joinWith)
|
|
||||||
import Data.Tuple (Tuple(..))
|
import Data.Tuple (Tuple(..))
|
||||||
import Data.Tuple.Nested ((/\))
|
import Data.Tuple.Nested ((/\))
|
||||||
import Effect (Effect)
|
import Effect (Effect)
|
||||||
import Effect.Class.Console (logShow)
|
|
||||||
import Effect.Console (log)
|
import Effect.Console (log)
|
||||||
import RealFunction (PortSide(..), RealFunction, SolveM, _constraints, collectConstraints, myFactory, runSolveM, tryFindBound)
|
import Moontorio.Render (RenderFn, renderFactory)
|
||||||
import Run.Except (runFail)
|
import RealFunction (PortSide(..), RealFunction, SolveM, collectConstraints, myFactory, runSolveM, tryFindBoundSolveM)
|
||||||
import Run.Reader.Extra (fromState')
|
|
||||||
|
|
||||||
p :: SolveM (Array RealFunction)
|
p :: SolveM (Array RealFunction)
|
||||||
p = do
|
p = do
|
||||||
collectConstraints
|
collectConstraints
|
||||||
a <- fromState' _constraints $ runFail $ tryFindBound (0 /\ Input)
|
a <- tryFindBoundSolveM (0 /\ Input)
|
||||||
b <- fromState' _constraints $ runFail $ tryFindBound (0 /\ Output)
|
b <- tryFindBoundSolveM (0 /\ Output)
|
||||||
c <- fromState' _constraints $ runFail $ tryFindBound (1 /\ Input)
|
c <- tryFindBoundSolveM (1 /\ Input)
|
||||||
d <- fromState' _constraints $ runFail $ tryFindBound (1 /\ Output)
|
d <- tryFindBoundSolveM (1 /\ Output)
|
||||||
e <- fromState' _constraints $ runFail $ tryFindBound (2 /\ Input)
|
e <- tryFindBoundSolveM (2 /\ Input)
|
||||||
f <- fromState' _constraints $ runFail $ tryFindBound (2 /\ Output)
|
f <- tryFindBoundSolveM (2 /\ Output)
|
||||||
g <- fromState' _constraints $ runFail $ tryFindBound (4 /\ Input)
|
g <- tryFindBoundSolveM (4 /\ Input)
|
||||||
h <- fromState' _constraints $ runFail $ tryFindBound (4 /\ Output)
|
h <- tryFindBoundSolveM (4 /\ Output)
|
||||||
pure $ compact [a, b, c, d, e, f, g, h]
|
pure [a, b, c, d, e, f, g, h]
|
||||||
|
|
||||||
main :: Effect Unit
|
main :: RenderFn -> Effect Unit
|
||||||
main = do
|
main render = do
|
||||||
for_ (HashMap.toArrayBy Tuple myFactory) \(Tuple key value) -> log $ show key <> ": " <> show value
|
-- for_ (HashMap.toArrayBy Tuple myFactory) \(Tuple key value) -> log $ show key <> ": " <> show value
|
||||||
|
|
||||||
case runSolveM myFactory p of
|
case runSolveM myFactory p of
|
||||||
Left err -> log err
|
Left err -> log err
|
||||||
Right (Tuple s f) -> do
|
Right (Tuple s f) -> do
|
||||||
log $ joinWith "\n" $ show <$> s.constraints
|
renderFactory render myFactory s.constraints
|
||||||
logShow $ f <*> pure 0.0
|
-- log $ joinWith "\n" $ show <$> s.constraints
|
||||||
|
-- logShow $ f <*> pure 0.0
|
18
purescript/factorio-throughput/src/Render.purs
Normal file
18
purescript/factorio-throughput/src/Render.purs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module Moontorio.Render where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Data.Foldable (for_)
|
||||||
|
import Data.Tuple.Nested ((/\))
|
||||||
|
import Effect (Effect)
|
||||||
|
import RealFunction (Constraints, Factory, PortSide(..), RealFunction, factoryPorts, tryFindBoundPure, tryFindValuePure)
|
||||||
|
|
||||||
|
type RenderFn = String -> Array RealFunction -> Effect Unit
|
||||||
|
|
||||||
|
renderFactory :: RenderFn -> Factory -> Constraints -> Effect Unit
|
||||||
|
renderFactory render factory constraints = for_ (factoryPorts factory) \portId -> do
|
||||||
|
let inputMax = tryFindBoundPure (portId /\ Input) constraints
|
||||||
|
let outputMax = tryFindBoundPure (portId /\ Output) constraints
|
||||||
|
let actual = tryFindValuePure portId constraints
|
||||||
|
|
||||||
|
render ("Port " <> show portId) [inputMax, outputMax, actual]
|
33
purescript/factorio-throughput/src/Run/Id.purs
Normal file
33
purescript/factorio-throughput/src/Run/Id.purs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module Run.Supply where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Data.Tuple (Tuple(..))
|
||||||
|
import Run (Run, Step(..), lift, on, runAccumPure)
|
||||||
|
import Type.Proxy (Proxy(..))
|
||||||
|
|
||||||
|
-- | Monad providing an infinite supply of values of a particular type.
|
||||||
|
-- | Example use cases: generating unique ids.
|
||||||
|
data SupplyF s a = Supply (s -> a)
|
||||||
|
|
||||||
|
type SUPPLY s r = ( supply :: SupplyF s | r )
|
||||||
|
|
||||||
|
generate :: forall r s. Run (SUPPLY s r) s
|
||||||
|
generate = lift _supply (Supply identity)
|
||||||
|
|
||||||
|
-- | Elimininate the supply monad using a function generating the next value
|
||||||
|
runSupply :: forall r s a. (s -> s) -> s -> Run (SUPPLY s r) a -> Run r a
|
||||||
|
runSupply next
|
||||||
|
= runAccumPure
|
||||||
|
(next >>> \current -> on _supply (Loop <<< handleSupply current) Done)
|
||||||
|
(\s a -> a)
|
||||||
|
where
|
||||||
|
handleSupply :: forall i. s -> SupplyF s i -> Tuple s i
|
||||||
|
handleSupply current (Supply continue) = Tuple current (continue current)
|
||||||
|
|
||||||
|
---------- Typeclass instances
|
||||||
|
derive instance functorSupply :: Functor (SupplyF s)
|
||||||
|
|
||||||
|
--------- SProxies
|
||||||
|
_supply :: Proxy "supply"
|
||||||
|
_supply = Proxy
|
|
@ -5,12 +5,13 @@ import Prelude
|
||||||
import Data.Array (length, mapWithIndex)
|
import Data.Array (length, mapWithIndex)
|
||||||
import Data.Array as Array
|
import Data.Array as Array
|
||||||
import Data.Either (Either)
|
import Data.Either (Either)
|
||||||
import Data.Foldable (for_, minimum)
|
import Data.Foldable (foldMap, for_, minimum)
|
||||||
import Data.FoldableWithIndex (forWithIndex_)
|
import Data.FoldableWithIndex (forWithIndex_)
|
||||||
import Data.Generic.Rep (class Generic)
|
import Data.Generic.Rep (class Generic)
|
||||||
import Data.HashMap (HashMap)
|
import Data.HashMap (HashMap)
|
||||||
import Data.HashMap as HashMap
|
import Data.HashMap as HashMap
|
||||||
import Data.HashMap as Map
|
import Data.HashMap as Map
|
||||||
|
import Data.HashSet as HashSet
|
||||||
import Data.Int (toNumber)
|
import Data.Int (toNumber)
|
||||||
import Data.Lens (Lens')
|
import Data.Lens (Lens')
|
||||||
import Data.Lens.Record (prop)
|
import Data.Lens.Record (prop)
|
||||||
|
@ -20,15 +21,18 @@ import Data.Maybe (Maybe(..), fromJust, fromMaybe)
|
||||||
import Data.Number (infinity)
|
import Data.Number (infinity)
|
||||||
import Data.Show.Generic (genericShow)
|
import Data.Show.Generic (genericShow)
|
||||||
import Data.Traversable (for)
|
import Data.Traversable (for)
|
||||||
import Data.Tuple (Tuple(..), uncurry)
|
import Data.Tuple (Tuple(..), fst, uncurry)
|
||||||
import Data.Tuple.Nested (type (/\), (/\))
|
import Data.Tuple.Nested (type (/\), (/\))
|
||||||
import Functorio.Lens (getAt, modifyAt)
|
import Functorio.Lens (modifyAt)
|
||||||
|
import Math (sin)
|
||||||
import Partial.Unsafe (unsafeCrashWith, unsafePartial)
|
import Partial.Unsafe (unsafeCrashWith, unsafePartial)
|
||||||
import Run (Run, extract)
|
import Run (Run, extract)
|
||||||
import Run.Except (EXCEPT, fail, runExcept)
|
import Run.Except (EXCEPT, fail, runExcept)
|
||||||
import Run.Fail.Extra (traverseFail)
|
import Run.Fail.Extra (traverseFail)
|
||||||
import Run.Reader (READER, ask, runReader)
|
import Run.Reader (READER, ask, runReader)
|
||||||
|
import Run.Reader.Extra (fromState')
|
||||||
import Run.State (STATE, runState)
|
import Run.State (STATE, runState)
|
||||||
|
import Run.Supply (SUPPLY, generate, runSupply)
|
||||||
import Type.Proxy (Proxy(..))
|
import Type.Proxy (Proxy(..))
|
||||||
import Type.Row (type (+))
|
import Type.Row (type (+))
|
||||||
import Visited (VISITED, once, runVisited)
|
import Visited (VISITED, once, runVisited)
|
||||||
|
@ -57,28 +61,34 @@ type Factory = HashMap MachineId Machine
|
||||||
|
|
||||||
---------- Some configs
|
---------- Some configs
|
||||||
yellowBelt :: BeltConfig
|
yellowBelt :: BeltConfig
|
||||||
yellowBelt = { speed: 15.0, delay: 1.0/3.0 }
|
yellowBelt = { speed: 15.0, delay: 4.0/3.0 }
|
||||||
|
|
||||||
redBelt :: BeltConfig
|
redBelt :: BeltConfig
|
||||||
redBelt = { speed: 30.0, delay: 1.0/6.0 }
|
redBelt = { speed: 30.0, delay: 4.0/6.0 }
|
||||||
|
|
||||||
blueBelt :: BeltConfig
|
blueBelt :: BeltConfig
|
||||||
blueBelt = { speed: 45.0, delay: 1.0/8.0 }
|
blueBelt = { speed: 45.0, delay: 4.0/8.0 }
|
||||||
|
|
||||||
-- | Example factory
|
-- | Example factory
|
||||||
myFactory :: Factory
|
myFactory :: Factory
|
||||||
myFactory = Map.fromArray machines
|
myFactory = Map.fromArray machines
|
||||||
where
|
where
|
||||||
machines = mapWithIndex Tuple
|
machines = mapWithIndex Tuple
|
||||||
[ Provider [0, 1, 2] $ const 80.0
|
[ Provider [0, 1] $ startsAtZero $ \t -> 40.0 + 10.0 * sin t
|
||||||
, Belt { input: 0, output: 3, config: yellowBelt }
|
, Belt { input: 0, output: 3, config: yellowBelt }
|
||||||
, Belt { input: 1, output: 4, config: redBelt }
|
, Belt { input: 1, output: 4, config: redBelt }
|
||||||
, Belt { input: 2, output: 5, config: blueBelt }
|
-- , Belt { input: 2, output: 5, config: blueBelt }
|
||||||
, Consumer 3
|
, Consumer 3
|
||||||
, Consumer 4
|
, Consumer 4
|
||||||
, Consumer 5
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
---------- Helpers for real functions
|
||||||
|
type Endomorphism a = a -> a
|
||||||
|
|
||||||
|
startsAtZero :: Endomorphism RealFunction
|
||||||
|
startsAtZero f x | x >= 0.0 = f x
|
||||||
|
| otherwise = 0.0
|
||||||
|
|
||||||
---------- Monad for factory solving
|
---------- Monad for factory solving
|
||||||
type PortData =
|
type PortData =
|
||||||
{ id :: PortId
|
{ id :: PortId
|
||||||
|
@ -105,38 +115,44 @@ data ThroughputConstraint
|
||||||
type Constraints = Array ThroughputConstraint
|
type Constraints = Array ThroughputConstraint
|
||||||
|
|
||||||
type SolveState =
|
type SolveState =
|
||||||
{ constraints :: Constraints
|
{ constraints :: Constraints }
|
||||||
, lastId :: Int }
|
|
||||||
|
|
||||||
type SolveM = Run
|
type SolveM = Run
|
||||||
( EXCEPT String
|
( EXCEPT String
|
||||||
+ STATE SolveState
|
+ STATE SolveState
|
||||||
+ READER Factory
|
+ READER Factory
|
||||||
|
+ SUPPLY Int
|
||||||
+ () )
|
+ () )
|
||||||
|
|
||||||
runSolveM :: forall a. Factory -> SolveM a -> Either String (Tuple SolveState a)
|
runSolveM :: forall a. Factory -> SolveM a -> Either String (Tuple SolveState a)
|
||||||
runSolveM factory = runReader factory >>> runState initialState >>> runExcept >>> extract
|
runSolveM factory = runReader factory >>> runState mempty >>> runExcept >>> runSupply ((+) 1) 0 >>> extract
|
||||||
|
|
||||||
initialState :: SolveState
|
|
||||||
initialState = { constraints: [], lastId: 0 }
|
|
||||||
|
|
||||||
focusBiRelationship :: PortId /\ PortSide -> BiRelationship -> Maybe BiRelationship
|
focusBiRelationship :: PortId /\ PortSide -> BiRelationship -> Maybe BiRelationship
|
||||||
focusBiRelationship place relationship | relationship.p1 == place = Just relationship
|
focusBiRelationship place relationship | relationship.p1 == place = Just relationship
|
||||||
| relationship.p2 == place = Just $ flipBiRelationship relationship
|
| relationship.p2 == place = Just $ flipBiRelationship relationship
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
|
|
||||||
|
focusBiRelationshipWithoutSide :: PortId -> BiRelationship -> Maybe BiRelationship
|
||||||
|
focusBiRelationshipWithoutSide id relationship | fst relationship.p1 == id = Just relationship
|
||||||
|
| fst relationship.p2 == id = Just $ flipBiRelationship relationship
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
flipBiRelationship :: BiRelationship -> BiRelationship
|
flipBiRelationship :: BiRelationship -> BiRelationship
|
||||||
flipBiRelationship { p1, p2, p1top2, p2top1 } = { p1: p2, p2: p1, p1top2: p2top1, p2top1: p1top2 }
|
flipBiRelationship { p1, p2, p1top2, p2top1 } = { p1: p2, p2: p1, p1top2: p2top1, p2top1: p1top2 }
|
||||||
|
|
||||||
|
factoryPorts :: Factory -> HashSet.HashSet PortId
|
||||||
|
factoryPorts = foldMap case _ of
|
||||||
|
Belt { input, output } -> HashSet.fromArray [input, output]
|
||||||
|
Provider outputs _ -> HashSet.fromArray outputs
|
||||||
|
Chest { inputs, outputs } -> HashSet.fromArray (inputs <> outputs)
|
||||||
|
Consumer input -> HashSet.singleton input
|
||||||
|
|
||||||
---------- System solving algorithm
|
---------- System solving algorithm
|
||||||
constrain :: ThroughputConstraint -> SolveM Unit
|
constrain :: ThroughputConstraint -> SolveM Unit
|
||||||
constrain constraint = modifyAt _constraints $ push constraint
|
constrain constraint = modifyAt _constraints $ push constraint
|
||||||
where
|
where
|
||||||
push = flip Array.snoc
|
push = flip Array.snoc
|
||||||
|
|
||||||
getId :: SolveM Int
|
|
||||||
getId = modifyAt _lastId ((+) 1) *> getAt _lastId
|
|
||||||
|
|
||||||
collectConstraints :: SolveM Unit
|
collectConstraints :: SolveM Unit
|
||||||
collectConstraints = do
|
collectConstraints = do
|
||||||
factory <- ask
|
factory <- ask
|
||||||
|
@ -157,6 +173,12 @@ evalExpr = case _ of
|
||||||
tryFindBound :: forall r. PortId /\ PortSide -> Run (READER Constraints r) RealFunction
|
tryFindBound :: forall r. PortId /\ PortSide -> Run (READER Constraints r) RealFunction
|
||||||
tryFindBound at = tryFindBoundImpl at <#> \f time -> extract $ runVisited $ f time
|
tryFindBound at = tryFindBoundImpl at <#> \f time -> extract $ runVisited $ f time
|
||||||
|
|
||||||
|
tryFindBoundSolveM :: PortId /\ PortSide -> SolveM RealFunction
|
||||||
|
tryFindBoundSolveM at = fromState' _constraints $ tryFindBound at
|
||||||
|
|
||||||
|
tryFindBoundPure :: PortId /\ PortSide -> Constraints -> RealFunction
|
||||||
|
tryFindBoundPure at constraints = extract $ runReader constraints $ tryFindBound at
|
||||||
|
|
||||||
tryFindBoundImpl :: forall r k.
|
tryFindBoundImpl :: forall r k.
|
||||||
PortId /\ PortSide ->
|
PortId /\ PortSide ->
|
||||||
Run (READER Constraints r) (Number -> Run (VISITED BiRelationshipId k) Number)
|
Run (READER Constraints r) (Number -> Run (VISITED BiRelationshipId k) Number)
|
||||||
|
@ -174,7 +196,29 @@ tryFindBoundImpl (targetId /\ targetSide) = do
|
||||||
# runReader constraints
|
# runReader constraints
|
||||||
<#> minimum'
|
<#> minimum'
|
||||||
where
|
where
|
||||||
minimum' = minimum >>> fromMaybe infinity
|
minimum' = minimum >>> fromMaybe 0.0
|
||||||
|
|
||||||
|
tryFindValue :: forall r. PortId -> Run (READER Constraints r) RealFunction
|
||||||
|
tryFindValue at = tryFindValueImpl at <#> \f time -> extract $ runVisited $ f time
|
||||||
|
|
||||||
|
tryFindValueImpl :: forall r k. PortId -> Run (READER Constraints r) (Number -> Run (VISITED BiRelationshipId k) Number)
|
||||||
|
tryFindValueImpl targetId = do
|
||||||
|
constraints <- ask
|
||||||
|
pure \time -> constraints
|
||||||
|
# traverseFail case _ of
|
||||||
|
Limit expr _ id | id == targetId -> evalExpr expr <*> pure time
|
||||||
|
BiRelationship id raw
|
||||||
|
| Just relationship <- focusBiRelationshipWithoutSide targetId raw -> do
|
||||||
|
f <- once id fail $ tryFindValueImpl $ fst relationship.p2
|
||||||
|
f (relationship.p1top2 time)
|
||||||
|
_ -> fail
|
||||||
|
# runReader constraints
|
||||||
|
<#> minimum'
|
||||||
|
where
|
||||||
|
minimum' = minimum >>> fromMaybe 0.0
|
||||||
|
|
||||||
|
tryFindValuePure :: PortId -> Constraints -> RealFunction
|
||||||
|
tryFindValuePure at constraints = extract $ runReader constraints $ tryFindValue at
|
||||||
|
|
||||||
collectConstraintsImpl :: MachineId -> Machine -> SolveM Unit
|
collectConstraintsImpl :: MachineId -> Machine -> SolveM Unit
|
||||||
collectConstraintsImpl at = case _ of
|
collectConstraintsImpl at = case _ of
|
||||||
|
@ -204,9 +248,10 @@ collectConstraintsImpl at = case _ of
|
||||||
| otherwise = head.maxOutput time
|
| otherwise = head.maxOutput time
|
||||||
outputsImpl _ _ _ = Nil
|
outputsImpl _ _ _ = Nil
|
||||||
|
|
||||||
Consumer for -> pure unit
|
Consumer for -> do
|
||||||
|
constrain $ Limit (Literal infinity) Output for
|
||||||
Belt { input, output, config } -> do
|
Belt { input, output, config } -> do
|
||||||
biId <- getId
|
biId <- generate
|
||||||
|
|
||||||
constrain $ BiRelationship biId
|
constrain $ BiRelationship biId
|
||||||
{ p1: input /\ Output
|
{ p1: input /\ Output
|
||||||
|
@ -220,9 +265,6 @@ collectConstraintsImpl at = case _ of
|
||||||
_ -> unsafeCrashWith "unimplemented"
|
_ -> unsafeCrashWith "unimplemented"
|
||||||
|
|
||||||
---------- Lenses
|
---------- Lenses
|
||||||
_lastId :: Lens' SolveState Int
|
|
||||||
_lastId = prop (Proxy :: _ "lastId")
|
|
||||||
|
|
||||||
_constraints :: Lens' SolveState (Array ThroughputConstraint)
|
_constraints :: Lens' SolveState (Array ThroughputConstraint)
|
||||||
_constraints = prop (Proxy :: _ "constraints")
|
_constraints = prop (Proxy :: _ "constraints")
|
||||||
|
|
||||||
|
|
38
purescript/factorio-throughput/src/index.js
Normal file
38
purescript/factorio-throughput/src/index.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { main } from "Main.purs";
|
||||||
|
import functionPlot from "function-plot";
|
||||||
|
|
||||||
|
let lastId = 0;
|
||||||
|
const root = document.body;
|
||||||
|
|
||||||
|
const width = 400;
|
||||||
|
const height = 250;
|
||||||
|
|
||||||
|
const render = (name) => (functions) => () => {
|
||||||
|
const currentId = ++lastId;
|
||||||
|
|
||||||
|
console.log("Renering!!!");
|
||||||
|
|
||||||
|
const node = document.createElement("div");
|
||||||
|
node.id = currentId;
|
||||||
|
node.title = name;
|
||||||
|
node.className = "graph";
|
||||||
|
|
||||||
|
root.appendChild(node);
|
||||||
|
|
||||||
|
const functionData = functions.map((fn) => ({
|
||||||
|
fn: (scope) => fn(scope.x),
|
||||||
|
graphType: "polyline",
|
||||||
|
}));
|
||||||
|
|
||||||
|
functionPlot({
|
||||||
|
target: node,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
yAxis: { domain: [-5, 35] },
|
||||||
|
xAxis: { domain: [-1, 5] },
|
||||||
|
grid: true,
|
||||||
|
data: functionData,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
main(render)();
|
Loading…
Reference in a new issue