package games.strategy.engine.random;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import games.strategy.engine.data.PlayerID;
import games.strategy.util.IntegerMap;
public class RandomStatsDetails implements Serializable {
private static final long serialVersionUID = 69602197220912520L;
private final Map<PlayerID, IntegerMap<Integer>> m_data;
private final IntegerMap<Integer> m_totalMap;
private final DiceStatistic m_totalStats;
private final Map<PlayerID, DiceStatistic> m_playerStats = new HashMap<>();
public RandomStatsDetails(final Map<PlayerID, IntegerMap<Integer>> randomStats, final int diceSides) {
m_data = randomStats;
m_totalMap = new IntegerMap<>();
for (final Entry<PlayerID, IntegerMap<Integer>> entry : m_data.entrySet()) {
m_totalMap.add(entry.getValue());
}
m_totalStats = getDiceStatistic(m_totalMap, diceSides);
for (final Entry<PlayerID, IntegerMap<Integer>> entry : m_data.entrySet()) {
m_playerStats.put(entry.getKey(), getDiceStatistic(entry.getValue(), diceSides));
}
}
private static DiceStatistic getDiceStatistic(final IntegerMap<Integer> stats, final int diceSides) {
final double m_average;
final int m_total;
final double m_median;
final double m_stdDeviation;
final double m_variance;
if (stats.totalValues() != 0) {
int sumTotal = 0;
int total = 0;
// TODO: does this need to be updated to take data.getDiceSides() ?
for (int i = 1; i <= diceSides; i++) {
sumTotal += i * stats.getInt(Integer.valueOf(i));
total += stats.getInt(Integer.valueOf(i));
}
m_total = total;
m_average = (sumTotal) / ((double) stats.totalValues());
/**
* calculate median
*/
if (total % 2 != 0) {
m_median = calcMedian((total / 2) + 1, diceSides, stats);
} else {
double tmp1 = 0;
double tmp2 = 0;
tmp1 = calcMedian((total / 2), diceSides, stats);
tmp2 = calcMedian((total / 2) + 1, diceSides, stats);
m_median = (tmp1 + tmp2) / 2;
}
// calculate variance
double variance = 0;
// TODO: does this need to be updated to take data.getDiceSides() ?
for (int i = 1; i <= diceSides; i++) {
variance += (stats.getInt(Integer.valueOf(i)) - (total / diceSides))
* (stats.getInt(Integer.valueOf(i)) - (total / diceSides));
}
m_variance = variance / (total - 1);
/**
* calculate standard deviation
*/
m_stdDeviation = Math.sqrt(m_variance);
} else {
m_average = 0;
m_total = 0;
m_median = 0;
m_stdDeviation = 0;
m_variance = 0;
}
return new DiceStatistic(m_average, m_total, m_median, m_stdDeviation, m_variance);
}
public Map<PlayerID, IntegerMap<Integer>> getData() {
return m_data;
}
public IntegerMap<Integer> getTotalData() {
return m_totalMap;
}
public Map<PlayerID, DiceStatistic> getPlayerStats() {
return m_playerStats;
}
public DiceStatistic getTotalStats() {
return m_totalStats;
}
private static int calcMedian(final int centerPoint, final int diceSides, final IntegerMap<Integer> stats) {
int sum = 0;
int i = 1;
for (i = 1; i <= diceSides; i++) {
sum += stats.getInt(Integer.valueOf(i));
if (sum >= centerPoint) {
return i;
}
}
// This is to stop java from complaining
return i;
// it should never reach this part.
}
private static String getStatsString(final IntegerMap<Integer> diceRolls, final DiceStatistic diceStats,
final String title, final String indentation) {
final StringBuilder sb = new StringBuilder();
sb.append(indentation).append(title).append("\n");
for (final int key : new TreeSet<>(diceRolls.keySet())) {
final int value = diceRolls.getInt(key);
sb.append(indentation).append(indentation).append(indentation).append(key).append(" was rolled ").append(value)
.append(" times").append("\n");
}
final DecimalFormat format = new DecimalFormat("#0.000");
sb.append(indentation).append(indentation).append("Average roll : ").append(format.format(diceStats.getAverage()))
.append("\n");
sb.append(indentation).append(indentation).append("Median : ").append(format.format(diceStats.getMedian()))
.append("\n");
sb.append(indentation).append(indentation).append("Variance : ").append(format.format(diceStats.getVariance()))
.append("\n");
sb.append(indentation).append(indentation).append("Standard Deviation : ")
.append(format.format(diceStats.getStdDeviation())).append("\n");
sb.append(indentation).append(indentation).append("Total rolls : ").append(diceStats.getTotal()).append("\n");
return sb.toString();
}
public static String getAllStatsString(final RandomStatsDetails details, final String indentation) {
if (details.getTotalStats().getTotal() <= 0) {
return "";
}
final StringBuilder sb = new StringBuilder();
sb.append("Dice Statistics:\n\n");
sb.append(getStatsString(details.getTotalData(), details.getTotalStats(), "Total", indentation));
if (details.getData().containsKey(null)) {
sb.append("\n");
sb.append(
getStatsString(details.getData().get(null), details.getPlayerStats().get(null), "Null / Other", indentation));
}
for (final Entry<PlayerID, IntegerMap<Integer>> entry : details.getData().entrySet()) {
if (entry.getKey() == null) {
continue;
}
sb.append("\n");
sb.append(getStatsString(entry.getValue(), details.getPlayerStats().get(entry.getKey()),
(entry.getKey() == null ? "Null / Other" : entry.getKey().getName() + " Combat"), indentation));
}
return sb.toString();
}
public String getAllStatsString(final String indentation) {
return getAllStatsString(this, indentation);
}
private static JPanel getStatsDisplay(final IntegerMap<Integer> diceRolls, final DiceStatistic diceStats,
final String title) {
final JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEtchedBorder());
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(new JLabel("<html><b>" + title + "</b></html>"));
for (final int key : new TreeSet<>(diceRolls.keySet())) {
final int value = diceRolls.getInt(key);
final JLabel label = new JLabel(key + " was rolled " + value + " times");
panel.add(label);
}
panel.add(new JLabel(" "));
final DecimalFormat format = new DecimalFormat("#0.000");
panel.add(new JLabel("Average roll : " + format.format(diceStats.getAverage())));
panel.add(new JLabel("Median : " + format.format(diceStats.getMedian())));
panel.add(new JLabel("Variance : " + format.format(diceStats.getVariance())));
panel.add(new JLabel("Standard Deviation : " + format.format(diceStats.getStdDeviation())));
panel.add(new JLabel("Total rolls : " + diceStats.getTotal()));
return panel;
}
public static JPanel getAllStats(final RandomStatsDetails details) {
final Insets insets = new Insets(2, 2, 2, 2);
final JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
panel.setBorder(BorderFactory.createEmptyBorder());
panel.add(getStatsDisplay(details.getTotalData(), details.getTotalStats(), "Total"), new GridBagConstraints(0, 0, 1,
1, 1, 1, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE, insets, 0, 0));
if (details.getData().containsKey(null)) {
panel.add(getStatsDisplay(details.getData().get(null), details.getPlayerStats().get(null), "Null / Other"),
new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE, insets,
0, 0));
}
final int rows = Math.max(2, details.getData().size() / 6);
int x = 0;
for (final Entry<PlayerID, IntegerMap<Integer>> entry : details.getData().entrySet()) {
if (entry.getKey() == null) {
continue;
}
panel.add(
getStatsDisplay(entry.getValue(), details.getPlayerStats().get(entry.getKey()),
(entry.getKey() == null ? "Null / Other" : entry.getKey().getName() + " Combat")),
new GridBagConstraints((x / rows), 1 + (x % rows), 1, 1, 1, 1, GridBagConstraints.FIRST_LINE_START,
GridBagConstraints.NONE, insets, 0, 0));
x++;
}
return panel;
}
public JPanel getAllStats() {
return getAllStats(this);
}
}