Browse Source

Making the structure less of a mess because I don't need to support 2{2{2{...

Sam Jaffe 7 years ago
parent
commit
9531b593d6
3 changed files with 127 additions and 125 deletions
  1. 23 7
      include/die.h
  2. 64 57
      src/die.cxx
  3. 40 61
      src/roll.cxx

+ 23 - 7
include/die.h

@@ -8,20 +8,36 @@
 
 #pragma once
 
-#include <boost/variant/variant.hpp>
-
 #include <iosfwd>
 #include <memory>
+#include <vector>
 
 namespace dice {
-  struct dice;
-  using sub_dice = boost::variant<int, std::shared_ptr<dice>>;
+  enum sign {
+    PLUS = 1, MINUS = -1, ZERO = 0
+  };
+  template <typename T> static sign sgn(T val) {
+    return sign((T(0) < val) - (val < T(0)));
+  }
+  int sgn(sign);
+  std::string str(sign);
+  
+  struct die {
+    sign sgn;
+    int num, sides;
+  };
+  
+  struct mod {
+    operator int() const;
+    sign sign;
+    int value;
+  };
   
-  // Default value: 1d1+0
+  // Default value: 1{+0}
   struct dice {
     int num{1};
-    sub_dice of{1};
-    sub_dice modifier{+0};
+    std::vector<die> of{};
+    std::vector<mod> modifier{+0};
   };
     
   std::ostream & operator<<(std::ostream & out, dice const & d);

+ 64 - 57
src/die.cxx

@@ -13,7 +13,8 @@
 
 #include <iostream>
 
-static void advance_over_whitespace(std::istream & in) {
+static void advance_over_whitespace(std::istream & in, char const * also = "") {
+  if (strchr(also, in.peek())) { in.get(); }
   while (isspace(in.peek())) { in.get(); }
 }
 
@@ -25,18 +26,21 @@ static std::string carrot(long long pos) {
   return out;
 }
 
-void print(std::ostream & out, int i) { out << 'd' << i; }
-void print(std::ostream & out, std::shared_ptr<dice::dice> d) {
-  out << '{' << *d << '}';
-}
-
-void modifier(std::ostream & out, int i) {
-  if (i > 0) out << '+';
-  if (i) out << i;
-}
-void modifier(std::ostream & out, std::shared_ptr<dice::dice> d) {
-  if (d->num > 0) out << '+';
-  out << *d;
+namespace dice {
+  int sgn(sign s) {
+    return s == MINUS ? -1 : 1;
+  }
+  std::string str(sign s) {
+    switch (s) {
+      case PLUS:  return "+";
+      case MINUS: return "-";
+      default:    return "";
+    }
+  }
+  
+  mod::operator int() const {
+    return sgn(sign) * value;
+  }
 }
 
 namespace dice {
@@ -46,67 +50,70 @@ namespace dice {
   }
     
   std::ostream & operator<<(std::ostream & out, dice const & d) {
-    out << d.num;
-    boost::apply_visitor([&out](auto v) { print(out, v); }, d.of);
-    boost::apply_visitor([&out](auto v) { modifier(out, v); }, d.modifier);
+    if (d.num != 1) out << d.num << '{';
+    for (die const & di : d.of) {
+      out << str(di.sgn) << di.num << 'd' << di.sides;
+    }
+    for (mod m : d.modifier) {
+      out << str(m.sign) << m.value;
+    }
+    if (d.num != 1) out << '}';
     return out;
   }
   
-  static void parse(std::istream & in, dice & d, bool allow_nest = false);
+  static void parse(std::istream & in, dice & d, sign s);
+  
+  static void parse_dN(std::istream & in, dice & d, die di) {
+    advance_over_whitespace(in, "dD");
+    d.of.push_back(di);
+    in >> d.of.back().sides;
+    parse(in, d, ZERO);
+  }
   
-  static void parse_inner_expr(std::istream & in, dice & d, bool allow_nest) {
-    using sp_dice = std::shared_ptr<dice>;
+  static void parse(std::istream & in, dice & d, sign s) {
+    // Allow implicit 1
+    advance_over_whitespace(in);
+    int value = 0;
+    if (isnumber(in.peek())) { in >> value; }
     advance_over_whitespace(in);
-    switch (in.get()) {
+    switch (in.peek()) {
       case 'd': case 'D':
-        in >> boost::get<int>(d.of);
-        break;
-      case '{':
-        if (!allow_nest) { throw unexpected_token(in.tellg()); }
-        d.of = std::make_shared<dice>();
-        parse(in, *boost::get<std::shared_ptr<dice>>(d.of));
-        if (in.get() != '}') { throw unexpected_token(in.tellg()); }
+        return parse_dN(in, d, {s, value, 0});
+      case '+': case '-':
+        if (value) {
+          d.modifier.push_back({s, std::abs(value)});
+        }
+        s = (in.get() == '+') ? PLUS : MINUS;
+        parse(in, d, s);
         break;
       case EOF:
         break;
       default:
-        throw unexpected_token(in.tellg());
+        d.modifier.push_back({s, std::abs(value)});
+        break;
     }
   }
-  
-  static void parse_modifier_expr(std::istream & in, dice & d) {
+
+  std::istream & operator>>(std::istream & in, dice & d) {
+    int value{1};
     advance_over_whitespace(in);
-    int modifier = 0;
+    if (isnumber(in.peek())) { in >> value; }
     switch (in.peek()) {
-      case '+':
+      case 'd': case 'D':
+        parse(in, d, ZERO);
+        d.of.front().num = value;
+        break;
+      case '{':
         in.get();
-      case '-':
-        in >> modifier;
-        advance_over_whitespace(in);
-        if (in.peek() != 'd' && in.peek() != 'D') {
-          d.modifier = modifier;
-        } else {
-          d.modifier = std::make_shared<dice>();
-          parse(in, *boost::get<std::shared_ptr<dice>>(d.modifier));
-          boost::get<std::shared_ptr<dice>>(d.modifier)->num = modifier;
+        d.num = value;
+        parse(in, d, ZERO);
+        if (in.get() != '}') {
+          throw unexpected_token(in.tellg());
         }
-      default:
         break;
+      default:
+        throw unexpected_token(in.tellg());
     }
-  }
-  
-  static void parse(std::istream & in, dice & d, bool allow_nest) {
-    // Allow implicit 1
-    advance_over_whitespace(in);
-    if (isnumber(in.peek())) { in >> d.num; }
-    parse_inner_expr(in, d, allow_nest);
-    if (!d.of.which()) {
-      parse_modifier_expr(in, d);
-    }
-  }
-
-  std::istream & operator>>(std::istream & in, dice & d) {
-    parse(in, d, true);
-    return in;
+   return in;
   }
 }

+ 40 - 61
src/roll.cxx

@@ -8,9 +8,6 @@
 
 #include "roll.h"
 
-#include <boost/variant/apply_visitor.hpp>
-#include <boost/variant/get.hpp>
-
 #include <iostream>
 #include <memory>
 #include <numeric>
@@ -19,50 +16,30 @@
 #include "random.h"
 
 namespace dice {
-  struct roll_t;
-  using chain_roll = boost::variant<int, std::shared_ptr<roll_t>>;
-  struct roll_t {
+  struct die_roll {
     operator int() const;
-    
-    enum Sign { PLUS=1, MINUS=-1, ZERO=0 } sign{ZERO};
+    sign sign;
     std::vector<int> rolled;
-    chain_roll chain;
   };
-
-  roll_t roll_impl(dice const & d, engine::random & gen);
-}
-
-template <typename T> static int sgn(T val) {
-  return (T(0) < val) - (val < T(0));
+  struct dice_roll {
+    operator int() const;
+    std::vector<die_roll> sub_rolls;
+    std::vector<mod> modifiers;
+  };
 }
 
 namespace dice {
-  int to_int(int i) { return i; }
-  int to_int(std::shared_ptr<roll_t> r) { return *r; }
-  roll_t::operator int() const {
-    return (sign ? sign : 1) *
-    std::accumulate(rolled.begin(), rolled.end(), 0) +
-    boost::apply_visitor([](auto c) { return to_int(c); }, chain);
-  }
-  
-  void print(std::ostream & out, roll_t const & r);
-  void print(std::ostream & out, int i) {
-    if (i > 0) out << '+';
-    if (i) out << i;
+  die_roll::operator int() const {
+    return sgn(sign) * std::accumulate(rolled.begin(), rolled.end(), 0);
   }
-  void print(std::ostream & out, std::shared_ptr<roll_t> r) {
-    print(out, *r);
+
+  dice_roll::operator int() const {
+    return std::accumulate(sub_rolls.begin(), sub_rolls.end(), 0) +
+        std::accumulate(modifiers.begin(), modifiers.end(), 0);
   }
-  void print(std::ostream & out, roll_t const & r) {
-    switch (r.sign) {
-      case roll_t::PLUS:
-        out << '+';
-        break;
-      case roll_t::MINUS:
-        out << '-';
-      default:
-        break;
-    }
+  
+  void print(std::ostream & out, die_roll const & r) {
+    out << str(r.sign);
     if (r.rolled.size() > 1) {
       out << "[ ";
     }
@@ -73,49 +50,51 @@ namespace dice {
     if (r.rolled.size() > 1) {
       out << " ]";
     }
-    boost::apply_visitor([&out](auto c) { return print(out, c); }, r.chain);
+  }
+  
+  void print(std::ostream & out, dice_roll const & r) {
+    for (die_roll const & dr : r.sub_rolls) {
+      print(out, dr);
+    }
+    for (mod const & m : r.modifiers) {
+      out << str(m.sign);
+      out << m.value;
+    }
   }
 
-  std::ostream & operator<<(std::ostream & out, roll_t const & r) {
+  std::ostream & operator<<(std::ostream & out, dice_roll const & r) {
     out << int(r) << " (";
     print(out, r);
     return out << ")";
   }
   
-  chain_roll roll_impl(int i, engine::random &) {
-    return i;
-  }
-  chain_roll roll_impl(std::shared_ptr<dice> d, engine::random & gen) {
-    return std::make_shared<roll_t>(roll_impl(*d, gen));
+  die_roll roll_impl(die const & d, engine::random & gen) {
+    std::vector<int> hits;
+    for (int i = 0; i < d.num; ++i) {
+      hits.push_back(gen.roll(d.sides));
+    }
+    return {d.sgn, hits};
   }
   
-  roll_t roll_impl(dice const & d, engine::random & gen) {
-    std::vector<int> hits;
-    int const count = std::abs(d.num);
-    int const sign = sgn(d.num);
-    hits.reserve(count);
-    for (int i = 0; i < count; ++i) {
-      hits.push_back(gen.roll(boost::get<int>(d.of)));
+  dice_roll roll_impl(dice const & d, engine::random & gen) {
+    std::vector<die_roll> hits;
+    for (die const & di : d.of) {
+      hits.push_back(roll_impl(di, gen));
     }
-    auto chain = boost::apply_visitor([&gen](auto d) {
-      return roll_impl(d, gen);
-    }, d.modifier);
-    return { (roll_t::Sign)sign, hits, chain };
+    return {hits, d.modifier};
   }
   
   void roll(dice const & d) {
     engine::random gen;
     std::cout << "Result of '" << d << "': ";
-    if (d.of.which()) {
+    if (d.num != 1) {
       std::cout << "\n";
       for (int i = 0; i < d.num; ++i) {
-        auto r = roll_impl(*boost::get<std::shared_ptr<dice>>(d.of), gen);
-        r.sign = roll_t::ZERO;
+        auto r = roll_impl(d, gen);
         std::cout << "  Result/" << i << ": " << r << "\n";
       }
     } else {
       auto r = roll_impl(d, gen);
-      r.sign = roll_t::ZERO;
       std::cout << r << "\n";
     }
   }