package games.strategy.triplea.oddsCalculator.ta;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.UnitType;
import games.strategy.triplea.delegate.BattleCalculator;
import games.strategy.util.IntegerMap;
import games.strategy.util.Tuple;
public class AggregateResults implements Serializable {
private static final long serialVersionUID = -556699626060414738L;
// can be empty!
private final List<BattleResults> m_results;
private long m_time;
public AggregateResults(final int expectedCount) {
m_results = new ArrayList<>(expectedCount);
}
public void addResult(final BattleResults result) {
m_results.add(result);
}
public void addResults(final Collection<BattleResults> results) {
m_results.addAll(results);
}
public List<BattleResults> getResults() {
return m_results;
}
/**
* This could be null if we have zero results.
*/
public BattleResults getBattleResultsClosestToAverage() {
double closestBattleDif = Integer.MAX_VALUE;
BattleResults closestBattle = null;
for (final BattleResults results : m_results) {
double dif = Math.abs(results.getAttackingCombatUnitsLeft() - getAverageAttackingUnitsLeft());
dif += Math.abs(results.getDefendingCombatUnitsLeft() - getAverageDefendingUnitsLeft());
if (dif < closestBattleDif) {
closestBattleDif = dif;
closestBattle = results;
}
}
// can be null!
return closestBattle;
}
public List<Unit> getAverageAttackingUnitsRemaining() {
// can be null!
final BattleResults results = getBattleResultsClosestToAverage();
return results == null ? new ArrayList<>() : results.getRemainingAttackingUnits();
}
public List<Unit> getAverageDefendingUnitsRemaining() {
// can be null!
final BattleResults results = getBattleResultsClosestToAverage();
return results == null ? new ArrayList<>() : results.getRemainingDefendingUnits();
}
public double getAverageAttackingUnitsLeft() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
for (final BattleResults result : m_results) {
count += result.getAttackingCombatUnitsLeft();
}
return count / m_results.size();
}
/**
* First is Attacker, Second is Defender.
*/
public Tuple<Double, Double> getAverageTUVofUnitsLeftOver(final IntegerMap<UnitType> attackerCostsForTUV,
final IntegerMap<UnitType> defenderCostsForTUV) {
if (m_results.isEmpty()) { // can be empty!
return Tuple.of(0.0, 0.0);
}
double attackerTUV = 0;
double defenderTUV = 0;
for (final BattleResults result : m_results) {
attackerTUV += BattleCalculator.getTUV(result.getRemainingAttackingUnits(), attackerCostsForTUV);
defenderTUV += BattleCalculator.getTUV(result.getRemainingDefendingUnits(), defenderCostsForTUV);
}
return Tuple.of(attackerTUV / m_results.size(), defenderTUV / m_results.size());
}
public double getAverageTUVswing(final PlayerID attacker, final Collection<Unit> attackers, final PlayerID defender,
final Collection<Unit> defenders, final GameData data) {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
final IntegerMap<UnitType> attackerCostsForTUV = BattleCalculator.getCostsForTUV(attacker, data);
final IntegerMap<UnitType> defenderCostsForTUV = BattleCalculator.getCostsForTUV(defender, data);
final int attackerTotalTUV = BattleCalculator.getTUV(attackers, attackerCostsForTUV);
final int defenderTotalTUV = BattleCalculator.getTUV(defenders, defenderCostsForTUV);
// could we possibly cause a bug by comparing UnitType's from one game data, to a different game data's UnitTypes?
final Tuple<Double, Double> average = getAverageTUVofUnitsLeftOver(attackerCostsForTUV, defenderCostsForTUV);
final double attackerLost = attackerTotalTUV - average.getFirst();
final double defenderLost = defenderTotalTUV - average.getSecond();
return defenderLost - attackerLost;
}
public double getAverageAttackingUnitsLeftWhenAttackerWon() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
double total = 0;
for (final BattleResults result : m_results) {
if (result.attackerWon()) {
count += result.getAttackingCombatUnitsLeft();
total += 1;
}
}
if (total <= 0) {
return 0;
}
return count / total;
}
public double getAverageDefendingUnitsLeft() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
for (final BattleResults result : m_results) {
count += result.getDefendingCombatUnitsLeft();
}
return count / m_results.size();
}
public double getAverageDefendingUnitsLeftWhenDefenderWon() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
double total = 0;
for (final BattleResults result : m_results) {
if (result.defenderWon()) {
count += result.getDefendingCombatUnitsLeft();
total += 1;
}
}
if (total <= 0) {
return 0;
}
return count / total;
}
public double getAttackerWinPercent() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
for (final BattleResults result : m_results) {
if (result.attackerWon()) {
count++;
}
}
return count / m_results.size();
}
public double getDefenderWinPercent() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
for (final BattleResults result : m_results) {
if (result.defenderWon()) {
count++;
}
}
return count / m_results.size();
}
public double getAverageBattleRoundsFought() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
for (final BattleResults result : m_results) {
count += result.getBattleRoundsFought();
}
if (count == 0) {
// If this is a 'fake' aggregate result, return 1.0
return 1.0;
}
return count / m_results.size();
}
public double getDrawPercent() {
if (m_results.isEmpty()) { // can be empty!
return 0.0;
}
double count = 0;
for (final BattleResults result : m_results) {
if (result.draw()) {
count++;
}
}
return count / m_results.size();
}
public int getRollCount() {
return m_results.size();
}
public long getTime() {
return m_time;
}
public void setTime(final long time) {
m_time = time;
}
}