diff options
| author | Sébastien Dailly <sebastien@dailly.me> | 2024-02-24 11:31:28 +0100 | 
|---|---|---|
| committer | Sébastien Dailly <sebastien@dailly.me> | 2024-02-24 11:31:28 +0100 | 
| commit | ef9beb0814c36cda979a4ed7e9175e72e69540ac (patch) | |
| tree | c2852b1dd5df53f5d78e9e952f29360d50126e06 /calculette_aoo | |
| parent | 9e7f27c60a425e2baa67cd459d8509a43b1d123d (diff) | |
New application: build upgrade helper for aoo
Diffstat (limited to 'calculette_aoo')
| -rw-r--r-- | calculette_aoo/bin/dune | 3 | ||||
| -rw-r--r-- | calculette_aoo/bin/main.ml | 49 | ||||
| -rw-r--r-- | calculette_aoo/index.html | 180 | ||||
| -rw-r--r-- | calculette_aoo/js/dune | 19 | ||||
| -rw-r--r-- | calculette_aoo/js/main.ml | 97 | ||||
| -rw-r--r-- | calculette_aoo/lib/build.ml | 135 | ||||
| -rw-r--r-- | calculette_aoo/lib/build.mli | 35 | ||||
| -rw-r--r-- | calculette_aoo/lib/carac.ml | 40 | ||||
| -rw-r--r-- | calculette_aoo/lib/carac.mli | 17 | ||||
| -rw-r--r-- | calculette_aoo/lib/dune | 2 | ||||
| -rw-r--r-- | calculette_aoo/test/dune | 2 | ||||
| -rw-r--r-- | calculette_aoo/test/test_optim_aoo.ml | 0 | 
12 files changed, 579 insertions, 0 deletions
| 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 @@ +<!DOCTYPE html> +<html> +<head> +<title>AOO PC Builder</title> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> + +<link rel="stylesheet" type="text/css" href="/theme/font-awesome/css/fontawesome.css"> +<link rel="stylesheet" type="text/css" href="/theme/font-awesome/css/brands.css"> +<link rel="stylesheet" type="text/css" href="/theme/font-awesome/css/solid.css"> + + +<style> +* { +  box-sizing: border-box; +} + +/* Style the body */ +body { +  font-family: Arial; +  margin: 0; +} + +/* Header/logo Title */ +.header { +  padding: 50px; +  text-align: center; +  color: white; +  background-color: #333; +} + +/* Column container */ +.row {   +  display: grid; +  grid-auto-flow: column; +} + +.side { +  flex: 30%; +  padding: 20px; +} + +/* Main column */ +.main { +  flex: 70%; +  background-color: white; +  padding: 20px; +} + +input[type='text'] { font-size: 24px; } + +fieldset { +  display: table; +} +fieldset div { +  display: table-row; +} + +fieldset label { + display: table-cell; + padding-right: 20px; + vertical-align: middle; +} + +textarea { +    width: 100%; +    height: 300px; +} + +table tr td input { width: 2em; text-align: center} +table tr td input.wellplaced { background-color: lightgreen;} +table tr td input.misplaced { background-color: gold;} +tr:first-child { outline: thin solid; } + +/* Responsive layout - when the screen is less than 700px wide, make the two columns stack on top of each other instead of next to each other */ +@media screen and (max-width: 700px) { +  .row, .navbar {    +    flex-direction: column; +  } +} +</style> +</head> +<body> + +<!-- Header --> +<div class="header"> +  <h1>AOO PC Builder</h1> +</div> + +<!-- The flexible grid (content) --> +<div class="main"> +  <div> +    <noscript>Sorry, you need to enable JavaScript to see this page.</noscript> +      <script id="lib" type="text/javascript" defer="defer" src="js/aoo.js"></script> +    <script> +      var script = document.getElementById('lib'); +      script.addEventListener('load', function() { +        lib.run(); +      }); +    </script> +  </div> +  <form id="form" > +  <div class="row"> +    <fieldset> +      <div> +        <label for="a">A</label> +        <input type="number" id="a" name="a" value="2"/> +      </div> +      <div> +        <label for="m">M</label> +        <input type="number" id="m" name="m" value="5"/> +      </div> +      <div> +        <label for="fm">FM</label> +        <input type="number" id="fm" name="fm" value="9"/> +      </div> +      <div> +        <label for="rm">RM</label> +        <input type="number" id="rm" name="rm" value="7"/> +      </div> +      <div> +        <label for="pm">PM</label> +        <input type="number" id="pm" name="pm" value="40"/> +      </div> +    </fieldset>  +    <fieldset> +      <div> +        <label for="a">Upgrade A</label> +        <input type="number" id="a_bonus" name="a" value="0"/> +      </div> +      <div> +        <label for="m">Upgrade M</label> +        <input type="number" id="m_bonus" name="m" value="0"/> +      </div> +      <div> +        <label for="fm">Upgrade FM</label> +        <input type="number" id="fm_bonus" name="fm" value="0"/> +      </div> +      <div> +        <label for="rm">Upgrade RM</label> +        <input type="number" id="rm_bonus" name="rm" value="0"/> +      </div> +      <div> +        <label for="pm">Upgrade PM</label> +        <input type="number" id="pm_bonus" name="pm" value="0"/> +      </div> +    </fieldset>  +  </div> +  <div class="row"> +    <fieldset> +      <div> +        <label for="xp">XP à dépenser</label> +        <input type="number" id="xp" name="xp" value="1000"/> +      </div> +      <div> +        <label for="tours">Tour de combats</label> +        <input type="number" id="tours" name="tours" value="5"/> +      </div> +      <div> +        <label for="cost">Couts du sort</label> +        <input type="number" id="cost" name="cost" value="8"/> +      </div> +      <div> +        <label for="dammage">Dégats du sort</label> +        <input type="number" id="dammage" name="dammage" value="6"/> +      </div> +      <div> +        <label for="fm_oponent">FM de l’adversaire</label> +        <input type="number" id="fm_oponent" name="fm_oponent" value="10"/> +      </div> +    </fieldset>  +  </div> +  <input id="send" type="submit" value="Charger"/> +  </form> +  <h2>Résultats</h2> +  <textarea id="result" > +  </textarea> +</div> +</body> +</html> 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@;\ +     (@[<v 2>%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 --- /dev/null +++ b/calculette_aoo/test/test_optim_aoo.ml | 
