我正在学习榆树,这是我开始使用的第一种功能语言。
我的模型中有List Resource
和List Converter
。 Converter
接收List Resource
并输出另一个List Resource
。转换仅在有足够输入时发生。然后我想迭代每个Converter
并减少/增加资源数量。
我无法绕过解决方案。我觉得List.foldr
是我应该使用的,但我不确定。我应该如何更新模型,以便每次迭代都会更新?
这是我到目前为止所做的:
type alias Resource =
{ resourcetype : ResourceType
, quantity: Int
}
type ResourceType
= Energy
| Water
| Metal
type alias Converter =
{ convertertype : ConverterType
, intakes : List (ResourceType, Int)
, outputs: List (ResourceType, Int)
}
type ConverterType
= SolarCollector
| Humidifer
| MetalCollector
type alias Model =
{ resources : List Resource
, converters : List Converter
}
type Msg
= Tick Time
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick time ->
(updateGame model, Cmd.none)
convert : List Resource -> Converter -> List Resource
convert resources converter =
let
intakes = converter.intakes
outputs = converter.outputs
-- TODO, remove intakes from resources and add outputs to resources
in
resources
updateGame : Model -> Model
updateGame model =
-- TODO, call convert on each converter and update model
model
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every second Tick
资源不会耗尽:
--Initial converters
[ SolarCollector [] [Energy 2]
, MetalCollector [Energy 1] [Metal 1]
]
--Initial resources
[ Energy 10, Metal 0]
--After 5 ticks
[ Energy 15, Metal 5]
资源将耗尽:
--Initial converters
[ MetalCollector [Energy 2] [Metal 1]
, SolarCollector [] [Energy 1]
]
--Initial resources
[ Energy 4, Metal 0]
--Tick 1
[ Energy 3, Metal 1] -- MetalCollector uses 2 Energy to get 1 Metal, SolarCollector will generate 1 Energy
--Tick 2
[ Energy 2, Metal 2] -- MC -2E,+1M; SC +1E
--Tick 3
[ Energy 1, Metal 3] -- MC -2E,+1M; SC +1E
--Tick 4
[ Energy 2, Metal 3] -- SC +1E
-- Notice how this tick the MetalCollector didn't run because of list order.
--Tick 5
[ Energy 1, Metal 4] -- MC -2E,+1M; SC +1E
我搞定了! Converter
的顺序现在很重要,每次打勾都需要适量的资源。这是最终的工作代码,谢谢你的帮助!在这里,您可以尝试一下:https://ellie-app.com/RB3YsxwbGja1/0
module Main exposing (..)
import Html exposing (Html, div, text, program, ul, li)
import Time exposing (Time, second)
type alias Resources =
{ energy : Float
, water : Float
, metal : Float
}
resourcesToList : Resources -> List Float
resourcesToList resources =
[resources.energy, resources.water, resources.metal]
noResource : Resources
noResource = Resources 0 0 0
energyResource : Float -> Resources
energyResource energy =
{ noResource | energy = energy }
waterResource : Float -> Resources
waterResource water =
{ noResource | water = water }
metalResource : Float -> Resources
metalResource metal =
{ noResource | metal = metal }
type alias Converter =
{ convertertype : ConverterType
, quantity : Int
, intakes : Resources
, outputs: Resources
}
type ConverterType
= SolarCollector
| Humidifer
| MetalCollector
initialResources : Resources
initialResources =
{ noResource | energy = 10}
initialConverters : List Converter
initialConverters =
[ { convertertype = MetalCollector
, quantity = 2
, intakes = energyResource 1
, outputs = metalResource 1
}
, { convertertype = SolarCollector
, quantity = 2
, intakes = noResource
, outputs = energyResource 1
}
, { convertertype = Humidifer
, quantity = 1
, intakes = energyResource 1
, outputs = waterResource 1
}
]
convert : Converter -> Resources -> Resources
convert converter resources =
let
activatedQuantity =
toFloat (getActiveConverterQuantity converter resources)
getActiveConverterQuantity : Converter -> Resources -> Int
getActiveConverterQuantity converter resources =
let
resourcesList = resourcesToList resources
intakesList = resourcesToList converter.intakes
finalList =
List.map2 (,) resourcesList intakesList
|> List.filter (\(r,i) -> i > 0)
|> List.map (\(r,i) -> floor (r/i))
in
case List.maximum finalList of
Just q ->
min q converter.quantity
Nothing ->
converter.quantity
subtractIntakes : Converter -> Resources -> Resources
subtractIntakes converter resources =
{ resources
| energy = resources.energy - activatedQuantity * converter.intakes.energy
, water = resources.water - activatedQuantity * converter.intakes.water
, metal = resources.metal - activatedQuantity * converter.intakes.metal
}
addOutputs : Converter -> Resources -> Resources
addOutputs converter resources =
{ resources
| energy = resources.energy + activatedQuantity * converter.outputs.energy
, water = resources.water + activatedQuantity * converter.outputs.water
, metal = resources.metal + activatedQuantity * converter.outputs.metal
}
in
resources
|> subtractIntakes converter
|> addOutputs converter
-- MODEL
type alias Model =
{ resources : Resources
, converters : List Converter
}
init : ( Model, Cmd Msg )
init =
( { resources = initialResources
, converters = initialConverters
}
, Cmd.none
)
-- MESSAGES
type Msg
= Tick Time
-- VIEW
view : Model -> Html Msg
view model =
div []
[ div[] [text (toString model.resources)]
, div[]
[ model.converters
|> List.map
(\c -> li [] [text (toString (c.convertertype,c.quantity,c.intakes))])
|> ul []
]
]
-- UPDATE
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick time ->
(updateGame model, Cmd.none)
updateGame : Model -> Model
updateGame model =
let
newResources = model.converters |> List.foldr convert model.resources
in
{ model | resources = newResources }
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every second Tick
-- MAIN
main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
答案 0 :(得分:1)
我希望你的model.resources不必拥有一个ResourceType的多个值,所以一个Record会让事情变得更容易( - >假设资源= [energy 2,water 1,energy 2]将不会感觉)
type alias Resources =
{ energy : Int
, water : Int
, metal : Int
}
type alias Model =
{ resources : Resources
, converters : List Converter
}
现在您可以折叠每个转换器的入口并累积资源
convert : Converter -> Resources -> Resources
convert {intakes, outputs} resources =
let
substractResource : (ResourceType, Int) -> Resources -> Resources
substractResource (rType, quantity) res =
case rType of
Energy ->
{ res | energy = energy - quantity }
-- TODO: same for Water and Metal
-- TODO: addResource function
hasEnoughResources newRes =
case negativeResources newRes of
-- TODO: function to check if any value of the new resources Record is negative
True -> -- return original resources if converter can not run
resources
False -> -- add outputs to res and return new recources
outputs |> List.foldr addResource newRes
in
intakes
|> List.foldr substractResource resources
|> hasEnoughResources
Bonus:将add和substract函数组合为1个(Int -> Int) -> (ResourceType, Int) -> Resources -> Resources
类型的函数,并将其称为calcResource << (+)
updateGame : Model -> Model
updateGame model =
let
newResources = model.converters |> List.foldr convert model.resources
in
{ model | resources = newResources }