Advertisement
Guest User

Untitled

a guest
Mar 10th, 2014
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.40 KB | None | 0 0
  1. #include <iostream>
  2. #include <random>
  3. #include <string>
  4. #include <vector>
  5. #include <sstream>
  6. #include <algorithm>
  7. #include <stdexcept>
  8. #include <functional>
  9. #include <cctype>
  10. #include <numeric>
  11. #include <ctime>
  12.  
  13. using namespace std;
  14.  
  15. #ifndef NDEBUG
  16. bool debugOutput = true;
  17. #else
  18. bool debugOutput = false;
  19. #endif
  20.  
  21. default_random_engine generator;
  22.  
  23. void debugPrint( string in ) {
  24.     if( debugOutput )
  25.         cout << in << endl;
  26. }
  27.  
  28. typedef vector<string> StringList;
  29.  
  30. StringList mergeArguments( int argc , char* argv[] )
  31. {
  32.     stringstream merged;
  33.     for( int i=1 ; i<argc ; ++i ) // argv[0] is the program path.
  34.     {
  35.         merged << argv[i];
  36.     }
  37.  
  38.     // Reset the stream.
  39.     merged = stringstream(merged.str());
  40.     StringList rv;
  41.     while( merged.good() )
  42.     {
  43.         string temp;
  44.         getline( merged , temp , ';' );
  45.         rv.push_back( temp );
  46.     }
  47.     return rv;
  48. }
  49.  
  50. // Parse the dice roll.
  51. int parseArg( const string& in );
  52.  
  53. int main( int argc , char* argv[] )
  54. {
  55.     generator.seed(time(nullptr));
  56.     // Check that the argument exists.
  57.     if( argc < 2 ) {
  58.         debugPrint( "Nothing supplied." );
  59.         return -1;
  60.     }
  61.  
  62.     // Stuff all the arguments together, for processing. The argv is split by spaces, but I want to ignore spaces and split by semi-colons.
  63.     const auto argList = mergeArguments( argc , argv );
  64.  
  65.     // Handle top level arguments: -help and -debug
  66.     if( end(argList) != find( begin(argList),end(argList) , "-debug" ) )
  67.     {
  68.         debugOutput = true;
  69.         debugPrint( "Turning on debug output." );
  70.  
  71.     }
  72.     if( end(argList) != find( begin(argList),end(argList) , "-help" ) )
  73.     {
  74.         debugPrint( "Argument found: '-help'" );
  75.         cout << "Dice Roller, by /u/arkadye\n"
  76.             "Usage dice_roller [args]\n"
  77.             "Valid args:\n"
  78.             "\t-debug: turns on debug messages.\n"
  79.             "\t-help: displays this message.\n"
  80.             "\tA dice roll. Supported syntax includes:\n"
  81.             "\t\tNdX: rolls N X-sided dice\n"
  82.             "\t\tNdXkY: roll N X-sided dice and keep Y results\n"
  83.             "\t\tNdXsY: roll N X-sided dice, and count the results that are Y or higher.\n"
  84.             "\t\tNdXsYrZ: as above, but dice scoring Z or higher are counted and then rerolled.\n"
  85.             "\t\tConstant values: put '3' into an expression to get '3' out. Integers only, commas and dots are ignored.\n"
  86.             "\t\tMultiplication: use 'x' or '*' to multiply two dice rolls, or by a constant.\n"
  87.             "\t\tAddition / subtraction: '+' and '-' are both supported.\n"
  88.             "\t\tBrackets work as expected.\n"
  89.             "\tIf numbers are omitted, they are assumed to be 1, so d6 = 1d6, 2d6- = 2d6 - 1\n";
  90.         return 0;
  91.     }
  92.  
  93.     for( const auto& arg : argList )
  94.     {
  95.         if( arg != "-debug" )
  96.         {
  97.             try
  98.             {
  99.                 const auto val = parseArg(arg);
  100.                 cout << "Roll: " << arg << " - Result: " << val << endl;
  101.             } catch( exception ex )
  102.             {
  103.                 cout << "Failed roll: " << arg << "Reason: " << ex.what() << endl;
  104.             }
  105.         }
  106.     }
  107.     cin.get();
  108.     return 0;
  109.  
  110. }
  111.  
  112. vector<int> rollDice( int no_dice , int no_sides )
  113. {
  114.     if( no_dice <= 0 )
  115.     {
  116.         stringstream message;
  117.         message << no_dice << " requested. Returning no values." ;
  118.         debugPrint( message.str() );
  119.     }
  120.     vector<int> result;
  121.     result.reserve( no_dice );
  122.     uniform_int_distribution<> die(1 , no_sides );
  123.     stringstream message;
  124.     message << "Rolling " << no_dice << " " << no_sides << "-sided dice. Results:";
  125.     for( int i(0) ; i<no_dice ; ++i )
  126.     {
  127.         const int value = die(generator);
  128.         message << " " << value;
  129.         result.push_back( value );
  130.     }
  131.     debugPrint( message.str() );
  132.     return result;
  133. }
  134.  
  135. int NdXkY( int no_dice , int no_sides , int keep )
  136. {
  137.     auto rolls = rollDice( no_dice , no_sides );
  138.     if( keep >= no_dice )
  139.     {
  140.         const int result = accumulate( begin(rolls) , end(rolls) , 0 );
  141.         debugPrint( "Total: " + to_string(result) );
  142.         return result;
  143.     }
  144.     nth_element( begin(rolls) , begin(rolls)+keep , end(rolls) , [](int l , int r) { return l>r; } );
  145.     const int result = accumulate( begin(rolls) , begin(rolls)+keep , 0 );
  146.     stringstream message;
  147.     message << "Keep " << keep << ". Total: " << result;
  148.     debugPrint( message.str() );
  149.     return result;
  150. }
  151.  
  152. int NdX( int no_dice , int no_sides )
  153. {
  154.     return NdXkY(no_dice,no_sides,no_dice);
  155. }
  156.  
  157. int NdXsYrZ( int no_dice , int no_sides , int success_val , int reroll_val )
  158. {
  159.     const auto rolls = rollDice( no_dice , no_sides );
  160.     int successes = count_if( begin(rolls) , end(rolls) , [&](int in) { return in > success_val; } );
  161.     stringstream message;
  162.     message << "Success threshold: " << success_val << ". " << successes << " successes";
  163.     if( reroll_val <= no_sides )
  164.     {
  165.         int rerolls = count_if( begin(rolls) , end(rolls) , [&](int in) { return in > reroll_val; } );
  166.         message << " and " << rerolls << " rerolls. (" << reroll_val << "+)";
  167.         debugPrint( message.str() );
  168.         successes += rerolls > 0 ? NdXsYrZ( rerolls , no_sides , success_val , reroll_val ) : 0;
  169.     }
  170.     debugPrint( "Total successes: " + to_string( successes ) );
  171.     return successes;
  172. }
  173.  
  174. int NdXsY( int no_dice , int no_sides , int success_val )
  175. {
  176.     return NdXsYrZ(no_dice,no_sides,success_val,no_sides+1);
  177. }
  178.  
  179. // Returns position of the operator, or -1 if not found in reverse, or in.size() if not found forward.
  180. int findIgnoringBrackets( const string& in , function<bool(char)> criteria , bool reverseDirection = false )
  181. {
  182.     int pos = reverseDirection ? in.size() - 1 : 0;
  183.     int openBrackets = 0;
  184.     auto openBracket = [&]() { ++openBrackets; };
  185.     auto closeBracket = [&]() {
  186.         if( --openBrackets < 0 )
  187.             {
  188.                 stringstream message;
  189.                 message << "Error parsing expression \"" << in << "\". " <<
  190.                     (reverseDirection ? "Unopened" : "Unclosed") << "bracket " <<
  191.                     (reverseDirection ? "closed" : "opened") << " at position " << pos << ".";
  192.                 debugPrint( message.str() );
  193.                 throw runtime_error( message.str() );
  194.             }
  195.     };
  196.     // Get the position of the operator
  197.     do
  198.     {
  199.         // Handle brackets.
  200.         switch( in[pos] )
  201.         {
  202.         case '(':
  203.             reverseDirection ? closeBracket() : openBracket();
  204.             break;
  205.         case ')':
  206.             reverseDirection ? openBracket() : closeBracket();
  207.             break;
  208.         default:
  209.             break;
  210.         }
  211.         if( openBrackets == 0 && criteria( in[pos] ) )
  212.             break;
  213.         reverseDirection ? --pos : ++pos;
  214.     } while( pos < int(in.size()) && pos >= 0 );
  215.     if( openBrackets > 0 )
  216.     {
  217.         throw runtime_error( (reverseDirection ? "Unclosed" : "Unopened") + string(" brackets in expression \"") + in + "\"." );
  218.     }
  219.     return pos;
  220. }
  221.  
  222. pair<string,string> splitString( const string& in , int pos )
  223. {
  224.     pair<string,string> rv;
  225.     rv.first = in.substr(0,pos);
  226.     rv.second = in.substr(pos+1,in.size());
  227.     return rv;
  228. }
  229.  
  230. int parseArg( const string& in )
  231. {
  232.     debugPrint( "Parsing argument: \"" + in + '"' );
  233.     try
  234.     {
  235.         // Step 0: Check for the empty string:
  236.         if( in.empty() ) {
  237.             debugPrint( "Empty expression. Interpreting value as 1." );
  238.             return 1;
  239.         }
  240.  
  241.         // Step 1: Process brackets
  242.         {
  243.             if( *in.begin() == '(' && *in.rbegin() == ')' )
  244.             {
  245.                 bool breakFound = false;
  246.                 int bracketsOpen = 0;
  247.                 for( int i(0) ; int(i<in.size()-1) ; ++i )
  248.                 {
  249.                     switch( in[i] )
  250.                     {
  251.                     case '(':
  252.                         ++bracketsOpen;
  253.                         break;
  254.                     case ')':
  255.                         --bracketsOpen;
  256.                         break;
  257.                     default:
  258.                         break;
  259.                     }
  260.                     if( bracketsOpen == 0 ) {
  261.                         breakFound = true;
  262.                         break;
  263.                     }
  264.                 }
  265.                 if( !breakFound ) {
  266.                     debugPrint( "Stripping brackets from front and back." );
  267.                     return parseArg( in.substr(1,in.size()-2) );
  268.                 }
  269.             }
  270.         }
  271.  
  272.         // Step 2: Handle +/- arguments.
  273.         {
  274.             const int pos = findIgnoringBrackets( in , [](char in) { return (in=='+' || in=='-'); } , true );
  275.             if( pos > -1 )
  276.             {
  277.                 const auto args = splitString( in , pos );
  278.                 stringstream message;
  279.                 message << "Found operator " << in[pos] << " at position " << pos<<  ".\n"
  280.                     "Left arg: " << args.first << "\nRight arg: " << args.second ;
  281.                 debugPrint( message.str() );
  282.                 if( in[pos] == '+' ) {
  283.                     return parseArg(args.first) + parseArg(args.second);
  284.                 } else
  285.                 {
  286.                     return parseArg(args.first) - parseArg(args.second);
  287.                 }
  288.             }
  289.         }
  290.  
  291.         // Step 3: Process multiplication.
  292.         {
  293.             const int pos = findIgnoringBrackets( in , [](char in) { return (in=='*' || in=='x'); } );
  294.             // Check we found the operator, and parse each side.
  295.             if( pos < int(in.size()) )
  296.             {
  297.                 const auto args = splitString( in , pos );
  298.                 stringstream message;
  299.                 message << "Found operator " << in[pos] << " at position " << pos<<  ".\n"
  300.                     "Left arg: " << args.first << "\nRight arg: " << args.second ;
  301.                 return parseArg(args.first) * parseArg(args.second);
  302.             }
  303.         }
  304.  
  305.         // Step 4: Process the 'd' operator. Process these backwards so 2d3d4 would roll 2 d3s, and then roll that many d4s.
  306.         {
  307.             const int pos = findIgnoringBrackets( in , [](char in){ return in=='d'; } , true );
  308.             if( pos > -1 )
  309.             {
  310.                 const auto d_args = splitString( in , pos );
  311.                 stringstream d_message;
  312.                 d_message << "Found operator 'd'. Left arg: " << d_args.first << " Right arg: " << d_args.second;
  313.                 debugPrint( d_message.str() );
  314.                 // Look for more args on the right.
  315.                 const int secondArgPos = findIgnoringBrackets( d_args.second , [](char in){ return (in=='k'||in=='s'); } );
  316.                 if( secondArgPos < int(d_args.second.size()) )
  317.                 {
  318.                     // Process the 'k' first:
  319.                     if( d_args.second[secondArgPos] == 'k' )
  320.                     {
  321.                         const auto k_args = splitString( d_args.second , secondArgPos );
  322.                         stringstream k_message;
  323.                         k_message << "Found operator 'k'. Left arg: " << k_args.first << " Right arg: " << k_args.second;
  324.                         debugPrint( k_message.str() );
  325.                         return NdXkY( parseArg(d_args.first) , parseArg(k_args.first) , parseArg(k_args.second) );
  326.                     } else // It's 's'
  327.                     {
  328.                         const auto s_args = splitString( d_args.second , secondArgPos );
  329.                         stringstream s_message;
  330.                         s_message << "Found operator 's'. Left arg: " << s_args.first << " Right arg: " << s_args.second;
  331.                         debugPrint( s_message.str() );
  332.                         // Now check for the 'r' args:
  333.                         const int r_argPos = findIgnoringBrackets( s_args.second , [](char in){ return in=='r'; } );
  334.                         if( r_argPos < int(s_args.second.size()) ) // Found an r
  335.                         {
  336.                             const auto r_args = splitString( s_args.second , r_argPos );
  337.                             stringstream r_message;
  338.                             r_message << "Found operator 'r'. Left arg: " << r_args.first << " Right arg: " << r_args.second;
  339.                             debugPrint( r_message.str() );
  340.                             return NdXsYrZ( parseArg(d_args.first) , parseArg(s_args.first) , parseArg(r_args.first) , parseArg(r_args.second) );
  341.                         } else // No 'r' arg found
  342.                         {
  343.                             return NdXsY( parseArg(d_args.first) , parseArg(s_args.first) , parseArg(s_args.second) );
  344.                         }
  345.                     }
  346.                 } else // No extra args found.
  347.                 {
  348.                     return NdX( parseArg(d_args.first) , parseArg(d_args.second) );
  349.                 }
  350.             }
  351.         }
  352.  
  353.         // Step 5: Process constants
  354.         {
  355.             int val(0);
  356.             for( auto ch : in )
  357.             {
  358.                 if( isdigit(ch) )
  359.                 {
  360.                     val *= 10;
  361.                     val += ch - '0';
  362.                 } else
  363.                 {
  364.                     switch( ch )
  365.                     {
  366.                     case '.':
  367.                     case ',':
  368.                         // Ignore.
  369.                         break;
  370.                     default:
  371.                         // Error
  372.                         {
  373.                             stringstream message;
  374.                             message << "Unidentified value: '" << ch << "' found in expression \"" << in << "\".";
  375.                             throw runtime_error( message.str() );
  376.                         }
  377.                         break;
  378.                     }
  379.                 }
  380.  
  381.             }
  382.             stringstream message;
  383.             message << "Found constant value: " << val << " from " << in << ".";
  384.             debugPrint( message.str() );
  385.             return val;
  386.         }
  387.     }
  388.     catch( runtime_error ex )
  389.     {
  390.         stringstream message;
  391.         message << ex.what() << " Part of expression \"" << in << "\".";
  392.         throw runtime_error( message.str() );
  393.     }
  394. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement