/** Calculates the Food Made+Used data sets. */ private void calculateFoodMadeUsedDataSets() { // Food Made+Used is showing 2 player stats at once: Food Made and Food Used // First calculate FOOD_USED data set... calculateDataSets(Stat.FOOD_USED); // ...and clone it because we'll need it in a moment final List<Chart<LineChartDataSet>> clonedChartList = new ArrayList<>(chartList.size()); for (final Chart<LineChartDataSet> chart : chartList) { final Chart<LineChartDataSet> clonedChart = createChart(); for (final DataModel<LineChartDataSet> model : chart.getDataModelList()) { final DataModel<LineChartDataSet> clonedModel = new DataModel<>(model.getTitle(), model.getColor()); for (final LineChartDataSet dataSet : model.getDataSetList()) { // Use brighter color as this will be the 2nd dataset final Color color = ((User) model.getUserobObject()).getPlayerColor().brighterColor; clonedModel.addDataSet( new LineChartDataSet( color, dataSet.getStroke(), dataSet.getLoops(), dataSet.getValues())); } clonedChart.addModel(clonedModel); } clonedChartList.add(clonedChart); } // Cloning done, let's roll on to calculating the FOOD_MADE data set... calculateDataSets(Stat.FOOD_MADE); // And the final step: "merge" the 2 data sets into one, simply add the datasets... for (int chartIdx = 0; chartIdx < chartList.size(); chartIdx++) { final Chart<LineChartDataSet> chart = chartList.get(chartIdx); final Chart<LineChartDataSet> clonedChart = clonedChartList.get(chartIdx); for (int modelIdx = 0; modelIdx < chart.getDataModelList().size(); modelIdx++) { final DataModel<LineChartDataSet> model = chart.getDataModelList().get(modelIdx); final DataModel<LineChartDataSet> clonedModel = clonedChart.getDataModelList().get(modelIdx); model.getDataSetList().addAll(clonedModel.getDataSetList()); } } }
/** * Calculates the data sets for the specified {@link Stat}. * * @param stat stat to calculate the data sets for */ private void calculateDataSets(final Stat stat) { // Data points for each data set, inside being mapped from loop to value. final Map<LineChartDataSet, Map<Integer, Int>> dataSetPointsMap = new HashMap<>(); final boolean mergeMinGas = stat.hasMinGas && !separateMinsGasCheckBox.isSelected(); final String[] eventFields; // Event fields to query eventually if (stat.hasMinGas) { final boolean includeMins = minsCheckBox.isSelected(); final boolean includeGas = gasCheckBox.isSelected(); if (includeMins && includeGas) eventFields = stat.eventFields; else if (includeMins) eventFields = new String[] {stat.eventFields[0]}; else if (includeGas) eventFields = new String[] {stat.eventFields[1]}; else eventFields = new String[0]; } else eventFields = stat.eventFields; // Calculate data points maps for (final Event event : repProc.replay.trackerEvents.events) { if (event.id != ITrackerEvents.ID_PLAYER_STATS) continue; final DataModel<LineChartDataSet> model = modelByPlayerIds[event.getPlayerId()]; if (model == null) continue; // One extra player stat event is recorded when a user leaves (at the same loop) even if at // the same loop there was a // scheduled player stat event. So we have to exclude the last player stat event (to avoid the // chart getting // "screwed"). (This for example contains mineralsCurrent = 0.) // Note: if there's a scheduled stat event when the player left, this will be excluded // wrongly, // but I don't care, it won't make any noticable difference... if (event.loop == repProc.usersByPlayerId[event.getPlayerId()].leaveLoop) continue; // Data sets per event fields for (int i = 0; i < eventFields.length; i++) { final String field = eventFields[i]; final LineChartDataSet dataSet = model.getDataSetList().get(mergeMinGas ? 0 : i); Map<Integer, Int> pointsMap = dataSetPointsMap.get(dataSet); if (pointsMap == null) dataSetPointsMap.put(dataSet, pointsMap = new HashMap<>()); final Integer loop = Integer.valueOf(event.loop); Int point = pointsMap.get(loop); if (point == null) { pointsMap.put(loop, point = new Int()); } int value = ((PlayerStatsEvent) event).stats.<Integer>get(field); if (field == PlayerStatsEvent.F_FOOD_MADE || field == PlayerStatsEvent.F_FOOD_USED) value >>= 12; point.value += value; } } // Convert data points maps to arrays for (final Chart<LineChartDataSet> chart : chartList) { for (final DataModel<LineChartDataSet> model : chart.getDataModelList()) { for (final LineChartDataSet dataSet : model.getDataSetList()) { final Map<Integer, Int> pointsMap = dataSetPointsMap.get(dataSet); if (pointsMap == null) continue; // Data points are needed sorted by loop final List<Entry<Integer, Int>> dataPointList = new ArrayList<>(pointsMap.entrySet()); Collections.sort( dataPointList, new Comparator<Entry<Integer, Int>>() { @Override public int compare(Entry<Integer, Int> o1, Entry<Integer, Int> o2) { return o1.getKey().compareTo(o2.getKey()); } }); int i = 0; final int[] loops = new int[pointsMap.size()]; final int[] values = new int[pointsMap.size()]; for (final Entry<Integer, Int> dataPoint : dataPointList) { loops[i] = dataPoint.getKey(); values[i] = dataPoint.getValue().value; i++; } dataSet.setLoops(loops); dataSet.setValues(values); dataSet.calculateValueMax(); } } } }
/** Calculates the Spending Quotient data sets. */ private void calculateSQDataSets() { // Spending Quotient is a function of 2 other Stats: SQ = f( RES_CURRENT, RES_COLL_RATE ) // First calculate RES_CURRENT data set... calculateDataSets(Stat.RES_CURRENT); // ...and clone it because we'll need it in a moment final List<Chart<LineChartDataSet>> clonedChartList = new ArrayList<>(chartList.size()); for (final Chart<LineChartDataSet> chart : chartList) { final Chart<LineChartDataSet> clonedChart = createChart(); for (final DataModel<LineChartDataSet> model : chart.getDataModelList()) { final DataModel<LineChartDataSet> clonedModel = new DataModel<>(null, null); for (final LineChartDataSet dataSet : model.getDataSetList()) clonedModel.addDataSet( new LineChartDataSet(null, null, dataSet.getLoops(), dataSet.getValues())); clonedChart.addModel(clonedModel); } clonedChartList.add(clonedChart); } // Cloning done, let's roll on to calculating the RES_COLL_RATE data set... calculateDataSets(Stat.RES_COLL_RATE); // We'll need the last command game event loops indexed by data model (last cmd loop is the same // for all data sets in a // model) final Map<DataModel<LineChartDataSet>, Int> modelLastCmdLoopsMap = new HashMap<>(); for (final Event event : repProc.replay.gameEvents.events) { if (event.id != IGameEvents.ID_CMD) continue; // Player stats go by tracker events which go by player id. First check if there is player for // the user! final int playerId = repProc.usersByUserId[event.userId].playerId; if (playerId <= 0) continue; final DataModel<LineChartDataSet> model = modelByPlayerIds[playerId]; if (model == null) continue; // Update last command loop per model Int lastCmdLoop = modelLastCmdLoopsMap.get(model); if (lastCmdLoop == null) modelLastCmdLoopsMap.put(model, lastCmdLoop = new Int()); lastCmdLoop.value = event.loop; } // And the final step: "merge" the 2 data sets, the result being the SQ for (int chartIdx = 0; chartIdx < chartList.size(); chartIdx++) { final Chart<LineChartDataSet> chart = chartList.get(chartIdx); final Chart<LineChartDataSet> clonedChart = clonedChartList.get(chartIdx); for (int modelIdx = 0; modelIdx < chart.getDataModelList().size(); modelIdx++) { final DataModel<LineChartDataSet> model = chart.getDataModelList().get(modelIdx); final DataModel<LineChartDataSet> clonedModel = clonedChart.getDataModelList().get(modelIdx); final int lastCmdLoop = modelLastCmdLoopsMap.get(model) == null ? 0 : modelLastCmdLoopsMap.get(model).value; for (int dataSetIdx = 0; dataSetIdx < model.getDataSetList().size(); dataSetIdx++) { final LineChartDataSet dataSet = model.getDataSetList().get(dataSetIdx); final LineChartDataSet clonedDataSet = clonedModel.getDataSetList().get(dataSetIdx); // Only include values in total before the last command loop! And of course only count // samples up to that. final int[] loops = dataSet.getLoops(); final int[] values = dataSet.getValues(); final int[] clonedValues = clonedDataSet.getValues(); long totalValues = 0, totalClonedValues = 0; int totalCount = 0; for (int valueIdx = 0; valueIdx < values.length; valueIdx++) { if (loops[valueIdx] <= lastCmdLoop) { totalClonedValues += clonedValues[valueIdx]; totalValues += values[valueIdx]; totalCount++; } values[valueIdx] = RepProcessor.calculateSQImpl(clonedValues[valueIdx], values[valueIdx]); } // Calculate average SQ for the data set, put in data set title! final int avgSQ = totalCount == 0 ? 0 : RepProcessor.calculateSQImpl( (int) (totalClonedValues / totalCount), (int) (totalValues / totalCount)); dataSet.setTitle("SQ: " + avgSQ); dataSet.calculateValueMax(); } } } }