在列表

时间:2017-04-04 08:10:05

标签: elm

更新,见下文

我正在学习榆树,这是我开始使用的第一种功能语言。 我的模型中有List ResourceList ConverterConverter接收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
        }

1 个答案:

答案 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 }