From fe13ea5ce39b24b2d49b43c384ed01b013d683db Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Mon, 4 Mar 2024 21:07:51 +0100 Subject: calculette_aoo: now evaluate the exact frequency, some corrections --- calculette_aoo/lib/build.ml | 58 ++++++++++++------------------- calculette_aoo/lib/build.mli | 2 +- calculette_aoo/lib/dune | 4 ++- calculette_aoo/lib/roll.ml | 81 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 39 deletions(-) create mode 100644 calculette_aoo/lib/roll.ml (limited to 'calculette_aoo/lib') diff --git a/calculette_aoo/lib/build.ml b/calculette_aoo/lib/build.ml index ddefe67..5c2ac0b 100644 --- a/calculette_aoo/lib/build.ml +++ b/calculette_aoo/lib/build.ml @@ -9,54 +9,41 @@ type build = { type env = { cout_sort : int ; degat_sort : int - ; max_tours : float + ; max_tours : int ; 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 + | None -> 1. | 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.)) + List.init 10 (fun i -> + (from + i, Roll.compare (from + i) thresold |> Q.to_float)) -let eval : build -> env -> float * float = +let eval : build -> env -> float * int = 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 + let mp_available = Carac.value pm + ((env.max_tours - 1) * Carac.value rm) in + + (* How many attacks can be done using this cost ? (rounded down) *) + let nb_attacks = + min (env.max_tours * Carac.value a) (mp_available / env.cout_sort) in let degats = - float (Carac.value a * (env.degat_sort + Carac.value m - 5)) - *. get_chance env (Carac.value fm - env.fm_oponent) + float (nb_attacks * (env.degat_sort + Carac.value m - 5)) + *. get_chance env (Carac.value fm) in - (degats, nb_tour) + (degats, nb_attacks) 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 + let delta = get_chance env (Carac.value build.fm) 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 @@ -71,7 +58,7 @@ let cost : build -> int = let repr : env -> Format.formatter -> build -> unit = fun env formatter build -> - let degats, nb_tour = eval build env in + let degats, nb_attacks = eval build env in Format.fprintf formatter {|Caractéristiques retenues : @@ -84,18 +71,17 @@ let repr : env -> Format.formatter -> build -> unit = 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@;\ + "@[Le magicien fera %.2f degats par attaque pour un total de %.2f (via %d \ + attaques)@;\ (@[%a@])@;" - degats - (int_of_float (nb_tour *. degats)) - nb_tour (repr_degats env) build; + (degats /. float_of_int nb_attacks) + degats nb_attacks (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 + d (* Upgrade each caracteristic and keep only the values in range *) let upgrade : env -> build -> build list = @@ -125,9 +111,7 @@ let rec traverse env (last_cost, last_score) = function upgrade env hd |> List.filter (fun build -> List.for_all - (fun element -> - cost build > cost element - || score env build > score env element) + (fun element -> score env build > score env element) tl) in diff --git a/calculette_aoo/lib/build.mli b/calculette_aoo/lib/build.mli index 3e2e690..7e39b71 100644 --- a/calculette_aoo/lib/build.mli +++ b/calculette_aoo/lib/build.mli @@ -9,7 +9,7 @@ type build = { type env = { cout_sort : int ; degat_sort : int - ; max_tours : float + ; max_tours : int ; fm_oponent : int ; cost_max : int ; frequencies : (int * float) list diff --git a/calculette_aoo/lib/dune b/calculette_aoo/lib/dune index 54b5a85..176147c 100644 --- a/calculette_aoo/lib/dune +++ b/calculette_aoo/lib/dune @@ -1,2 +1,4 @@ (library - (name aoo)) + (name aoo) + (libraries zarith) + ) diff --git a/calculette_aoo/lib/roll.ml b/calculette_aoo/lib/roll.ml new file mode 100644 index 0000000..00d8465 --- /dev/null +++ b/calculette_aoo/lib/roll.ml @@ -0,0 +1,81 @@ +open StdLabels + +(** Evaluate the coefficient for the product of two polynamials *) +let mul p1 p2 = + let n1 = Array.length p1 and n2 = Array.length p2 in + let n = n1 + n2 in + let res = Array.make n 0 in + for i = 0 to n1 - 1 do + for j = 0 to n2 - 1 do + res.(i + j + 1) <- res.(i + j + 1) + (p1.(i) * p2.(j)) + done + done; + res + +(** Apply a power function over a polynomial *) +let pow p1 n = + let result = ref p1 in + for _ = 1 to n - 1 do + result := mul !result p1 + done; + !result + +(** Evaluate the odd to win agains a give difficulty *) +let against : int -> int array -> Q.t array = + fun n difficulties -> + match n with + | 0 -> Array.map ~f:(fun _ -> Q.zero) difficulties + | _ -> + (* Create the polynomial with the odd ratio for the given caracteristic *) + let arr = Array.make 3 1 in + let frequencies = pow arr n in + let ratio = Z.(of_int 3 ** n) in + + let get_chances difficulty = + (* Evaluate the ratio to win the roll *) + let chances = ref Z.zero in + for i = difficulty - 1 to Array.length frequencies - 1 do + (* The index in the table is the odd to get exactly this value in the roll. + Here, we just add every value in the array from the index strarting from + the given difficulty. *) + chances := Z.(!chances + of_int frequencies.(i)) + done; + Q.make !chances ratio + in + Array.map ~f:get_chances difficulties + +(** Compare two caracteristics. + [compare v1 v2] will evaluate the odds to win if the player has the + caracteristics [v1] against the openent [v2]. + + In case of equality, the win is given to [v1] *) +let compare : int -> int -> Q.t = + fun carac1 carac2 -> + let arr = Array.make 3 1 in + let z3 = Z.of_int 3 in + + let ordinal1 = Z.(z3 ** carac1) + and ordinal2 = Z.(z3 ** carac2) + (*Number of possibles values for each caracteristc *) + and elements1 = 3 * carac1 + and elements2 = 3 * carac2 in + + (* The number of iterations to do is elements1 × elements2. For every iteration of elements2, we have ordinal1 points to get. *) + let cases = Z.(ordinal1 * ordinal2) in + + let frequencies_1 = pow arr carac1 |> Array.map ~f:(fun v -> Z.(of_int v)) + and frequencies_2 = pow arr carac2 |> Array.map ~f:(fun v -> Z.(of_int v)) in + + (* Now, compare for each values the probabily to win *) + let res = ref Z.zero in + let () = + for i = 0 to elements1 - 1 do + for j = 0 to elements2 - 1 do + if i >= j then + (* Number of times this combinaison is occuring *) + let freq = Z.(frequencies_1.(i) * frequencies_2.(j)) in + Z.(res := !res + freq) + done + done + in + Q.make !res cases -- cgit v1.2.3