aboutsummaryrefslogtreecommitdiff
path: root/calculette_aoo/lib/build.ml
blob: 5c2ac0bcc0c32b6b397cbb46d12316d89926dbf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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 : int
  ; fm_oponent : int
  ; cost_max : int
  ; frequencies : (int * float) list
}

let get_chance : env -> int -> float =
 fun env delta ->
  match List.assoc_opt delta env.frequencies with
  | None -> 1.
  | Some v -> v

(** Build a list with the differents percentages to hit *)
let buil_freq_table from thresold =
  List.init 10 (fun i ->
      (from + i, Roll.compare (from + i) thresold |> Q.to_float))

let eval : build -> env -> float * int =
 fun { a; m; rm; pm; fm } env ->
  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 (nb_attacks * (env.degat_sort + Carac.value m - 5))
    *. get_chance env (Carac.value fm)
  in
  (degats, nb_attacks)

let repr_degats : env -> Format.formatter -> build -> unit =
 fun env out build ->
  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
  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_attacks = 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 attaque pour un total de %.2f (via %d \
     attaques)@;\
     (@[<v 2>%a@])@;"
    (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

(* 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 -> 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)