Guest User

Untitled

a guest
Dec 16th, 2017
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.88 KB | None | 0 0
  1. module type POSSIBLES = sig
  2. (* A monad to simulate all possible outcomes of a non-deterministic
  3. * operation with discrete outcomes such as dice rolling. *)
  4.  
  5. type 'a t
  6.  
  7. (** [return x] is the possible outcomes for an operation where [x] is the
  8. * only possible outcome. *)
  9. val return: 'a -> 'a t
  10.  
  11. (** [bind os f] is the combined outcomes for all of the operations [f o]
  12. * where [o] spans over the outcomes [os]. *)
  13. val bind: 'a t -> ('a -> 'b t) -> 'b t
  14. val ( >>= ): 'a t -> ('a -> 'b t) -> 'b t
  15.  
  16. (** [join os1 os2] is the combined outcomes from [os1] and [os2]. *)
  17. val join: 'a t -> 'a t -> 'a t
  18.  
  19. val map: 'a t -> ('a -> 'b) -> 'b t
  20. val ( >|= ): 'a t -> ('a -> 'b) -> 'b t
  21. val lift: ('a -> 'b) -> 'a t -> 'b t
  22. val lift2: ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
  23.  
  24. (* The possibilities for a die of given sidedness*)
  25. val die: int -> int t
  26.  
  27. (* interaction with the rest of the world *)
  28. val to_list: 'a t -> 'a list
  29. val of_list: 'a list -> 'a t
  30. val fold: ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b
  31.  
  32. end
  33.  
  34. module Possibles : POSSIBLES = struct
  35. type 'a t = 'a list
  36. let return x = [ x ]
  37. let bind x f = List.flatten (List.map f x)
  38. let ( >>= ) = bind
  39. let join = ( @ )
  40. let map x f = List.map f x
  41. let ( >|= ) = map
  42. let lift f x =
  43. x >>= fun xo ->
  44. return (f xo)
  45. let lift2 f x y =
  46. x >>= fun xo ->
  47. y >>= fun yo ->
  48. return (f xo yo)
  49.  
  50. let die sidedness =
  51. let rec loop res i =
  52. if i <= 0 then
  53. res
  54. else
  55. loop (i :: res) (pred i)
  56. in
  57. loop [] sidedness
  58.  
  59. let to_list t = t
  60. let of_list t = t
  61. let fold = List.fold_left
  62. end
  63.  
  64. (*Managing advantage *)
  65. type advantage =
  66. | Advantage
  67. | Disadvantage
  68. let d ?advantage sidedness =
  69. let open Possibles in
  70. match advantage with
  71. | None -> die sidedness
  72. | Some Advantage -> lift2 max (die sidedness) (die sidedness)
  73. | Some Disadvantage -> lift2 min (die sidedness) (die sidedness)
  74.  
  75. (* Specialised 20-sided die *)
  76. let d20 = d 20
  77. let d20_advantage = d ~advantage:Advantage 20
  78. let d20_disadvantage = d ~advantage:Disadvantage 20
  79.  
  80. (* Possible outcomes of an attack roll *)
  81. type hit_result =
  82. | Crit_fail
  83. | Fail
  84. | Success
  85. | Crit_success
  86. (* Determining the outcome of an attack roll *)
  87. let hit_result ac roll bonus =
  88. if roll = 1 then
  89. Crit_fail
  90. else if roll = 20 then
  91. Crit_success
  92. else if roll + bonus < ac then
  93. Fail
  94. else if roll + bonus >= ac then
  95. Success
  96. else
  97. assert false
  98.  
  99. (* All possible outcomes of an attack roll *)
  100. let hit ?advantage ~bonus ~ac () =
  101. Possibles.map
  102. (d ?advantage 20)
  103. (fun roll -> hit_result ac roll bonus)
  104.  
  105. (* Analysing the possible outcomes of an attack roll *)
  106. let rates results =
  107. let total = float_of_int (List.length results) in
  108. let rate results kind =
  109. let kl = List.length (List.filter ((=) kind) results) in
  110. (float_of_int kl) /. total
  111. in
  112. (rate results Crit_fail,
  113. rate results Fail,
  114. rate results Success,
  115. rate results Crit_success
  116. )
  117.  
  118. (* Rolling for damage *)
  119. let damage ddice bonus =
  120. List.fold_left
  121. (* For each die, add all the possible outcomes *)
  122. (fun res ddie -> (Possibles.lift2 (+)) res (d ddie))
  123. (* Start with the bonus *)
  124. (Possibles.return bonus)
  125. (* Iterate over all dice *)
  126. ddice
  127.  
  128. (* roll for attack and determine damage based on outcome *)
  129. let attack ?advantage ~ac ~hit_bonus ~damage_dice ~damage_bonus () =
  130. let open Possibles in
  131. hit ?advantage ~ac ~bonus:hit_bonus () >>= function
  132. | Crit_fail | Fail -> return 0
  133. | Success -> damage damage_dice damage_bonus
  134. | Crit_success -> damage (damage_dice @ damage_dice) damage_bonus
  135.  
  136. (* Compact representation *)
  137. let length_encode l =
  138. match l with
  139. | [] -> []
  140. | h::t ->
  141. let (v, c, l) =
  142. List.fold_left
  143. (fun (value, count, l) new_value ->
  144. if value = new_value then
  145. (value, count + 1, l)
  146. else
  147. (new_value, 1, (value, count) :: l)
  148. )
  149. (h, 1, [])
  150. t
  151. in
  152. (v, c) :: l
  153.  
  154. let rates_of_len_encode l =
  155. let total = float_of_int (List.fold_left (+) 0 (List.map snd l)) in
  156. List.map (fun (v,c) -> (v, (float_of_int c) /. total)) l
  157. let average l =
  158. let q = List.length l in
  159. let t = List.fold_left (+) 0 l in
  160. (float_of_int t) /. (float_of_int q)
  161. let median_sorted l =
  162. let ll = List.rev l in
  163. let d = List.map2 (fun x y -> (x,y)) l ll in
  164. let rec loop = function
  165. | (x, y) :: t when x < y -> loop t
  166. | (x, y) :: _ when x = y -> x
  167. | (x, y) :: _ when x > y -> (x+y) / 2
  168. | _ -> assert false
  169. in
  170. loop d
  171.  
  172. let process_attack_result res =
  173. let res = Possibles.to_list res in
  174. let sorted_res = List.sort compare res in
  175. let len_encoded_res = length_encode sorted_res in
  176. let rates = rates_of_len_encode len_encoded_res in
  177. (average res, median_sorted sorted_res, rates)
Add Comment
Please, Sign In to add comment