aboutsummaryrefslogtreecommitdiff
path: root/calculette_aoo/lib/build.ml
blob: ddefe67993fb864fc3548c0228cab3642c5cf465 (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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)