From ef9beb0814c36cda979a4ed7e9175e72e69540ac Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Sat, 24 Feb 2024 11:31:28 +0100 Subject: New application: build upgrade helper for aoo --- calculette_aoo/bin/dune | 3 + calculette_aoo/bin/main.ml | 49 +++++++++ calculette_aoo/index.html | 180 ++++++++++++++++++++++++++++++++++ calculette_aoo/js/dune | 19 ++++ calculette_aoo/js/main.ml | 97 ++++++++++++++++++ calculette_aoo/lib/build.ml | 135 +++++++++++++++++++++++++ calculette_aoo/lib/build.mli | 35 +++++++ calculette_aoo/lib/carac.ml | 40 ++++++++ calculette_aoo/lib/carac.mli | 17 ++++ calculette_aoo/lib/dune | 2 + calculette_aoo/test/dune | 2 + calculette_aoo/test/test_optim_aoo.ml | 0 12 files changed, 579 insertions(+) create mode 100644 calculette_aoo/bin/dune create mode 100644 calculette_aoo/bin/main.ml create mode 100644 calculette_aoo/index.html create mode 100644 calculette_aoo/js/dune create mode 100644 calculette_aoo/js/main.ml create mode 100644 calculette_aoo/lib/build.ml create mode 100644 calculette_aoo/lib/build.mli create mode 100644 calculette_aoo/lib/carac.ml create mode 100644 calculette_aoo/lib/carac.mli create mode 100644 calculette_aoo/lib/dune create mode 100644 calculette_aoo/test/dune create mode 100644 calculette_aoo/test/test_optim_aoo.ml diff --git a/calculette_aoo/bin/dune b/calculette_aoo/bin/dune new file mode 100644 index 0000000..1177b56 --- /dev/null +++ b/calculette_aoo/bin/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries aoo)) diff --git a/calculette_aoo/bin/main.ml b/calculette_aoo/bin/main.ml new file mode 100644 index 0000000..c738cd7 --- /dev/null +++ b/calculette_aoo/bin/main.ml @@ -0,0 +1,49 @@ +let cout_a = (800, 200, 100) +let cout_m = (110, 55, 35) +let cout_rm = (50, 40, 20) +let cout_pm = (5, 3, 1) +let cout_fm = (100, 50, 30) +let fm_oponent = 10 + +let env : Aoo.Build.env = + { + cost_max = 1800 + ; max_tours = 5.0 + ; cout_sort = 8 + ; degat_sort = 6 + ; fm_oponent + ; frequencies = Aoo.Build.buil_freq_table 9 fm_oponent + } + +(* +let () = Random.self_init () + +let roll_and_accumulate dices = + Seq.repeat () |> Seq.take dices + |> Seq.fold_left (fun res _ -> res + (1 + Random.int 6)) 0 + +let delta_carac level = + Seq.repeat () |> Seq.take 1000000 + |> Seq.fold_left + (fun acc _ -> + if roll_and_accumulate level > roll_and_accumulate (level - 2) then acc + else acc + 1) + 0 +*) + +(* Définition des caractéristiques *) +let a = Aoo.Carac.create 2 cout_a +let m = Aoo.Carac.create 5 cout_m +let rm = Aoo.Carac.create 7 cout_rm +let pm = Aoo.Carac.create 40 cout_pm +let fm = Aoo.Carac.create 9 cout_fm +let default = Aoo.Build.{ a; m; rm; pm; fm } + +let () = + let build = + Aoo.Build.traverse env + (Aoo.Build.cost default, Aoo.Build.score env default) + (Aoo.Build.upgrade env default) + in + + Aoo.Build.repr env Format.std_formatter build diff --git a/calculette_aoo/index.html b/calculette_aoo/index.html new file mode 100644 index 0000000..6dd2955 --- /dev/null +++ b/calculette_aoo/index.html @@ -0,0 +1,180 @@ + + + +AOO PC Builder + + + + + + + + + + + + + +
+

AOO PC Builder

+
+ + +
+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Résultats

+ +
+ + diff --git a/calculette_aoo/js/dune b/calculette_aoo/js/dune new file mode 100644 index 0000000..8732ae0 --- /dev/null +++ b/calculette_aoo/js/dune @@ -0,0 +1,19 @@ +(executable + (name main) + (libraries + brr + note.brr + application + aoo + ) + (modes js) + (preprocess (pps js_of_ocaml-ppx)) + (link_flags (:standard -no-check-prims)) + ) + +(rule + (targets aoo.js) + (deps main.bc.js) + (action (copy %{deps} %{targets}))) + + diff --git a/calculette_aoo/js/main.ml b/calculette_aoo/js/main.ml new file mode 100644 index 0000000..9a63d12 --- /dev/null +++ b/calculette_aoo/js/main.ml @@ -0,0 +1,97 @@ +open Brr + +let ( let=? ) : 'a option -> ('a -> unit) -> unit = + fun f opt -> Option.iter opt f + +let get_int_value element = + let value = El.prop El.Prop.value element in + match Jstr.to_int value with + | Some v -> v + | None -> 0 + +let cout_a = (800, 200, 100) +let cout_m = (110, 55, 35) +let cout_rm = (50, 40, 20) +let cout_pm = (5, 3, 1) +let cout_fm = (100, 50, 30) + +let get_element_by_id id = + Jstr.of_string id |> Brr.Document.find_el_by_id Brr.G.document + +let get_int_element id = + Jstr.of_string id + |> Brr.Document.find_el_by_id Brr.G.document + |> Option.map get_int_value |> Option.value ~default:0 + +let eval _ = + let a_element = get_int_element "a" in + let m_element = get_int_element "m" in + let rm_element = get_int_element "rm" in + let fm_element = get_int_element "fm" in + let pm_element = get_int_element "pm" in + + let a_bonus = get_int_element "a_bonus" in + let m_bonus = get_int_element "m_bonus" in + let rm_bonus = get_int_element "rm_bonus" in + let fm_bonus = get_int_element "fm_bonus" in + let pm_bonus = get_int_element "pm_bonus" in + + let a = Aoo.Carac.create ~bonus:a_bonus a_element cout_a in + let m = Aoo.Carac.create ~bonus:m_bonus m_element cout_m in + let rm = Aoo.Carac.create ~bonus:rm_bonus rm_element cout_rm in + let fm = Aoo.Carac.create ~bonus:fm_bonus fm_element cout_fm in + let pm = Aoo.Carac.create ~bonus:pm_bonus pm_element cout_pm in + + let build = Aoo.Build.{ a; m; rm; fm; pm } in + + (* Evaluate the cost for the given upgrades *) + let cost = Aoo.Build.cost build in + + let fm_oponent = get_int_element "fm_oponent" in + let env : Aoo.Build.env = + { + cost_max = get_int_element "xp" + cost + ; max_tours = float (get_int_element "tours") + ; cout_sort = get_int_element "cost" + ; degat_sort = get_int_element "dammage" + ; fm_oponent + ; frequencies = Aoo.Build.buil_freq_table fm_element fm_oponent + } + in + let upgrades = + match Aoo.Build.upgrade env build with + | [] -> + (* If there is no upgrade available, return the current build as a + solution instead of nothing. *) + [ build ] + | upgrades -> upgrades + in + let build = + Aoo.Build.traverse env + (Aoo.Build.cost build, Aoo.Build.score env build) + upgrades + in + + let () = Aoo.Build.repr env Format.str_formatter build in + let result = Format.flush_str_formatter () in + let=? result_element = get_element_by_id "result" in + + El.set_children result_element [ El.txt' result ]; + () + +let main () = + let=? btn = get_element_by_id "form" in + let _ = + Ev.listen Brr_io.Form.Ev.submit + (fun ev -> + Ev.prevent_default ev; + eval ev) + (El.as_target btn) + in + + eval () + +let () = + let open Jv in + let main = obj [| ("run", repr main) |] in + set global "lib" main diff --git a/calculette_aoo/lib/build.ml b/calculette_aoo/lib/build.ml new file mode 100644 index 0000000..ddefe67 --- /dev/null +++ b/calculette_aoo/lib/build.ml @@ -0,0 +1,135 @@ +type build = { + a : Carac.t + ; m : Carac.t + ; rm : Carac.t + ; pm : Carac.t + ; fm : Carac.t +} + +type env = { + cout_sort : int + ; degat_sort : int + ; max_tours : float + ; fm_oponent : int + ; cost_max : int + ; frequencies : (int * float) list +} + +let roll_and_accumulate dices = + Seq.repeat () |> Seq.take dices + |> Seq.fold_left (fun res _ -> res + (1 + Random.int 3)) 0 + +let delta_carac level thresold = + Seq.repeat () |> Seq.take 10000 + |> Seq.fold_left + (fun acc _ -> + if roll_and_accumulate thresold > roll_and_accumulate level then acc + else acc + 1) + 0 + +let get_chance : env -> int -> float = + fun env delta -> + match List.assoc_opt delta env.frequencies with + | None -> 1.0 + | Some v -> v + +(** Build a list with the differents percentages to hit *) +let buil_freq_table from thresold = + let () = Random.self_init () in + List.init 6 (fun i -> + (from - thresold + i, float (delta_carac (from + i) thresold) /. 10000.)) + +let eval : build -> env -> float * float = + fun { a; m; rm; pm; fm } env -> + let cout_tour = (Carac.value a * env.cout_sort) - Carac.value rm in + let nb_tour = + if cout_tour > 0 then + float (Carac.value pm) /. float cout_tour |> Float.min env.max_tours + else env.max_tours + in + + let degats = + float (Carac.value a * (env.degat_sort + Carac.value m - 5)) + *. get_chance env (Carac.value fm - env.fm_oponent) + in + (degats, nb_tour) + +let repr_degats : env -> Format.formatter -> build -> unit = + fun env out build -> + let delta = get_chance env (Carac.value build.fm - env.fm_oponent) in + Format.fprintf out "%d A × (%d du sorts + %d M - %d M) × %.2f %%" + (Carac.value build.a) env.degat_sort (Carac.value build.m) 5 (delta *. 100.); + let sum = env.degat_sort + Carac.value build.m - 5 in + let prod = Carac.value build.a * sum in + Format.fprintf out "@;%d A × %d = %d@;" (Carac.value build.a) sum prod; + Format.fprintf out "%d × %.2f = %.2f" prod delta (delta *. float prod); + () + +let cost : build -> int = + fun { a; m; rm; pm; fm } -> + Carac.(cout a + cout m + cout rm + cout pm + cout fm) + +let repr : env -> Format.formatter -> build -> unit = + fun env formatter build -> + let degats, nb_tour = eval build env in + + Format.fprintf formatter + {|Caractéristiques retenues : +- A : %a +- M : %a +- FM: %a +- RM: %a +- PM: %a +|} + Carac.repr build.a Carac.repr build.m Carac.repr build.fm Carac.repr + build.rm Carac.repr build.pm; + Format.fprintf formatter + "@[Le magicien fera %.2f degats par tour pour un total de %d sur %.2f \ + tours@;\ + (@[%a@])@;" + degats + (int_of_float (nb_tour *. degats)) + nb_tour (repr_degats env) build; + Format.fprintf formatter "Le cout de ce build est de %d@]@." (cost build) + +let score : env -> build -> float = + fun env build -> + let d, v = eval build env in + d *. v + +(* Upgrade each caracteristic and keep only the values in range *) +let upgrade : env -> build -> build list = + fun env build -> + [ + { build with a = Carac.incr build.a } + ; { build with m = Carac.incr build.m } + ; { build with rm = Carac.incr build.rm } + ; { build with pm = Carac.incr build.pm } + ; { build with pm = Carac.incr ~step:3 build.pm } + ; { build with pm = Carac.incr ~step:10 build.pm } + ; { build with fm = Carac.incr build.fm } + ] + |> List.filter (fun f -> cost f <= env.cost_max) + +let rec traverse env (last_cost, last_score) = function + | [] -> failwith "Invalid data" + | hd :: [] -> hd + | hd :: tl -> + let score' = score env hd and cost' = cost hd in + if cost' > last_cost && score' < last_score then + traverse env (last_cost, last_score) tl + else + (* Get the new elements to add and filter them if they do not provide + anything better *) + let new_builds = + upgrade env hd + |> List.filter (fun build -> + List.for_all + (fun element -> + cost build > cost element + || score env build > score env element) + tl) + in + + (* For each new element, remove all the obsolete builds *) + traverse env (cost', score') (List.rev_append tl new_builds) diff --git a/calculette_aoo/lib/build.mli b/calculette_aoo/lib/build.mli new file mode 100644 index 0000000..3e2e690 --- /dev/null +++ b/calculette_aoo/lib/build.mli @@ -0,0 +1,35 @@ +type build = { + a : Carac.t + ; m : Carac.t + ; rm : Carac.t + ; pm : Carac.t + ; fm : Carac.t +} + +type env = { + cout_sort : int + ; degat_sort : int + ; max_tours : float + ; fm_oponent : int + ; cost_max : int + ; frequencies : (int * float) list +} + +val buil_freq_table : int -> int -> (int * float) list +(** Build a list with the differents percentages to hit *) + +val cost : build -> int +(** Get the cost for a build *) + +val score : env -> build -> float +(** Get the score for the build *) + +val traverse : env -> int * float -> build list -> build +(** Test differents upgrade configuration and present the best one with the + constraints given in [env]. + + The costt for the upgrade will not exceed the property [env.cost_max] + *) + +val upgrade : env -> build -> build list +val repr : env -> Format.formatter -> build -> unit diff --git a/calculette_aoo/lib/carac.ml b/calculette_aoo/lib/carac.ml new file mode 100644 index 0000000..a48c734 --- /dev/null +++ b/calculette_aoo/lib/carac.ml @@ -0,0 +1,40 @@ +type cout_carac = int * int * int + +type t = { + value : int + ; couts : cout_carac + ; bonus : int +} + +let create : ?bonus:int -> int -> cout_carac -> t = + fun ?(bonus = 0) value couts -> { value; couts; bonus } + +let incr ?(step = 1) t = { t with bonus = t.bonus + step } + +(* +Evaluate the cost for the successives upgrades. + +I’m pretty sure this can be transformed into a linear function, but I do not see how… + + c0 * t.bonus ++ max 0 ((((t.bonus - 1) * 2) - 1) * c1) ++ ? + + *) +let cout : t -> int = + fun t -> + let c0, c1, c2 = t.couts in + let rec c acc t = + match t with + | 0 -> acc + | 1 -> acc + c0 + | 2 -> c (acc + c0 + c1) (t - 1) + | 3 -> c (acc + c0 + (c1 * 2)) (t - 1) + | n -> c (acc + c0 + (c1 * 2) + ((n - 3) * c2)) (t - 1) + in + c 0 t.bonus + +let value t = t.value + t.bonus + +let repr : Format.formatter -> t -> unit = + fun out t -> Format.fprintf out "%d (+%d)" (value t) t.bonus diff --git a/calculette_aoo/lib/carac.mli b/calculette_aoo/lib/carac.mli new file mode 100644 index 0000000..f364c81 --- /dev/null +++ b/calculette_aoo/lib/carac.mli @@ -0,0 +1,17 @@ +type t +type cout_carac = int * int * int + +val create : ?bonus:int -> int -> cout_carac -> t + +val cout : t -> int +(** Get the cost for the upgrades for this property *) + +val value : t -> int +(** Get the value (including upgrades) for this property *) + +val incr : ?step:int -> t -> t +(** Increment this property. + step is default 1. + *) + +val repr : Format.formatter -> t -> unit diff --git a/calculette_aoo/lib/dune b/calculette_aoo/lib/dune new file mode 100644 index 0000000..54b5a85 --- /dev/null +++ b/calculette_aoo/lib/dune @@ -0,0 +1,2 @@ +(library + (name aoo)) diff --git a/calculette_aoo/test/dune b/calculette_aoo/test/dune new file mode 100644 index 0000000..859e67e --- /dev/null +++ b/calculette_aoo/test/dune @@ -0,0 +1,2 @@ +(test + (name test_optim_aoo)) diff --git a/calculette_aoo/test/test_optim_aoo.ml b/calculette_aoo/test/test_optim_aoo.ml new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3