Roll dice (eg Asphodice) and show outcomes https://rpg.bertieb.org/dice-roller/
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

186 lignes
5.5 KiB

  1. const util = require('util');
  2. function countOccurrencesOfNumber(inputArray: Array<number>, checkNumber: number): number {
  3. /*
  4. return inputArray.reduce(function(n: number, val: number) {
  5. return n + (val === checkNumber);
  6. }, 0);
  7. */
  8. return inputArray.filter(c => c === checkNumber).length;
  9. }
  10. function randIntMinOne(max: number): number {
  11. return (1 + Math.floor(Math.random() * Math.floor(max)));
  12. }
  13. interface Dice {
  14. readonly sides: number;
  15. readonly type: string;
  16. }
  17. enum Outcomes {
  18. Success = "Success",
  19. Fail = "Failure",
  20. CritSuccess = "Critical Success",
  21. CritFail = "Critical Failure",
  22. Other = "Something Else"
  23. }
  24. interface DiceResult {
  25. total: number;
  26. dice: Array<number>;
  27. olddice?: Array<number>;
  28. reroll?: boolean;
  29. outcome?: Outcomes;
  30. }
  31. class D10 implements Dice {
  32. sides: number = 10;
  33. type: string = "d10"
  34. constructor() {
  35. // this.sides = 10;
  36. }
  37. [util.inspect.custom](): string {
  38. return this.type;
  39. }
  40. roll(numberToRoll: number): DiceResult {
  41. let results: DiceResult = { total: 0, dice: [] };
  42. for (let i = 0; i < numberToRoll; i++) {
  43. results.dice.push(randIntMinOne(this.sides));
  44. }
  45. results.total = results.dice.reduce((acc: number, curr: number) => acc + curr);
  46. return results;
  47. }
  48. }
  49. class Asphodice extends D10 {
  50. readonly type: string = "asphodice";
  51. readonly passCrit: number = 10;
  52. readonly failCrit: number = 1;
  53. readonly successCutOff: number = 6; // this or larger
  54. rerollHighDice (rerollDice: Array<number>): Array<number> {
  55. // Re-roll the high dice (ie >= 6) for a chance of failure
  56. // happens on eg 1
  57. //
  58. // Basically we want to remove a die from the original result,
  59. // as long as that die isn't a 10, and is above the
  60. // cutoff. So we filter to get rerollCandidates, find the
  61. // highest value and get the index of that value in the
  62. // original results. We use that to splice() out (remove) that
  63. // value and push the rerolled dice onto the modified array
  64. //
  65. // Example:
  66. // rerollDice: [ 8, 1, 1, 4, 7, 10] --> filter (not 10, >= 6)
  67. // rerollCandidates : [8, 7] --> maxValue: 8
  68. // maxIndex: 0 (in original array)
  69. // rerollResult: 6
  70. // rerollOutcome: [ 1, 1, 4, 7, 10, 6] --> return
  71. let rerollCandidates: Array<number> = rerollDice.filter(die => (die < 10 && die >= this.successCutOff));
  72. let maxValue: number = Math.max(...rerollCandidates);
  73. let maxIndex = rerollDice.indexOf(maxValue);
  74. let rerollResult: number = randIntMinOne(this.sides);
  75. rerollDice.splice(maxIndex, 1);
  76. rerollDice.push(rerollResult);
  77. return rerollDice
  78. }
  79. rerollLowDice (rerollDice: Array<number>): Array<number> {
  80. // Re-roll the low dice (ie < 6) for a chance of success
  81. // happens on eg 10
  82. //
  83. // see this.rerollHighDice() for a full explanation
  84. let rerollCandidates: Array<number> = rerollDice.filter(die => (die > 1 && die < this.successCutOff));
  85. let minValue: number = Math.min(...rerollCandidates);
  86. let minIndex = rerollDice.indexOf(minValue);
  87. let rerollResult: number = randIntMinOne(this.sides);
  88. rerollDice.splice(minIndex, 1);
  89. rerollDice.push(rerollResult);
  90. return rerollDice
  91. }
  92. allAboveCutOff (checkDice: Array<number>): boolean {
  93. // if filtering those *below* cutoff results in empty set
  94. // then all must be above cutoff
  95. return ((checkDice.filter(die => die < this.successCutOff).length == 0));
  96. }
  97. allBelowCutOff (checkDice: Array<number>): boolean {
  98. // if filtering those *above or equal to* cutoff results in empty set
  99. // then all must be below cutoff
  100. return ((checkDice.filter(die => die >= this.successCutOff).length == 0));
  101. }
  102. roll (numToRoll: number): DiceResult {
  103. let results: DiceResult = { total: 0, dice: [] };
  104. // Initial roll
  105. for (let i = 0; i < numToRoll; i++) {
  106. results.dice.push(randIntMinOne(this.sides));
  107. }
  108. // Check for re-rolls
  109. // 1. if no re-rolls we can finish here
  110. if (!(results.dice.includes(this.passCrit) || results.dice.includes(this.failCrit))) {
  111. // results.total = results.dice.reduce((acc: number, curr: number) => acc + curr);
  112. results.reroll = false;
  113. } else {
  114. // count successes and fails
  115. let rerollGood = countOccurrencesOfNumber(results.dice, this.passCrit);
  116. let rerollBad = countOccurrencesOfNumber(results.dice, this.failCrit);
  117. // 2. only reroll if they don't cancel each other out
  118. if (rerollGood == rerollBad) {
  119. // console.log("Good = Bad, no need to reroll");
  120. results.reroll = false;
  121. // results.total = results.dice.reduce((acc: number, curr: number) => acc + curr);
  122. }
  123. // If all dice are above/below cutoff we don't need to reroll
  124. // 3a. Above
  125. else if (this.allAboveCutOff(results.dice)) {
  126. console.log("All above cutoff, auto-success", results.dice);
  127. results.reroll = false;
  128. }
  129. // 3b. Below
  130. else if (this.allBelowCutOff(results.dice)) {
  131. console.log("All below cutoff, auto-fail", results.dice);
  132. results.reroll = false;
  133. }
  134. // Reroll
  135. else {
  136. // Reminder: arr1 = arr2 is a copy by reference!
  137. let olddice = results.dice.slice();
  138. if (rerollGood > rerollBad) {
  139. // Re-roll low (<6) dice for chance of success
  140. results.dice = this.rerollLowDice(results.dice);
  141. } else {
  142. // Re-roll high (>=6) dice for chance of failure
  143. results.dice = this.rerollHighDice(results.dice);
  144. }
  145. results.olddice = olddice;
  146. results.reroll = true;
  147. }
  148. }
  149. results.total = results.dice.reduce((acc: number, curr: number) => acc + curr);
  150. return results;
  151. }
  152. }
  153. let asphodice: Asphodice = new Asphodice();
  154. let number: number = 4;
  155. for (let i = 0; i < 10; i++) {
  156. console.log("Rolling", number, asphodice);
  157. console.log(asphodice.roll(4));
  158. }