From d2b9cfadfe48337a3954ce3dffa6f79b610f2a6a Mon Sep 17 00:00:00 2001 From: Max Wilson Date: Fri, 5 Jan 2024 15:29:38 -0800 Subject: [PATCH] (red) test is correctly detecting lack of implementation for detecting whether an offer is selected or not. --- test/Chargen.Accept.fs | 59 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/test/Chargen.Accept.fs b/test/Chargen.Accept.fs index 051fd82..401d8dd 100644 --- a/test/Chargen.Accept.fs +++ b/test/Chargen.Accept.fs @@ -24,15 +24,17 @@ type OfferInput = { selected: Set } with static member fresh = { selected = Set.empty } -type 't Offer = Offer of (OfferInput -> 't) -type 't ListOffer = ('t list) Offer -type 't OptionOffer = ('t option) Offer - type OfferConfig = { key: Key option label: string option } with static member blank = { key = None; label = None } +type 't Offer = { config: OfferConfig; func: (OfferConfig -> OfferInput -> 't * MenuOutput) } + with + member this.recur input = this.func this.config input +type 't ListOffer = ('t list) Offer +type 't OptionOffer = ('t option) Offer + open type OfferConfig type 'reactElement RenderApi = { @@ -71,15 +73,46 @@ let render (render: 'reactElement RenderApi) (menus: MenuOutput list) = menus |> List.map (recur true render.unconditional) |> render.combine type Op = - static member skill v: 't OptionOffer = notImpl() - static member trait' v: 't OptionOffer = notImpl() + static member offer(config, func) = { config = config; func = func } + static member offer func = Op.offer(OfferConfig.blank, func) + + static member skill (name: string, level: int): _ OptionOffer = + Op.skill({ OfferConfig.blank with label = Some $"{name} %+d{level}" }, (name, [level])) + static member skill (name: string, levels: int list): _ OptionOffer = + Op.skill(OfferConfig.blank, (name, levels)) + static member skill (config, (name: string, levels: int list)): _ OptionOffer = + Op.offer(config, fun config input -> None, (Leaf (defaultArg config.label (toString name)))) + + static member trait' (v: 't): 't OptionOffer = + Op.trait'(OfferConfig.blank, v) + static member trait' (config, v): 't OptionOffer = + Op.offer(config, fun config input -> Some v, (Leaf (defaultArg config.label (toString v)))) + static member budgeted v: 't ListOffer = notImpl() - static member either v : 't OptionOffer = notImpl() + + static member either options : 't OptionOffer = + Op.either(OfferConfig.blank, options) + static member either (config, options: 't OptionOffer list) : 't OptionOffer = + Op.offer( + config, + fun config input -> + let children = options |> List.map (fun o -> o.recur input |> Tuple2.mapfst Option.isSome) + None, Either(config.label, children) + ) + static member and' v : 't OptionOffer = notImpl() static member eitherN v : 't ListOffer = notImpl() static member andN' v : 't ListOffer = notImpl() - static member promote (o: 't OptionOffer): 't ListOffer = notImpl() - static member evaluate (state: OfferInput) (offers: _ Offer list) = notImpl() + static member promote (o: 't OptionOffer): 't ListOffer = + Op.offer( + o.config, + fun config input -> + let (v, menu) = o.recur input + List.ofOption v, menu + ) + static member evaluate (state: OfferInput) (offer: _ Offer) = + offer.recur state + let newKey txt = $"{txt}-{System.Guid.NewGuid()}" let label txt = { blank with label = Some txt } open type Op @@ -107,7 +140,7 @@ let swash(): Trait' ListOffer list = [ skill("Acrobatics", [1..3]) ]) let mainWeapons = ["Rapier"; "Broadsword"; "Polearm"; "Two-handed sword"] |> List.map (fun name -> name, newKey name) - let weaponsAt (bonus: int) = mainWeapons |> List.map (fun (name, key) -> skill({ blank with key = Some key }, name, bonus)) + let weaponsAt (bonus: int) = mainWeapons |> List.map (fun (name, key) -> skill({ blank with key = Some key }, (name, [bonus]))) eitherN [ either(label "Sword!", weaponsAt +5) |> promote andN'(label "Sword and Dagger", [either(weaponsAt +4); skill("Main-gauche", +1)]) @@ -155,8 +188,12 @@ let pseudoReactApi = { combine = Fragment } +[] +let unit1 = testCase "Unit.Chargen.unite1" <| fun () -> + test <@ either[trait' "Fight"; trait' "Hide"] |> evaluate OfferInput.fresh |> snd = Either(None, [false, Leaf "Fight"; false, Leaf "Hide"]) @> + let proto1 = testCase "proto1" <| fun () -> - let actual = swash() |> evaluate OfferInput.fresh // shouldn't actually use OfferInput.fresh here. Need to pick the options we want to show up in pseudoActual.s + let actual = swash() |> List.map (evaluate OfferInput.fresh >> snd) // shouldn't actually use OfferInput.fresh here. Need to pick the options we want to show up in pseudoActual.s let pseudoActual = // pseudo-actual because actual will be created from templates + OfferInput (i.e. selected keys), not hardwired as Menus, but that's still TODO let menus = [ Leveled("Climbing", 1)