From: <mar...@us...> - 2013-09-18 23:07:14
|
Revision: 102 http://sourceforge.net/p/openrpg/svn/102 Author: markt1964 Date: 2013-09-18 23:07:11 +0000 (Wed, 18 Sep 2013) Log Message: ----------- Complete rewrite of dice rolling code Added Paths: ----------- trunk/src/openrpg2/common/dice/ trunk/src/openrpg2/common/dice/CompareExpr.java trunk/src/openrpg2/common/dice/Const.java trunk/src/openrpg2/common/dice/DiceExpression.java trunk/src/openrpg2/common/dice/DiceParser.java trunk/src/openrpg2/common/dice/DieFunction.java trunk/src/openrpg2/common/dice/Function.java trunk/src/openrpg2/common/dice/MultiDice.java trunk/src/openrpg2/common/dice/MultiValue.java trunk/src/openrpg2/common/dice/MultiValueFunction.java trunk/src/openrpg2/common/dice/NegExpr.java trunk/src/openrpg2/common/dice/ProdExpr.java trunk/src/openrpg2/common/dice/Scanner.java trunk/src/openrpg2/common/dice/SumExpr.java trunk/src/openrpg2/common/dice/TestExpr.java trunk/src/openrpg2/common/dice/UniValue.java trunk/src/openrpg2/common/dice/UniValueFunction.java Removed Paths: ------------- trunk/src/openrpg2/common/dice/ Added: trunk/src/openrpg2/common/dice/CompareExpr.java =================================================================== --- trunk/src/openrpg2/common/dice/CompareExpr.java (rev 0) +++ trunk/src/openrpg2/common/dice/CompareExpr.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,194 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + + +/** + * + * @author markt + */ +public abstract class CompareExpr implements DiceExpression { + /** + * Left and right hand sides of this expression + */ + protected DiceExpression left,right; + /** + * Name for the comparitor operator in this expression + */ + protected String name; + + /** + * + * @return 0 + */ + public int getValue() + { + return 0; + } + + /** + * + * @return text form of this compare expresssion + */ + public String getText() + { + return left.getText()+name+right.getText(); + } + + /** + * + * @return text form of this compare expresssion + */ + public String getTextDetail() + { + return left.getTextDetail()+name+right.getTextDetail(); + } + + /** + * Executes both sides of this compare expression + */ + public void exec() + { + left.exec(); + right.exec(); + } + + /** + * Compare Expression constructor + * @param l left hand side + * @param r right hand side + */ + public CompareExpr(DiceExpression l,DiceExpression r) + { + left=l; + right=r; + } + + /** + * Performs the actual comparison test + * @return true if the test is successful, false otherwise + */ + public abstract boolean test(); + + /** + * Invalid for comparison functions + * @return nothing, throws an exception + */ + public DiceExpression negate() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + +} +class CompareL extends CompareExpr +{ + public CompareL(DiceExpression l,DiceExpression r) + { + super(l,r); + name="<"; + } + + public boolean test() + { + return left.getValue()<right.getValue(); + } +} + +class CompareG extends CompareExpr +{ + public CompareG(DiceExpression l,DiceExpression r) + { + super(l,r); + name=">"; + } + + public boolean test() + { + return left.getValue()>right.getValue(); + } +} + +class CompareLE extends CompareExpr +{ + public CompareLE(DiceExpression l,DiceExpression r) + { + super(l,r); + name="<="; + } + + public boolean test() + { + return left.getValue()<=right.getValue(); + } +} + +class CompareGE extends CompareExpr +{ + public CompareGE(DiceExpression l,DiceExpression r) + { + super(l,r); + name=">="; + } + + public boolean test() + { + return left.getValue()>=right.getValue(); + } +} + +class CompareE extends CompareExpr +{ + public CompareE(DiceExpression l,DiceExpression r) + { + super(l,r); + name="="; + } + + public boolean test() + { + if (right instanceof TestExpr) + { + return ((TestExpr)right).testValue(left.getValue()); + } + else + { + return left.getValue()==right.getValue(); + } + } +} + +class CompareNE extends CompareExpr +{ + public CompareNE(DiceExpression l,DiceExpression r) + { + super(l,r); + name="<>"; + } + + public boolean test() + { + if (right instanceof TestExpr) + { + return !((TestExpr)right).testValue(left.getValue()); + } + else + { + return left.getValue()!=right.getValue(); + } + } +} Added: trunk/src/openrpg2/common/dice/Const.java =================================================================== --- trunk/src/openrpg2/common/dice/Const.java (rev 0) +++ trunk/src/openrpg2/common/dice/Const.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,88 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +/** + * + * @author markt + */ +public class Const implements UniValue, TestExpr +{ + private final int value; + + /** + * + * @return value of constant expression + */ + public int getValue() + { + return value; + } + + /** + * + * @return text form of constant expression + */ + public String getTextDetail() + { + return ""+value; + } + + /** + * + * @return text form of constant expression + */ + public String getText() + { + return ""+value; + } + + /** + * Placeholder for executing the expression (does nothing) + */ + public void exec() + { + } + + /** + * Constructor for constant expression + * @param n the integer this constant represents + */ + public Const(int n) + { + value=n; + } + + /** + * Test function to compare an integer with this expression + * @param n the integer to compare to + * @return true if they are the same, false otherwise + */ + public boolean testValue(int n) + { + return n==value; + } + + /** + * + * @return a negated constant expression + */ + public DiceExpression negate() + { + return new Const(-value); + } +} Added: trunk/src/openrpg2/common/dice/DiceExpression.java =================================================================== --- trunk/src/openrpg2/common/dice/DiceExpression.java (rev 0) +++ trunk/src/openrpg2/common/dice/DiceExpression.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,49 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +/** + * + * @author markt + */ +public interface DiceExpression +{ + /** + * + * @return the integer value for this expression + */ + public int getValue(); + /** + * + * @return the negated form if this expression + */ + public DiceExpression negate(); + /** + * + * @return text form of this expression + */ + public String getText(); + /** + * + * @return text form of this expression, after executing + */ + public String getTextDetail(); + /** + * Executes this expression + */ + public void exec(); +} Added: trunk/src/openrpg2/common/dice/DiceParser.java =================================================================== --- trunk/src/openrpg2/common/dice/DiceParser.java (rev 0) +++ trunk/src/openrpg2/common/dice/DiceParser.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,540 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +import java.io.StringReader; +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; + +/** + * Parses a numeric dice expression<br/> + * A dice expression is a mathematical expression that uses standard 'd' + * notation to represent a die with a given number of sides, and use that + * in an arithmetic expression, such as 3d8+1, which will roll 3 8-sided dice, + * and add 1 to the result.<br/> + * There are numerous functions and die roll modifiers that can be applied as + * well.<br/> + * For example, the dice expression <tt>highest(4d6,3)</tt> rolls 4 6-sided dice + * and takes the highest 3 for a result.<br/> + * Additionally, dice expressions can have modifers, for example + * <tt>5d6.reroll(1)</tt> would roll 5 6-sided dice rerolling 1's.<br/> + * Supported functions are as follows + * <ul> + * <li>highest(dice, n) takes the highest 'n' dice out of the ones in the dice + * expression.</li> + * <li>lowest(dice, n) takes the lowest 'n' dice out of the ones in the dice + * expression</li> + * <li>until(expr,test) keeps rerolling dice in the given expression until the + * value of the expression satisfies the test expression provided, which can be + * the word "even", "odd", "any", a single number, or a range of numbers + * specified as start..end (either the start or end value + * may optionally be omitted, in which case either would default to a minimum + * or maximum value, as appropriate</li> + * <li>max(expr,expr...) results in the maximum of the expressions passed into + * it.</li> + * <li>min(expr,expr...) results in the maximum of the expressions passed into + * it.</li> + * <li>count(dice) returns the number of dice rolled</li> + * </ul> + * Supported dice modifiers are as follows + * <ul> + * <li>min(value) specifies the minimum value for any given die. + * If the value rolled is less than this, the minimum is taken</li> + * <li>max(value) specifies the maximum value for any given die.</li> + * <li>reroll(test) causes dice values that satisfy test to be rerolled (see + * description of 'until' function above for an outline of what forms are + * suitable for test)</li> + * <li>ignore(test) causes dice values that satisfy test to be ignored.</li> + * <li>change(test,expr) causes dice values that satisfy test to be changed to + * the given expression</li> + * <li>more(test,expr) When a die roll satisfies the test case, add more dice to + * those being rolled. The number of dice to roll is given by the expression + * provided (and may itself be a dice expression)</li> + * </ul> + * @author markt + */ +public class DiceParser { + + static class CIString implements Comparable<CIString> + { + private String str; + + CIString(String s) + { + str=s; + } + + @Override + public String toString() + { + return str; + } + + @Override + public int hashCode() + { + return str.toUpperCase().hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final CIString other = (CIString) obj; + if ((str == null) ? (other.str != null) : !str.equalsIgnoreCase(other.str)) + { + return false; + } + return true; + } + + public int compareTo(CIString o) + { + if (str==null && o.str==null) + { + return 0; + } + if (str==null) + { + return -1; + } + if (o==null) + { + return 1; + } + return str.compareToIgnoreCase(o.str); + } + + } + + static interface DieFunctionFactory { + public DieFunction create(); + } + + static interface FunctionFactory { + public Function create(); + } + + static Map<CIString,DieFunctionFactory> dieFunctions = new HashMap<CIString,DieFunctionFactory>(); + static Map<CIString,FunctionFactory> functions = new HashMap<CIString,FunctionFactory>(); + + static { + dieFunctions.put(new CIString("min"), new DieFunctionFactory() + { + public DieFunction create() + { + return new MinDieFunc(); + } + }); + dieFunctions.put(new CIString("max"), new DieFunctionFactory() + { + public DieFunction create() + { + return new MaxDieFunc(); + } + }); + dieFunctions.put(new CIString("reroll"), new DieFunctionFactory() + { + + public DieFunction create() + { + return new RerollDieFunc(); + } + }); + dieFunctions.put(new CIString("ignore"), new DieFunctionFactory() + { + public DieFunction create() + { + return new IgnoreDieFunc(); + } + }); + dieFunctions.put(new CIString("change"), new DieFunctionFactory() + { + public DieFunction create() + { + return new ChangeDieFunc(); + } + }); + dieFunctions.put(new CIString("more"), new DieFunctionFactory() + { + public DieFunction create() + { + return new MoreDieFunc(); + } + }); + + functions.put(new CIString("min"), new FunctionFactory() + { + public Function create() + { + return new MinFunc(); + } + }); + functions.put(new CIString("max"), new FunctionFactory() + { + public Function create() + { + return new MaxFunc(); + } + }); + functions.put(new CIString("highest"), new FunctionFactory() + { + public Function create() + { + return new HighestFunc(); + } + }); + functions.put(new CIString("lowest"), new FunctionFactory() + { + public Function create() + { + return new LowestFunc(); + } + }); + functions.put(new CIString("until"), new FunctionFactory() + { + public Function create() + { + return new UntilFunc(); + } + }); + functions.put(new CIString("dice"), new FunctionFactory() + { + public Function create() + { + return new DiceFunc(); + } + }); + } + + /** + * Gets a test expression + * @param scanner Scanner to read from + * @return a test expression + * @throws ParseException on error + */ + public static TestExpr getTest(Scanner scanner) throws ParseException + { + Object token = scanner.getToken(); + TestExpr test =null; + if (token==Scanner.TINTEGER) + { + int start; + start = scanner.getIntValue(); + scanner.scan(); + if (scanner.getToken()==Scanner.TDOTDOT) + { + scanner.scan(); + if(scanner.getToken()==Scanner.TINTEGER) + { + int end = scanner.getIntValue(); + scanner.scan(); + if ( end < start ) + { + throw new ParseException("Second value cannot be before first",scanner.getLastPosition()); + } + if ( end == start ) + { + test = new Const(start); + } + else + { + test = new RangeTest(start,end); + } + } + else + { + test = new RangeTest(start,Integer.MAX_VALUE); + } + } + else + { + test = new Const(start); + } + } + else if (token==Scanner.TDOTDOT) + { + scanner.scan(); + if(scanner.getToken()==Scanner.TINTEGER) + { + test=new RangeTest(Integer.MIN_VALUE,scanner.getIntValue()); + scanner.scan(); + } + else + { + test = new RangeTest(Integer.MIN_VALUE,Integer.MAX_VALUE); + } + } + else if (token==Scanner.TID) + { + if (scanner.getString().equalsIgnoreCase("any")) + { + test = new RangeTest(Integer.MIN_VALUE,Integer.MAX_VALUE); + scanner.scan(); + } + else if (scanner.getString().equalsIgnoreCase("even")) + { + test = new EvenTest(); + scanner.scan(); + } + else if (scanner.getString().equalsIgnoreCase("odd")) + { + test = new OddTest(); + scanner.scan(); + } + } + if ( test==null ) + { + throw new ParseException("Expected test value",scanner.getLastPosition()); + } + return test; + } + + /** + * Gets an expression + * @param scanner Scanner to read from + * @return the expression at the current position in the scanner + * @throws ParseException on error + */ + public static DiceExpression getExpression(Scanner scanner) throws ParseException + { + DiceExpression expr = null; + expr = getTerm(scanner); + if(scanner.getToken()==Scanner.TPLUS || scanner.getToken()==Scanner.TMINUS) + { + SumExpr result = new SumExpr(); + result.append(expr); + while(scanner.getToken()==Scanner.TPLUS || scanner.getToken()==Scanner.TMINUS) + { + if(scanner.getToken()==Scanner.TPLUS) + { + scanner.scan(); + } + result.append(getTerm(scanner)); + } + return result; + } + return expr; + } + + /** + * Gets a single term expression + * @param scanner Scanner to read from + * @return a single term expression from the scanner + * @throws ParseException on error + */ + public static DiceExpression getTerm(Scanner scanner) throws ParseException + { + DiceExpression expr = getFactor(scanner); + if(scanner.getToken()==Scanner.TSTAR || scanner.getToken()==Scanner.TSLASH) + { + ProdExpr result = new ProdExpr(); + result.appendNum(expr); + while(scanner.getToken()==Scanner.TSTAR || scanner.getToken()==Scanner.TSLASH) + { + if(scanner.getToken()==Scanner.TSTAR) + { + scanner.scan(); + result.appendNum(getFactor(scanner)); + } + else + { + scanner.scan(); + result.appendDen(getFactor(scanner)); + } + } + return result; + } + return expr; + } + + /** + * Gets a single factor + * @param scanner Scanner to read from + * @return a single factor from the scanner + * @throws ParseException on error + */ + public static DiceExpression getFactor(Scanner scanner) throws ParseException + { + DiceExpression result; + boolean negate = false; + while (scanner.getToken()==Scanner.TMINUS) + { + negate=!negate; + scanner.scan(); + } + result=getUnary(scanner); + if (negate) + { + result=result.negate(); + } + return result; + } + + /** + * Gets a unary expression from the scanner + * @param scanner the Scanner to read from + * @return the unary expression from the scanner + * @throws ParseException on error + */ + public static DiceExpression getUnary(Scanner scanner) throws ParseException + { + if (scanner.getToken()==Scanner.TLEFT) + { + DiceExpression result; + scanner.scan(); + result=getExpression(scanner); + if(scanner.getToken()!=Scanner.TRIGHT) + { + throw new ParseException("Expected ')'",scanner.getLastPosition()); + } + scanner.scan(); + return result; + } + else if (scanner.getToken()==Scanner.TDICE) + { + MultiDice result = new MultiDice(scanner.getIntValue(),scanner.getDiceValue()); + scanner.scan(); + while(scanner.getToken()==Scanner.TDOT) + { + scanner.scan(); + if(scanner.getToken()!=Scanner.TID) + { + throw new ParseException("Expected function name",scanner.getLastPosition()); + } + DieFunctionFactory factory = dieFunctions.get(new CIString(scanner.getString())); + if(factory==null) + { + throw new ParseException("Expected function name",scanner.getLastPosition()); + } + DieFunction diefunc = factory.create(); + scanner.scan(); + if (scanner.getToken()!=Scanner.TLEFT) + { + throw new ParseException("Expected '('",scanner.getLastPosition()); + + } + scanner.scan(); + boolean parse = diefunc.parse(scanner); + if (!parse) + { + throw new ParseException("Invalid Argument(s)",scanner.getLastPosition()); + } + if (scanner.getToken()!=Scanner.TRIGHT) + { + throw new ParseException("Expected ')'",scanner.getLastPosition()); + } + scanner.scan(); + result.addModifier(diefunc); + } + return result; + } + else if (scanner.getToken()==Scanner.TINTEGER) + { + Const result = new Const(scanner.getIntValue()); + scanner.scan(); + return result; + } + else if (scanner.getToken()==Scanner.TID) + { + FunctionFactory factory=functions.get(new CIString(scanner.getString())); + if(factory==null) + { + throw new ParseException("Expected function name",scanner.getLastPosition()); + } + Function result = factory.create(); + scanner.scan(); + if (scanner.getToken()!=Scanner.TLEFT) + { + throw new ParseException("Expected '('",scanner.getLastPosition()); + + } + scanner.scan(); + boolean parse = result.parse(scanner); + if (!parse) + { + throw new ParseException("Invalid Argument(s)",scanner.getLastPosition()); + } + if (scanner.getToken()!=Scanner.TRIGHT) + { + throw new ParseException("Expected ')'",scanner.getLastPosition()); + } + scanner.scan(); + return result; + } + else + { + throw new ParseException("Expected expression",scanner.getLastPosition()); + } + } + + /** + * Parses a string to retrieve a DiceExpression. + * @param s The string to parse + * @return a Dice Expression which represents what was in the string + * @throws ParseException + */ + public static DiceExpression parse(String s) throws ParseException + { + Scanner scanner=new Scanner(new StringReader(s)); + DiceExpression result=getExpression(scanner); + if ( scanner.getToken() != Scanner.TEOF ) + { + throw new ParseException("Unexpected characters at end of expression",scanner.getLastPosition()); + } + return result; + } + + /** + * Test function + * @param args + * @throws ParseException on error + */ + public static void main(String[] args) throws ParseException + { + String s="dice(until(3d8,3..18),until(3d8,3..18),until(3d8,3..18),until(3d8,3..18),until(3d8,3..18),until(3d8,3..18))"; + DiceExpression express=parse(s); + for(int i=0;i<100;i++) + { + express.exec(); + //System.out.print(express.getValue()+" "); + System.out.print('['); + int [] details=((MultiValue)express).getDetails(); + for(int j=0;j<details.length;j++) + { + if ( j != 0 ) + { + System.out.print(','); + } + if ( details[j] < 10 ) + { + System.out.print(' '); + } + System.out.print(details[j]); + } + System.out.println(']'); + } + } +} Added: trunk/src/openrpg2/common/dice/DieFunction.java =================================================================== --- trunk/src/openrpg2/common/dice/DieFunction.java (rev 0) +++ trunk/src/openrpg2/common/dice/DieFunction.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,305 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +import java.text.ParseException; +import java.util.ArrayList; + + +/** + * + * @author markt + */ +public abstract class DieFunction +{ + private final String name; + /** + * Parameters for this dice function + */ + protected ArrayList<DiceExpression> params = new ArrayList<DiceExpression>(); + + /** + * Constructor for DieFunction + * @param n name of die function + */ + public DieFunction(String n) + { + name=n; + } + + /** + * + * @return text form of the die function, plus parameters + */ + public String getText() + { + StringBuilder result=new StringBuilder(); + result.append(name+"("); + boolean comma=false; + for(DiceExpression i:params) + { + if(comma) + { + result.append(','); + } + else + { + comma=true; + } + result.append(i.getText()); + } + result.append(')'); + return result.toString(); + } + + /** + * Executes the die function + */ + public void exec() + { + for(DiceExpression i:params) + { + i.exec(); + } + } + + /** + * Says what the new value of the currently rolled die should be + * @param orig original rolled value + * @param current rolled value + * @return new value + */ + public int getAlteredValue(int orig,int current) + { + return current; + } + + /** + * Tells how many more new dice should be rolled + * @param orig original rolled value + * @param current rolled value + * @return number of extra dice to roll + */ + public int getNewDice(int orig,int current) + { + return 0; + } + + /** + * DiceParser for the die function, parses each argument + * @param scanner Scanner being used to read input + * @return true if the function was parsed successfully, false otherwise + * @throws ParseException on error + */ + public abstract boolean parse(Scanner scanner) throws ParseException; +} +class RerollDieFunc extends DieFunction +{ + public RerollDieFunc() + { + super("reroll"); + } + + @Override + public int getAlteredValue(int orig,int current) + { + TestExpr test = (TestExpr) params.get(0); + if (test.testValue(current)) + { + return 0; + } + return current; + } + + @Override + public int getNewDice(int orig,int current) + { + TestExpr test = (TestExpr) params.get(0); + if (test.testValue(current)) + { + return 1; + } + return 0; + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + params.add(DiceParser.getTest(scanner)); + return true; + } +} + +class MinDieFunc extends DieFunction +{ + public MinDieFunc() + { + super("min"); + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + DiceExpression expr = DiceParser.getExpression(scanner); + if ( expr != null ) + { + params.add(expr); + return true; + } + return false; + } + + @Override + public int getAlteredValue(int orig,int current) + { + int test = params.get(0).getValue(); + if ( current < test ) + { + return test; + } + return current; + } +} + +class MaxDieFunc extends DieFunction +{ + public MaxDieFunc() + { + super("max"); + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + DiceExpression expr = DiceParser.getExpression(scanner); + if ( expr != null ) + { + params.add(expr); + return true; + } + return false; + } + + @Override + public int getAlteredValue(int orig,int current) + { + int test = params.get(0).getValue(); + if ( current > test ) + { + return test; + } + return current; + } +} + +class IgnoreDieFunc extends DieFunction +{ + public IgnoreDieFunc() + { + super("ignore"); + } + + + @Override + public int getAlteredValue(int orig,int current) + { + TestExpr test = (TestExpr) params.get(0); + if (test.testValue(current)) + { + return 0; + } + return current; + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + params.add(DiceParser.getTest(scanner)); + return true; + } + +} + +class ChangeDieFunc extends DieFunction +{ + + public ChangeDieFunc() + { + super("change"); + } + + @Override + public int getAlteredValue(int orig,int current) + { + TestExpr test = (TestExpr) params.get(0); + if (test.testValue(current)) + { + params.get(1).exec(); + return params.get(1).getValue(); + } + return current; + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + params.add(DiceParser.getTest(scanner)); + if ( scanner.getToken()!=Scanner.TCOMMA ) + { + throw new ParseException("Expected ','",scanner.getLastPosition()); + } + scanner.scan(); + params.add(DiceParser.getExpression(scanner)); + return true; + } + +} + +class MoreDieFunc extends DieFunction +{ + + public MoreDieFunc() + { + super("more"); + } + + @Override + public int getNewDice(int orig,int current) + { + TestExpr test = (TestExpr) params.get(0); + if (test.testValue(current)) + { + params.get(1).exec(); + return params.get(1).getValue(); + } + return 0; + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + params.add(DiceParser.getTest(scanner)); + if ( scanner.getToken()!=Scanner.TCOMMA ) + { + throw new ParseException("Expected ','",scanner.getLastPosition()); + } + scanner.scan(); + params.add(DiceParser.getExpression(scanner)); + return true; + } + +} \ No newline at end of file Added: trunk/src/openrpg2/common/dice/Function.java =================================================================== --- trunk/src/openrpg2/common/dice/Function.java (rev 0) +++ trunk/src/openrpg2/common/dice/Function.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +import java.text.ParseException; +import java.util.ArrayList; + +/** + * + * @author markt + */ +public abstract class Function implements DiceExpression { + /** + * parameters for the function + */ + protected ArrayList<DiceExpression> params=new ArrayList<DiceExpression>(); + + private final String name; + + /** + * Gets the text detail for one of the paremeters of the function + * @param index which parameter to examine + * @return the text detail for the index'th parameter to the function + */ + public String getParameterTextDetail(int index) + { + return params.get(index).getTextDetail(); + + } + /** + * + * @return text form of this expression (after executing) + */ + public String getTextDetail() + { + StringBuilder result=new StringBuilder(); + result.append(name+"("); + for(int i=0; i< params.size();i++) + { + if (i!=0) + result.append(","); + result.append(getParameterTextDetail(i)); + } + result.append(")"); + if ( this instanceof UniValue ) + { + result.append("->("+this.getValue()+")"); + } + else if ( this instanceof MultiValue ) + { + MultiValue val=(MultiValue)this; + result.append("->["); + for(int i=0;i<val.getDetails().length;i++) + { + if ( i > 0 ) + { + result.append(','); + } + result.append(val.getDetails()[i]); + } + result.append("]"); + } + return result.toString(); + } + + /** + * + * @return text form of this expression + */ + public String getText() + { + StringBuilder result=new StringBuilder(); + result.append(name+"("); + boolean comma=false; + for(DiceExpression i:params) + { + if(comma) + { + result.append(","); + } + else + { + comma=true; + } + result.append(i.getText()); + } + result.append(")"); + return result.toString(); + } + + /** + * executes the function + */ + public void exec() + { + for(DiceExpression i:params) + { + i.exec(); + } + run(); + } + + /** + * Function constructor + * @param n name of the function + */ + public Function(String n) + { + name=n; + } + + public DiceExpression negate() + { + return new NegExpr(this); + } + + /** + * Parses the function + * @param scanner Scanner that is reading the input + * @return true if the function has been parsed, false otherwise + * @throws ParseException on error + */ + public abstract boolean parse(Scanner scanner) throws ParseException; + + /** + * Executes the function after the parameters have been processed + */ + public abstract void run(); +} + + Added: trunk/src/openrpg2/common/dice/MultiDice.java =================================================================== --- trunk/src/openrpg2/common/dice/MultiDice.java (rev 0) +++ trunk/src/openrpg2/common/dice/MultiDice.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,159 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +import java.util.ArrayList; +import java.util.Random; + +/** + * + * @author markt + */ +public class MultiDice implements DiceExpression, MultiValue { + private final static Random random = new Random(); + private final int sides; + private final int count; + private int[] details; + private final ArrayList<DieFunction> modifiers = new ArrayList<DieFunction>(); + + public int getValue() + { + int result=0; + for(int i=0;i<details.length;i++ ) + { + result+=details[i]; + } + if ( count < 0 ) + { + result=-result; + } + return result; + } + + public int[] getDetails() + { + return details; + } + + /** + * Executes this expression + */ + public void exec() + { + ArrayList<Integer> list = new ArrayList<Integer>(); + for(int i=0;i<Math.abs(count);i++ ) + { + int c = random.nextInt(sides)+1; + int old=c; + for(DieFunction j:modifiers) + { + j.exec(); + i-=j.getNewDice(old,c); + c=j.getAlteredValue(old,c); + if(c==0) + { + break; + } + } + if (c!=0) + { + list.add(new Integer(c)); + } + } + details=new int[list.size()]; + for(int i=0;i<details.length;i++) + { + details[i]=list.get(i).intValue(); + } + } + + /** + * Constructor for the multidice expression + * @param n number of dice to roll + * @param s number of sides for the dice + */ + public MultiDice(int n,int s) + { + count=n; + sides=s; + details = new int[0]; + } + + /** + * + * @return text form of the expression + */ + public String getText() + { + StringBuilder result=new StringBuilder(); + if (count<0) + { + result.append('-'); + } + if ( Math.abs(count) != 1 ) + { + result.append(Math.abs(count)); + } + result.append('d'); + result.append(sides); + for(DieFunction j:modifiers) + { + result.append("."); + result.append(j.getText()); + } + return result.toString(); + } + + /** + * + * @return detailed text form of the expression + */ + public String getTextDetail() + { + StringBuilder result=new StringBuilder(); + if (count<0) + { + result.append('-'); + } + result.append("["); + if (details.length>0) + { + result.append(details[0]); + } + for(int i=1;i<details.length;i++) + { + result.append(","); + result.append(details[i]); + } + result.append("]"); + return result.toString(); + } + + /** + * Adds a die function into this expression + * @param m DieFunction to add into the expression + */ + public void addModifier(DieFunction m) + { + modifiers.add(m); + } + + public DiceExpression negate() + { + return new NegExpr(this); + } +} Added: trunk/src/openrpg2/common/dice/MultiValue.java =================================================================== --- trunk/src/openrpg2/common/dice/MultiValue.java (rev 0) +++ trunk/src/openrpg2/common/dice/MultiValue.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,30 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +/** + * + * @author markt + */ +public interface MultiValue +{ + /** + * + * @return the values that this represents + */ + public int[] getDetails(); +} Added: trunk/src/openrpg2/common/dice/MultiValueFunction.java =================================================================== --- trunk/src/openrpg2/common/dice/MultiValueFunction.java (rev 0) +++ trunk/src/openrpg2/common/dice/MultiValueFunction.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,362 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +import java.text.ParseException; +import java.util.Arrays; + + +/** + * + * @author markt + */ +public abstract class MultiValueFunction extends Function implements MultiValue +{ + public int getValue() + { + int[] array=getDetails(); + int result=0; + for(int i=0;i<array.length;i++) + { + result+=array[i]; + } + return result; + } + + MultiValueFunction(String name) + { + super(name); + } +} +class DiceFunc extends MultiValueFunction +{ + public DiceFunc() + { + super("dice"); + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + params.add(DiceParser.getExpression(scanner)); + while(scanner.getToken()==Scanner.TCOMMA) + { + scanner.scan(); + params.add(DiceParser.getExpression(scanner)); + } + return true; + } + + @Override + public void run() + { + } + + public int[] getDetails() + { + int [] result=new int[params.size()]; + for(int i=0;i<result.length;i++) + { + result[i]=params.get(i).getValue(); + } + return result; + } + + @Override + public String getTextDetail() + { + StringBuilder result=new StringBuilder(); + result.append(']'); + boolean comma=false; + for(DiceExpression i:params) + { + if(comma) + { + result.append(","); + } + else + { + comma=true; + } + result.append(i.getTextDetail()); + } + result.append(']'); + return result.toString(); + } +} + +class HighestFunc extends MultiValueFunction +{ + MultiValue param; + int [] details = new int[0]; + HighestFunc() + { + super("highest"); + } + + public int[] getDetails() + { + return details; + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + DiceExpression first = DiceParser.getExpression(scanner); + if(first instanceof MultiValue) + { + param = (MultiValue)first; + } + else + { + throw new ParseException("Not a compound value",scanner.getLastPosition()); + } + params.add(first); + if(scanner.getToken()!=Scanner.TCOMMA) + { + throw new ParseException("Expected ','",scanner.getLastPosition()); + } + scanner.scan(); + if(scanner.getToken()!=Scanner.TINTEGER) + { + throw new ParseException("Expected integer",scanner.getLastPosition()); + } + if(scanner.getIntValue()<0) + { + throw new ParseException("Cannot have < 0 dice",scanner.getLastPosition()); + } + params.add(new Const(scanner.getIntValue())); + scanner.scan(); + return true; + } + + @Override + public void run() + { + int[] detailsSrc = param.getDetails(); + int count = params.get(1).getValue(); + details = new int[Math.min( count, detailsSrc.length )]; + for(int i=0;i<details.length;i++) + { + details[i]=i; + } + int comp=0; + for(int k=0;k<details.length;k++) + { + if(detailsSrc[details[k]]<detailsSrc[details[comp]]) + { + comp=k; + } + } + for(int i=details.length;i<detailsSrc.length;i++) + { + if (detailsSrc[i]>detailsSrc[details[comp]]) + { + details[comp]=i; + for(int k=0;i<details.length;k++) + { + if(detailsSrc[details[k]]<detailsSrc[details[comp]]) + { + comp=k; + } + } + } + } + Arrays.sort(details); + for(int i=0;i<details.length;i++) + { + details[i]=detailsSrc[details[i]]; + } + } + +} + +class LowestFunc extends MultiValueFunction +{ + MultiValue param; + int [] details = new int[0]; + LowestFunc() + { + super("lowest"); + } + + public int[] getDetails() + { + return details; + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + DiceExpression first = DiceParser.getExpression(scanner); + if(first instanceof MultiValue) + { + param = (MultiValue)first; + } + else + { + throw new ParseException("Not a compound value",scanner.getLastPosition()); + } + params.add(first); + if(scanner.getToken()!=Scanner.TCOMMA) + { + throw new ParseException("Expected ','",scanner.getLastPosition()); + } + scanner.scan(); + if(scanner.getToken()!=Scanner.TINTEGER) + { + throw new ParseException("Expected integer",scanner.getLastPosition()); + } + if(scanner.getIntValue()<0) + { + throw new ParseException("Cannot have < 0 dice",scanner.getLastPosition()); + } + params.add(new Const(scanner.getIntValue())); + scanner.scan(); + return true; + } + + @Override + public void run() + { + int[] detailsSrc = param.getDetails(); + int count = params.get(1).getValue(); + details = new int[Math.min( count, detailsSrc.length )]; + for(int i=0;i<details.length;i++) + { + details[i]=i; + } + int comp=0; + for(int k=0;k<details.length;k++) + { + if(detailsSrc[details[k]]>detailsSrc[details[comp]]) + { + comp=k; + } + } + for(int i=details.length;i<detailsSrc.length;i++) + { + if (detailsSrc[i]<detailsSrc[details[comp]]) + { + details[comp]=i; + for(int k=0;i<details.length;k++) + { + if(detailsSrc[details[k]]>detailsSrc[details[comp]]) + { + comp=k; + } + } + } + } + Arrays.sort(details); + for(int i=0;i<details.length;i++) + { + details[i]=detailsSrc[details[i]]; + } + } +} + +class UntilFunc extends MultiValueFunction +{ + private static final int[] empty = new int[0]; + boolean counting; + + public UntilFunc() + { + super("until"); + } + + public String getParameterTextDetail(int index) + { + if (index==1 && counting) + return "#" + super.getParameterTextDetail(index); + else + return super.getParameterTextDetail(index); + + } + + @Override + public boolean parse(Scanner scanner) throws ParseException + { + counting = false; + params.add(DiceParser.getExpression(scanner)); + if ( scanner.getToken()!=Scanner.TCOMMA ) + { + throw new ParseException("Expected ','",scanner.getLastPosition()); + } + scanner.scan(); + if (scanner.getToken()==Scanner.THATCH) + { + scanner.scan(); + counting = true; + } + params.add(DiceParser.getTest(scanner)); + return true; + } + + private int getTestValue(DiceExpression test) + { + if (counting) + { + if (test instanceof MultiValue) + { + return ((MultiValue)test).getDetails().length; + } + else + { + return 1; + } + } + else + { + return test.getValue(); + } + } + + @Override + public void exec() + { + do + { + params.get(0).exec(); + params.get(1).exec(); + } + while (!((TestExpr)params.get(1)).testValue(getTestValue(params.get(0)))); + run(); + } + + @Override + public void run() + { + } + + @Override + public int getValue() + { + return params.get(0).getValue(); + } + + public int[] getDetails() + { + if ( params.get(0) instanceof MultiValue ) + { + return ((MultiValue)params.get(0)).getDetails(); + } + return empty; + } + +} \ No newline at end of file Added: trunk/src/openrpg2/common/dice/NegExpr.java =================================================================== --- trunk/src/openrpg2/common/dice/NegExpr.java (rev 0) +++ trunk/src/openrpg2/common/dice/NegExpr.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,73 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +/** + * + * @author markt + */ +public class NegExpr implements DiceExpression,UniValue { + private final DiceExpression base; + + public int getValue() + { + return -base.getValue(); + } + + public String getText() + { + if ( base instanceof SumExpr || base instanceof ProdExpr || base instanceof NegExpr ) + { + return "-("+base.getText()+")"; + } + else + { + return "-"+base.getText(); + } + } + + public String getTextDetail() + { + if ( base instanceof SumExpr || base instanceof ProdExpr || base instanceof NegExpr ) + { + return "-("+base.getTextDetail()+")"; + } + else + { + return "-"+base.getTextDetail(); + } + } + + public void exec() + { + base.exec(); + } + + /** + * Constructor for NegExpr + * @param e expression to negate + */ + public NegExpr(DiceExpression e) + { + base = e; + } + + public DiceExpression negate() + { + return base; + } +} Added: trunk/src/openrpg2/common/dice/ProdExpr.java =================================================================== --- trunk/src/openrpg2/common/dice/ProdExpr.java (rev 0) +++ trunk/src/openrpg2/common/dice/ProdExpr.java 2013-09-18 23:07:11 UTC (rev 102) @@ -0,0 +1,205 @@ +/* + * ==================================================================== + * Copyright (C) 2005-2006 The OpenRPG Project (www.openrpg.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License on www.gnu.org for more details. + * ==================================================================== + */ + +package openrpg2.common.dice; + +import java.util.ArrayList; + +/** + * + * @author markt + */ +public class ProdExpr implements DiceExpression,UniValue +{ + private ArrayList<DiceExpression> numerator = new ArrayList<DiceExpression>(); + private ArrayList<DiceExpression> denominator = new ArrayList<DiceExpression>(); + + public int getValue() + { + int result=1; + for(DiceExpression j:numerator) + { + result*=j.getValue(); + } + for(DiceExpression j:denominator) + { + result/=j.getValue(); + } + return result; + } + + public String getText() + { + StringBuilder result=new StringBuilder(); + boolean product=false; + for(DiceExpression j:numerator) + { + if (product) + { + result.append('*'); + } + else + { + product=true; + } + if (j instanceof SumExpr || j instanceof ProdExpr) + { + result.append('('); + result.append(j.getText()); + result.append(')'); + } + else + { + result.append(j.getText()); + } + } + if (denominator.size()>0) + { + result.append('/'); + if (denominator.size()>1) + { + result.append('('); + } + for(DiceExpression j:denominator) + { + if (product) + { + result.append('*'); + } + else + { + product=true; + } + if (j instanceof SumExpr) + { + result.append('('); + result.append(j.getText()); + result.append(')'); + } + else + { + result.append(j.getText()); + } + } + if (denominator.size()>1) + { + result.append(')'); + } + } + return result.toString(); + } + + public String getTextDetail() + { + StringBuilder result=new StringBuilder(); + boolean product=false; + for(DiceExpression j:numerator) + { + if (product) + { + result.append('*'); + } + else + { + ... [truncated message content] |