2017-04-04 53 views
1

更新,請參閱下面

我目前在學習Elm,這是我的第一個函數式語言。 我在我的模型中有List ResourceList Converter。 A 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

能否請添加輸入數據和期望的輸出的一個例子? – halfzebra

+0

我添加了一些示例來涵蓋用例。 –

+0

在你的情況'List.map'將比'List.foldr'更有幫助,但更好的是從'List'切換到'Dict'。 – daniula

回答

1

我期望你的model.resources不會有持有一個的ResourceType的多個值這麼一個記錄會使事情變得更容易爲( - >假設資源= [能源2,水1,節能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 

獎勵:梳子INE加減法,功能類型的功能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 } 
+0

感謝您的想法!記錄的這種用法確實有助於重構我的代碼。我認爲你的代碼不會運行,因爲你擺脫了資源類型,並且想要使用它們。此外updateGame函數與foldr的部分不適用於我。 –

+0

@GáborFekete我剛剛列出了我會改變的功能 - 只需保留ResourceType和Converter類型。我已經將convert函數的順序更新爲pipeable,並更正了substractResource的類型聲明。 (我現在無法測試) – farmio

+0

我將當前工作的代碼添加到了原始問題中。 –