package dice; import java.util.*; // We need Random let Random random=new Random(); // Random number generator /** A Roll is one set of dice and constants. 2d6+1 is a roll containing one die (2d6) and one constant (+1) */ class Roll{ // This is a collection of tuples // Each tuple has an object, and a sign (represented as '+' or '-') private Collection<(char,Object)> addends=new Vector(); // This operator puts a new addend into this Roll // The addend is given a '+' sign. alike `+` (T addend){ addends.add(('+',addend)); return this; } // This operator puts a new addend into this Roll // The addend is given a '-' sign. alike `-` (T addend){ addends.add(('-',addend)); return this; } // A few one-liners for cleaner syntax // (using int instead of Integer) alike `+` (int a)=this+new Integer(a); alike `-` (int a)=this-new Integer(a); // Print out this roll as a string toString(){ StringBuffer sb=new StringBuffer(); boolean first=true; for((char, Object) current : addends){ (char sign, Object addend)=current; if(!first || sign=='-') sb.append(sign); if(first)first=false; sb.append(addend.toString()); } return sb.toString(); } // Do something to each addend, and return the sum. // f(a) is added to the sum for positive addends, and // subtracted from the sum for negative ones int sum(Object->int f){ int total=0; for((char,Object) current : this.addends){ (char sign, Object addend)=current; if(sign=='-')total-=f(addend); else if(sign=='+')total+=f(addend); } return total; } } class Die{ int num; int sides; toString()=num+"d"+sides; } new Die(String str){ StringTokenizer tok=new StringTokenizer(str,"d"); int num=Integer.parseInt(tok.nextToken()); int sides=Integer.parseInt(tok.nextToken()); this(num: num, sides: sides); } /** Something is Rollable if it can be part of a Roll (either a constant or a die) */ abstract interface Rollable{} // Make things implement Rollable class java.lang.Integer implements Rollable; class dice.Die implements Rollable; class dice.Roll implements Rollable; // Default methods for rolling things int roll(T o)=0; int min(T o)=0; int max(T o)=0; // Override rolling methods for Integer roll(Integer i)=i.intValue(); min(Integer i)=i.intValue(); max(Integer i)=i.intValue(); // Override rolling methods for Die roll(Die d){ int sum=0; for(int n : 1..d.num) sum+=random.nextInt(d.sides)+1; return sum; } min(Die d)=d.num; max(Die d)=d.sides*d.num; // Override rolling methods for Roll itself // Each of these functions would have the same body, // so we use the sum() method in roll and pass in closures. roll(Roll r){ return r.sum(Object a=>a.roll()); } min(Roll r){ return r.sum(Object a=>a.min()); } max(Roll r){ return r.sum(Object a=>a.max()); } void main(String[] args){ Roll r=new Roll(); r+=new Die("2d6"); r+=new Die("1d20"); r-=new Die("1d10"); r+=3; println(r); println("min: "r.min()); println("max: "r.max()); println("roll: "r.roll()); Roll r2=new Roll(); r2-=4; r2+=r; r2+=10; println(r2); println("min: "r2.min()); println("max: "r2.max()); println("roll: "r2.roll()); }