Draco18s

AI Dungeon dice roller

Jan 1st, 2021 (edited)
990
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /***
  2.  *
  3.  *  Author: Draco18s
  4.  *
  5.  *  call this from Input Modifier
  6.  *  var r = MatchDiceAndParse(modifiedText, state);
  7.  *  if(r.stop) return r;
  8.  *  modifiedText = r.text;
  9.  *
  10.  *  /dice or /roll will display the results in a message.
  11.  *  Syntax errors will display in a message and leave the input unaltered.
  12.  *  /dice help will display the help text, reproduced here because it's unreadable in a message
  13.  *  due to AI Dungeon's current CSS.
  14.  *
  15.  *  Wrap dice values in [].
  16.  *  ndm: rolls n m-sided dice. +-/*% supported. < > compares two values as <= and >= respectively and the ternary
  17.  *  operator can evaluate a truthy value to strings, eg [1d6>5?"hit":"miss"].
  18.  *  {} creates a collection (eg {1,2,3}), dice rolled inside a collection are concatenated into the collection
  19.  *  instead of being summed, eg [{3d6}] will create a collection of 3 random values
  20.  *  Wrap in parens to evaluate the sum first, eg [{(3d6),(3d6)}].
  21.  *  p and s will pick (without replacement) or select (with replacement) n values from a collection.
  22.  *  /dice [...] will place the result in the message and not send to the AI.
  23.  *
  24. ***/
  25.  
  26. function MatchDiceAndParse(modifiedText, state) {
  27.   delete state.message;
  28.   const regex = /\[((?:[dhlps \d?:*+,/(){}<>-]|(?:\?"[^"]*":"[^"]*")){3,})\]/i;
  29.   var didMatch = false;
  30.   while(modifiedText.match(regex) && state.message == undefined) {
  31.     didMatch = true;
  32.     modifiedText = modifiedText.replace(regex, function(v) {
  33.       var m = v.match(regex);
  34.       return ParseAndRollDice(m[1], state);
  35.     });
  36.   }
  37.   const command = /\/dice|\/roll/i;
  38.   if(modifiedText.match(command)) {
  39.     if(modifiedText.match("help")) {
  40.       state.message = "Wrap dice values in [].\nndm: rolls n m-sided dice. +-*/% supported. < > compares two values as <= and >= respectively and the ternary operator can evaluate a truthy value to strings, eg [1d6>5?\"hit\":\"miss\"].\n{} creates a collection (eg {1,2,3}), dice rolled inside a collection are concatenated into the collection instead of being summed, eg [{3d6}] will create a collection of 3 random values\nWrap in parens to evaluate the sum first, eg [{(3d6),(3d6)}].\np and s will pick (without replacement) or select (with replacement) n values from a collection.\n/dice [...] will place the result in the message and not send to the AI.";
  41.       return { text: "", stop: true };
  42.     }
  43.     else if(didMatch) {
  44.       if(state.message == undefined)
  45.         state.message = modifiedText.replace(command,"");
  46.       else
  47.         return { text: modifiedText, stop: true };
  48.       return { text: "", stop: true };
  49.     }
  50.   }
  51.   return { text: modifiedText };
  52. }
  53.  
  54. var OpPrecedence = {
  55.     '(': 999,
  56.     'd': 50,
  57.     'p': 40,
  58.     's': 40,
  59.     '*': 20,
  60.     '/': 20,
  61.     '%': 20,
  62.     'h': 15,
  63.     'l': 15,
  64.     '+': 10,
  65.     '-': 10,
  66.     '<': 5,
  67.     '>': 5,
  68.     ',': 5,
  69.     '{': 2,
  70.     '?': 1,
  71.     ':': 1
  72. }
  73.  
  74. function ParseAndRollDice(dice, state) {
  75.   var output = [];
  76.   var trainyard = [];
  77.   var value = -1;
  78.   var stringread = false;
  79.   var tempString = "";
  80.   for(var i=0; i<dice.length;i++) {
  81.     var exp = dice[i];
  82.     if(stringread) {
  83.       if(exp == '"') {
  84.         stringread = false;
  85.         output.push(tempString);
  86.       }
  87.       else {
  88.         tempString += exp;
  89.       }
  90.       continue;
  91.     }
  92.     if(exp == ' ' || exp == ':') continue;
  93.     if(exp>='0' && exp<='9') {
  94.       if(value == -1) value = 0;
  95.       value = value*10+parseInt(exp.toString());
  96.       continue;
  97.     }
  98.     else if(value != -1) {
  99.       output.push(value);
  100.       value = -1;
  101.     }
  102.     switch(exp) {
  103.       case '"':
  104.         tempString = "";
  105.         stringread = true;
  106.         break;
  107.       case '{':
  108.       case '(':
  109.         output.push(exp);
  110.       case '+':
  111.       case '-':
  112.       case '*':
  113.       case '/':
  114.       case '%':
  115.       case 'd':
  116.       case 'p':
  117.       case 's':
  118.       case 'h':
  119.       case 'l':
  120.       case '<':
  121.       case '>':
  122.       case ',':
  123.       case '?':
  124.         while(trainyard.length > 0 && trainyard[trainyard.length-1] != '(' && OpPrecedence[exp] <= OpPrecedence[trainyard[trainyard.length-1]]) {
  125.           var pp = trainyard.pop();
  126.           output.push(pp);
  127.         }
  128.         trainyard.push(exp);
  129.         break;
  130.       case '}':
  131.         while(trainyard.length > 0) {
  132.           var c = trainyard.pop();
  133.           if(c == '{') break;
  134.           if(c == '(') {
  135.             state.message = "Unpaired (!";
  136.             return "["+dice+"]";
  137.           }
  138.           output.push(c);
  139.         }
  140.         output.push(exp);
  141.         break;
  142.       case ')':
  143.         while(trainyard.length > 0) {
  144.           var c = trainyard.pop();
  145.           if(c == '(') break;
  146.           if(c == '{') {
  147.             state.message = "Unpaired {!";
  148.             return "["+dice+"]";
  149.           }
  150.           output.push(c);
  151.         }
  152.         output.push(exp);
  153.         break;
  154.       default:
  155.         if(!(exp>='0' && exp<='9')) {
  156.           state.message = "Unrecognized dice operation: " + exp;
  157.           return "["+dice+"]";
  158.         }
  159.         break;
  160.     }
  161.   }
  162.   if(value > -1) {
  163.     output.push(value);
  164.   }
  165.   while(trainyard.length > 0) {
  166.     output.push(trainyard.pop());
  167.   }
  168.   var ops = [];
  169.   var v1=0;
  170.   var v2=0;
  171.   var collapse = 0;
  172.   var retreat = [];
  173.   while(output.length > 0) {
  174.     exp = output.shift();
  175.     switch(exp) {
  176.       case '+':
  177.         v1 = ParseArray(ops.pop());
  178.         v2 = ParseArray(ops.pop());
  179.         ops.push(v2+v1);
  180.         break;
  181.       case '-':
  182.         v1 = ParseArray(ops.pop());
  183.         v2 = ParseArray(ops.pop());
  184.         ops.push(v2-v1);
  185.         break;
  186.       case '*':
  187.         v1 = ParseArray(ops.pop());
  188.         v2 = ParseArray(ops.pop());
  189.         ops.push(v2*v1);
  190.         break;
  191.       case '/':
  192.         v1 = ParseArray(ops.pop());
  193.         v2 = ParseArray(ops.pop());
  194.         ops.push(Math.floor(v2/v1));
  195.         break;
  196.       case '%':
  197.         v1 = ParseArray(ops.pop());
  198.         v2 = ParseArray(ops.pop());
  199.         ops.push(Math.floor(v2%v1));
  200.         break;
  201.       case 'd':
  202.         v1 = ParseArray(ops.pop());
  203.         v2 = ParseArray(ops.pop());
  204.         var r = [];
  205.         for(var n=0; n < v2; n++) {
  206.           r.push(Math.floor(Math.random()*v1)+1);
  207.         }
  208.         if(collapse == 0) {
  209.           ops.push(ParseArray(r));
  210.         }
  211.         else {
  212.           ops.push(r);
  213.         }
  214.         break;
  215.       case ':':
  216.         break;
  217.       case '?':
  218.         if(ops.length < 3) {
  219.           state.message = "Invalid dice operation: " + exp + "\nInvalid number of operands.";
  220.           return "["+dice+"]";
  221.         }
  222.         v3 = ops.pop();
  223.         v2 = ops.pop();
  224.         var v1 = ops.pop();
  225.         if(v1) {
  226.           ops.push(v2);
  227.         }
  228.         else {
  229.           ops.push(v3);
  230.         }
  231.         break;
  232.       case 'p':
  233.       case 's':
  234.         v1 = ParseArray(ops.pop());
  235.         v2 = ops.pop();
  236.         if(!Array.isArray(v2)) {
  237.           state.message = "Unrecognized dice operation: " + exp + "\nValue " + v2 + " was not a collection.";
  238.           return "["+dice+"]";
  239.         }
  240.         if(v2.length < v1) {
  241.           state.message = "Invalid dice operation: " + exp + "\nValue " + v1 + " was larger than the size of the collection " + v2 + ".";
  242.           return "["+dice+"]";
  243.         }
  244.         var r = [];
  245.         for(var h=0;h<v1;h++) {
  246.           var i = Math.floor(Math.random()*v2.length);
  247.           r.push(v2[i]);
  248.           if(exp == 'p')
  249.             v2.splice(i,1);
  250.         }
  251.         if(collapse == 0) {
  252.           ops.push(ParseArray(r));
  253.         }
  254.         else {
  255.           ops.push(r);
  256.         }
  257.         break;
  258.       case 'h':
  259.         v1 = ParseArray(ops.pop());
  260.         v2 = ops.pop();
  261.         if(!Array.isArray(v2)) {
  262.           state.message = "Unrecognized dice operation: " + exp + "\nValue " + v2 + " was not a collection.";
  263.           return "["+dice+"]";
  264.         }
  265.         v2.sort(function(a, b){return a - b});
  266.         v2.reverse();
  267.         var r = [];
  268.         for(var h=0;h<v1;h++) {
  269.           r.push(v2[h]);
  270.         }
  271.         ops.push(r);
  272.         break;
  273.       case 'l':
  274.         v1 = ParseArray(ops.pop());
  275.         v2 = ops.pop();
  276.         if(!Array.isArray(v2)) {
  277.           state.message = "Unrecognized dice operation: " + exp + "\nValue " + v2 + " was not a collection.";
  278.           return "["+dice+"]";
  279.         }
  280.         v2.sort(function(a, b){return a - b});
  281.         var r = [];
  282.         for(var h=0;h<v1;h++) {
  283.           r.push(v2[h]);
  284.         }
  285.         ops.push(r);
  286.         break;
  287.       case '<':
  288.         v1 = ParseArray(ops.pop());
  289.         v2 = ParseArray(ops.pop());
  290.         ops.push(v2 <= v1);
  291.         break;
  292.         break;
  293.       case '>':
  294.         v1 = ParseArray(ops.pop());
  295.         v2 = ParseArray(ops.pop());
  296.         ops.push(v2 >= v1);
  297.         break;
  298.       case '{':
  299.         collapse++;
  300.         break;
  301.       case '}':
  302.         collapse--;
  303.         break;
  304.       case '(':
  305.         if(collapse > 0) {
  306.           retreat.push(collapse);
  307.           collapse = 0;
  308.         }
  309.         break;
  310.       case ')':
  311.         if(retreat.length > 0) {
  312.           collapse = retreat.pop();
  313.         }
  314.         break;
  315.       case ',':
  316.         v1 = ops.pop();
  317.         v2 = ops.pop();
  318.         var p = [];
  319.         if(collapse > 0 && v1 != null && v2 != null) {
  320.           if(Array.isArray(v2)) {
  321.             p = v2.concat(v1);
  322.           }
  323.           else if(Array.isArray(v1)) {
  324.             var v3 = [];
  325.             v3.push(v2);
  326.             p = v3.concat(v1);
  327.           }
  328.           else {
  329.             p = [v2, v1];
  330.           }
  331.           ops.push(p);
  332.         }
  333.         else if(v1 == null || v2 == null) {
  334.           state.message = "Invalid operation: " + exp + "\nInvalid collection syntax!";
  335.           return "["+dice+"]";
  336.         }
  337.         else {
  338.           state.message = "Invalid operation: " + exp + "\nTo create a collection, use {a,b}.";
  339.           return "["+dice+"]";
  340.         }
  341.         break;
  342.       default:
  343.         if(collapse > 0) {
  344.           ops.push([exp]);
  345.         }
  346.         else {
  347.           ops.push(exp);
  348.         }
  349.         break;
  350.     }
  351.   }
  352.   var ret = ops.pop();
  353.   if(ret === true) {
  354.     return "[Success!]";
  355.   }
  356.   else if(ret === false) {
  357.     return "[Failed!]";
  358.   }
  359.   if(Array.isArray(ret)) {
  360.     return "(" + ret + ")";
  361.   }
  362.   return ParseArray(ret);
  363. }
  364.  
  365. function ParseArray(val) {
  366.   if(Array.isArray(val)) {
  367.     var r = 0;
  368.     for(var i = 0; i < val.length; i++) {
  369.       r+=val[i];
  370.     }
  371.     return r;
  372.   }
  373.   else
  374.     return val;
  375. }
Add Comment
Please, Sign In to add comment