Android Open Source - dice-probabilities Dice Set






From Project

Back to project page dice-probabilities.

License

The source code is released under:

MIT License

If you think the Android project dice-probabilities listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package org.kleemann.diceprobabilities;
//w w  w  .  ja  v  a 2  s. c  om
import java.text.DecimalFormat;

import org.apache.commons.math3.fraction.BigFraction;
import org.kleemann.diceprobabilities.distribution.Distribution;
import org.kleemann.diceprobabilities.graph.GraphView;
import org.kleemann.diceprobabilities.special.Special;
import org.kleemann.diceprobabilities.special.SpecialSpinner;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

/**
 * <p>This class contains the behavior for a set of pool dice, 
 * current dice, and result values. 
 * 
 * <p>Changes to the dice cause a background task to calculate the 
 * new result probability and distribution and report it to the 
 * passed GraphSettor.
 */
public class DiceSet {

  /**
   * <p>Caller must pass an array of these objects to the DiceSet constructor.
   * A pressing a pool die adds to the target die.  Pressing a target die
   * decreases it's value.
   */
  public static class DieType {
    private int sides;
    private Button pool;
    private Button current;
    public DieType(int sides, Button pool, Button current) {
      this.sides = sides;
      this.pool = pool;
      this.current = current;
    }
    public int getSides() { return sides; }
    public Button getPool() { return pool; }
    public Button getCurrent() { return current; }
  }
  
  public static class TargetParam {
    private int increment;
    private Button button;
    public TargetParam(int increment, Button button) {
      this.increment = increment;
      this.button = button;
    }
    public int getIncrement() { return increment; }
    public Button getButton() { return button; }
  }
  
  private CurrentDicePile[] dice;
  private SpecialSpinner specialSpinner;
  private Target target;
  
  private TextView answerFraction;
  private Button answerProbability;
  private Check check;
  private GraphView.Setter graphSetter;
  
  // every time the dice are changed; this is incremented
  private long serial = 0;
  // true if the background distribution calculation job is running
  private boolean running = false;

  // data members below here are thread-safe
  
  private final DecimalFormat answerFormatter;
  private final String answerHighest;
  private final String answerSecondHighest;
  private final int maxFractionChars;
  
  private static final String APPROXIMATELY_EQUAL_TO = "\u2245";
  
  public DiceSet(
      ViewGroup poolViewGroup,
      ViewGroup currentViewGroup,
      DieType[] dieType,
      SpecialSpinner specialSpinner,
      TargetParam[] targetParam,
      Button targetButton,
      Button clear,
      TextView answerFraction,
      Button answerProbability,
      GraphView.Setter graphSetter
      ) {
    
    this.answerFormatter = new DecimalFormat(
        clear.getResources().getString(R.string.answer_format));
    this.answerHighest = answerFormatter.format(1.0d);
    this.answerSecondHighest = clear.getResources().getString(R.string.answer_second_highest);
    this.maxFractionChars = clear.getResources().getInteger(R.integer.max_fraction_chars);
    
    // associate each Button object with a behavioral object
    CurrentDiceChanged diceChanged = new CurrentDiceChanged();
    dice = new CurrentDicePile[dieType.length];
    int i = 0;
    for (DieType dt : dieType) {
      dice[i] = CurrentDicePile.create(dt.getSides(), dt.getCurrent(), diceChanged);
      new PoolDicePile(dt.getPool(), dice[i]);
      ++i;
    }
    
    target = new Target(targetButton, diceChanged);
    for (TargetParam tp : targetParam) {
      new TargetPool(tp.getIncrement(), tp.getButton(), target);
    }
    
    assert(target != null);

    this.specialSpinner = specialSpinner;
    specialSpinner.setChangeListener(diceChanged);
    this.answerFraction = answerFraction;
    this.answerProbability = answerProbability;
    this.check = new Check(answerProbability);
    this.graphSetter = graphSetter;

    final View.OnClickListener clearListener = new Clear();
    clear.setOnClickListener(clearListener);
    // explicit clear is necessary to set current constant to GONE
    clearListener.onClick(clear);
    
    // iss13: have the pool gridview and the current linearlayout "eat" clicks so that the user doesn't accidentally
    // select the background graph
    final View.OnClickListener doNothing = new View.OnClickListener(){
      public void onClick(View v) {}
    };
    poolViewGroup.setOnClickListener(doNothing);
    currentViewGroup.setOnClickListener(doNothing);
  }

  /**
   * Copies all values from the specified "other" dice set
   */
  public void copyFrom(DiceSet that) {
    for (int i=0 ; i<dice.length ; ++i) {
      dice[i].setCount(that.dice[i].getCount());
      // special case to not display constant of zero
      if (dice[i].getSides()==1 && dice[i].getCount()==0) {
        dice[i].clear();
      }
    }
    target.setCount(that.target.getCount());
    specialSpinner.setSelectedItemPosition(that.specialSpinner.getSelectedItemPosition());
  }
  
  /**
   * <p>Resets current dice and target to their default values.
   */
  private class Clear implements View.OnClickListener {
    @Override
    public void onClick(View v) {
      for (CurrentDicePile c : dice) {
        c.clear();
      }
      target.clear();
      specialSpinner.setSelectedItemPosition(0);
    }    
  }

  public void saveInstanceState(Bundle savedInstanceState, String prefix) {
    for (CurrentDicePile c : dice) {
      savedInstanceState.putInt(prefix+"d"+c.getSides(), c.getCount());
    }
    savedInstanceState.putInt(prefix+"target", target.getCount());
    savedInstanceState.putInt(prefix+"spinner", specialSpinner.getSelectedItemPosition());
  }
  
  public void restoreInstanceState(Bundle savedInstanceState, String prefix) {
    for (CurrentDicePile c : dice) {
      c.setCount(savedInstanceState.getInt(prefix+"d"+c.getSides()));
      // special case to not display constant of zero
      if (c.getSides()==1 && c.getCount()==0) {
        c.clear();
      }
    }
    target.setCount(savedInstanceState.getInt(prefix+"target"));
    specialSpinner.setSelectedItemPosition(savedInstanceState.getInt(prefix+"spinner"));
  }

  /**
   * <p>When any of the dice have changed, note the change by incrementing
   * serial and start the background job.
   */
  private class CurrentDiceChanged implements View.OnClickListener {
    @Override
    public void onClick(View v) {
      ++serial;
      startBackgroundJob();
    }
  }

  /**
   * <p>Just a payload into the background calculation job.
   * The background job cannot only write to these values.
   */
  private static class BackgroundIn {
    public long serial;
    // current dice: number of sides, and number of dice pairs
    public SparseIntArray sidesToCount = new SparseIntArray();
    public int target;
    public Special special;
  }
  
  /**
   * <p>Just a payload out of the background calculation job.
   * Used both to set the answer values as well as to update
   * the background graph.
   */
  private static class BackgroundOut {
    public long serial;
    public Distribution distribution;
    public int target;
    public String answerFraction;
    public String answerProbability;
    public String answerFormula;
  }
  
  /**
   * Attempts to start a new background distribution calculation. If a calculation is already 
   * running then let it run, it will check that the data has changed and start 
   * another Job.
   */
  private void startBackgroundJob() {
    if (!running) {
      running = true;
      BackgroundIn in = new BackgroundIn();
      in.serial = serial;
      for (CurrentDicePile c : dice) {
        in.sidesToCount.put(c.getSides(),c.getCount());
      }
      in.target = target.getCount();
      in.special = specialSpinner.getSelected();
      
      answerFraction.setText("");
      answerProbability.setText("?");
      
      new BackgroundJob().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, in);
    }
  }
  
  private class BackgroundJob extends AsyncTask<BackgroundIn, Void, BackgroundOut> {

    /**
     * Calculate the full distribution of the current dice. This thread is run in the background so it
     * shouldn't access any other non-threadsafe objects in DiceSet or call android gui methods.
     * The only threadsafe methods in DiceSet are those that are initialized in the constructor
     * and never changed such as loaded resource values. 
     */
    @Override
    protected BackgroundOut doInBackground(BackgroundIn... arg0) {
      BackgroundIn in = arg0[0];
      
      // calculate the distribution
      Distribution d = in.special.getDistribution(in.sidesToCount);
      // no modification to d after this; cache the cumulative values
      d = d.cacheCumulative();
      
      BackgroundOut out = new BackgroundOut();
      out.serial = in.serial;
      out.distribution = d;
      out.target = in.target;
      
      if (d.isZero()) {
        // if distribution is trivial then show minimal text
        out.answerFraction = "";
        out.answerProbability = answerFormatter.format(0.0d);
      } else {
        final BigFraction f = d.getCumulativeProbability(in.target);
        
        String fraction = f.toString();
        if (fraction.length() > maxFractionChars) {
          fraction = "! / !";
        }
        out.answerFraction = " " + APPROXIMATELY_EQUAL_TO + " " + fraction;
        
        out.answerProbability = answerFormatter.format(f.doubleValue());
        // don't round to 100% ; only show 100% if the probability is exactly 1.0
        if (out.answerProbability.equals(answerHighest) && !f.equals(BigFraction.ONE)) {
          out.answerProbability = answerSecondHighest;
        }
      }
      
      // format the textual answer of the distribution at the target
      out.answerFormula = in.special.getFormula(in.sidesToCount, in.target, out.answerProbability);
      
      return out;
    }
    
    /**
     * <p>Run from the GUI thread.  Safe to call GUI methods and access 
     * and data members in DiceSet.
     */
    @Override
    protected void onPostExecute(BackgroundOut out) {
      running = false;
      if (out.serial == serial) {
        answerFraction.setText(out.answerFraction);
        answerProbability.setText(out.answerProbability);
        check.set(out.answerProbability, out.distribution, out.target);
        graphSetter.setResult(out.distribution, out.target, out.answerFormula);
      } else {
        // the dice have changed since we started the background task
        // run the calculation again
        startBackgroundJob();
      }
    }
  }
}




Java Source Code List

com.asolutions.widget.RowLayout.java
org.kleemann.diceprobabilities.Check.java
org.kleemann.diceprobabilities.ConstantCurrentDicePile.java
org.kleemann.diceprobabilities.CurrentDicePile.java
org.kleemann.diceprobabilities.DiceSet.java
org.kleemann.diceprobabilities.MainActivity.java
org.kleemann.diceprobabilities.PoolDicePile.java
org.kleemann.diceprobabilities.TargetPool.java
org.kleemann.diceprobabilities.Target.java
org.kleemann.diceprobabilities.distribution.AbstractDistribution.java
org.kleemann.diceprobabilities.distribution.CachedCumulativeDistribution.java
org.kleemann.diceprobabilities.distribution.ConstantDistribution.java
org.kleemann.diceprobabilities.distribution.CritDistribution.java
org.kleemann.diceprobabilities.distribution.DeathZoneDieDistribution.java
org.kleemann.diceprobabilities.distribution.DieDistribution.java
org.kleemann.diceprobabilities.distribution.Distribution.java
org.kleemann.diceprobabilities.distribution.DogslicerDistribution.java
org.kleemann.diceprobabilities.distribution.ScaleCumulativeDistribution.java
org.kleemann.diceprobabilities.distribution.SumDistribution.java
org.kleemann.diceprobabilities.graph.GraphView.java
org.kleemann.diceprobabilities.graph.Interpolate.java
org.kleemann.diceprobabilities.graph.Point.java
org.kleemann.diceprobabilities.graph.Vector.java
org.kleemann.diceprobabilities.special.AbstractSpecial.java
org.kleemann.diceprobabilities.special.CritSpecial.java
org.kleemann.diceprobabilities.special.DeathZoneSpecial.java
org.kleemann.diceprobabilities.special.DogslicerSpecial.java
org.kleemann.diceprobabilities.special.FailureSpecial.java
org.kleemann.diceprobabilities.special.ForcedRerollSpecial.java
org.kleemann.diceprobabilities.special.ModifyEachDieSpecial.java
org.kleemann.diceprobabilities.special.NormalSpecial.java
org.kleemann.diceprobabilities.special.SecondChanceSpecial.java
org.kleemann.diceprobabilities.special.SpecialSpinner.java
org.kleemann.diceprobabilities.special.Special.java