aboutsummaryrefslogtreecommitdiff
path: root/calculette_aoo/lib/roll.ml
blob: 14488e617ae5e93ae9b7907b95550b9cb2667de6 (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
open StdLabels

(** Build the frequencies table for the given number of dices to roll. The
    function return a table with the number of occurences for the each index.

    Example : 
        table[2] = 1
        table[3] = 4

    Tell us than there the probabily to get the value 3 is 4× higher to get the
    value 2.
    *)
let build_frequencies n =
  let length = n * 3 in

  let arr_source = Array.init length ~f:(fun i -> if i < 3 then 1 else 0) in

  (* Recursive function, evaluate the odd by adding a new dice to the previous
     distribution.

     The probabily to get the value V with N dice is equal to :
     - The probabilyt to get the value V - 1 with N - 1 dices and having 1 with
       the new dice
     - The probabilyt to get the value V - 2 with N - 1 dices and having 2 with
       the new dice
     - The probabilyt to get the value V - 3 with N - 1 dices and having 3 with
       the new dice

     As the dice is fair, the probability to get the new value is equal in each
     case, and we can just ignore this part.

     This give us this formula :

     P(V)_N = P(V - 1)_(N-1) + P(V - 2)_(N-1) + P(V - 3)_(N-1)
  *)
  let rec apply level arr_source =
    match level with
    | 0 -> arr_source
    | _ ->
        let arr_target = Array.init length ~f:(fun _ -> 0) in
        let depth = n - level + 1 in
        for index = max 1 (depth - 1) to (3 * depth) - 1 do
          for j = max 0 (index - 3) to index - 1 do
            arr_target.(index) <- arr_target.(index) + arr_source.(j)
          done
        done;
        apply (level - 1) arr_target
  in
  apply (n - 1) arr_source

(** 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 frequencies = build_frequencies 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 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 =
    build_frequencies carac1 |> Array.map ~f:(fun v -> Z.(of_int v))
  and frequencies_2 =
    build_frequencies 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