/*
* DTGameManager.java
*
* Created on 17 May 2006, 10:33
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package ffg.game.vs;
import static ffg.util.FFGConstants.*;
import ca.odell.glazedlists.swing.TableComparatorChooser;
import ffg.entity.AFLPlayer;
import ffg.entity.AFLPosition;
import ffg.entity.AFLPositions;
import ffg.entity.AFLTeam;
import ffg.game.FFGPanelException;
import ffg.game.GameManager;
import ffg.game.UpdateScoresException;
import ffg.gui.SaveException;
import ffg.gui.table.column.FFGTableColumn;
import ffg.gui.table.filter.FFGTableFilter;
import ffg.gui.table.filter.FilterKey;
import ffg.gui.table.filter.FilterOperator;
import ffg.gui.table.filter.FilterStringTranslator;
import ffg.gui.table.filter.MatchMode;
import ffg.gui.table.renderer.AlternateRowTableCellRenderer;
import ffg.gui.table.renderer.FFGTableCellRenderer;
import ffg.util.BareBonesBrowserLaunch;
import ffg.util.FFGConstants;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import javax.swing.Icon;
import javax.swing.JOptionPane;
import javax.swing.JTable;
/**
* A {@link GameManager} that is the base class of all VS-based competitions.
* @param <E> The subclass of {@link VSGameStats}
* @author eugene
*/
public abstract class VSGameManager<E extends VSGameStats> extends GameManager<E> implements PropertyChangeListener {
public static final AFLPosition[] GAME_POSITIONS = new AFLPosition[] {
AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,AFLPosition.Back,
AFLPosition.Centre,AFLPosition.Centre,AFLPosition.Centre,AFLPosition.Centre,AFLPosition.Centre,AFLPosition.Centre,AFLPosition.Centre,AFLPosition.Centre,
AFLPosition.Ruck,AFLPosition.Ruck,AFLPosition.Ruck,AFLPosition.Ruck,
AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward,AFLPosition.Forward
};
// /**
// * The number of rounds in the dream team competition.
// */
// public static final int N_ROUNDS = 22;
/**
* STATS_FILE_PROPERTY
*/
public static final String STATS_FILE_PROPERTY = "Stats File";
/**
* ROUNDS_COMPLETED_PROPERTY
*/
public static final String ROUNDS_COMPLETED_PROPERTY = "Rounds Completed";
/**
* PLAYER_EDITING_PROPERTY
*/
public static final String PLAYER_EDITING_PROPERTY = "Player Being Edited";
/**
* NUMBER_EDITED_PROPERTY
*/
public static final String NUMBER_EDITED_PROPERTY = "Number Edited";
/**
* UPDATE_STRING_PROPERTY
*/
public static final String UPDATE_STRING_PROPERTY = "Update String";
/**
* MULTIPLIER_PROPERTY
*/
public static final String MULTIPLIER_PROPERTY = "Multiplier";
// /**
// * PERCENTAGE_PROPERTY
// */
// public static final String PERCENTAGE_PROPERTY = "Percentage";
// Used to extract portions from the html.
// private static final String BEGIN_RESULTS_STRING = "<!--end brief results table -->";
// private static final String END_RESULTS_STRING = "<!--end expanded results table-->";
// private static final Pattern ROW_PATTERN = Pattern.compile(
// "<tr align=\"center\">\\s+"
// + "<td style=\"width:\\d\\%\" class=\"t\\d_Head\"><strong>(\\d+)</strong></td>\\s+"
// + "<td class=\"mtm_teamc\\d+\" align=\".*?\">\\$(.*?)\\ \\ .*?<span class=\"mtm_.*?text\">.*?</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\">(\\-?\\d+)</td>\\s+"
// + "<td style=\"width:\\d\\%\" class=\"mtm_teamc\\d+?\"><strong>(\\-?\\d+)</strong></td>\\s+"
// );
private static final Pattern ROW_PATTERN = Pattern.compile(
"<td style=\"width.*?\" class=\".*?\"><strong>(\\d+)</strong></td>\\s+"
+ "<td class=\".*?\">.*?</td>\\s+"
+ "<td class=\".*?\" align=\".*?\">\\$(.*?)\\ \\ .*?<span class=\".*?\">.*?</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\">(.*?)</td>\\s+"
+ "<td style=\".*?\" class=\".*?\"><strong>(.*?)</strong></td>\\s+"
);
private static final int ROW_ROUND = 1;
private static final int ROW_PRICE = 2;
private static final int ROW_KICKS = 3;
private static final int ROW_HANDBALLS = 4;
private static final int ROW_MARKS = 5;
private static final int ROW_TACKLES = 6;
private static final int ROW_FREESFOR = 7;
private static final int ROW_FREESAGAINST = 8;
private static final int ROW_HITOUTS = 9;
private static final int ROW_GOALS = 10;
private static final int ROW_BEHINDS = 11;
private static final int ROW_SCORE = 12;
private static final Pattern PLAYER_ROUND_PATTERN = Pattern.compile(
// "<td class=\"t\\dC\\d\" align=\"left\">(\\d+)\\.\\s+<a class=\".*?\" href=\"/\\?p=researchmini&pid=(\\d+)\"><strong>(.*?),</strong> (.*?)</a>\\s+.*?<strong>.*?\\$(.*?)</strong>.*?\\s+"
// + "\\s+<span class=\".*?\">.*?</span>\\s+"
// + "\\s+<br /><span class=\"txt_abrv\">.*?</td>\\s+"
// + "<td class=\"t\\dC\\d\">(\\d+)</td>\\s+"
// + "<td class=\"t\\dC\\d\">(.*?)</td>\\s+"
// + "<td class=\"t\\dC\\d\">(\\d+) \\(.*?\\)</td>\\s+"
// + "<td class=\"t\\dC\\d\">(.*?)</td>\\s+"
// + "<td class=\"t\\dC\\d\"><strong>([\\d\\-]+)</strong></td>\\s+"
// + "<td class=\"t\\dC\\d\"><strong>([\\d,]+)</strong></td>\\s+"
// + "<td class=\"t\\dC\\d\"><a href=\"\""
"<td class=\"t\\dC\\d\" align=\"left\">(\\d+)\\.\\s+<a class=\".*?\" href=\"/\\?p=researchmini&pid=(\\d+)\"><strong>(.*?),</strong> (.*?)</a>\\s+"
+ ".*?<strong>.*?\\$(.*?)</strong>.*?\\s+"
+ "\\s+<span class=\".*?\">.*?</span>\\s+"
+ "\\s+<br /><span class=\"txt_abrv\">.*?</td>\\s+"
+ "<td class=\"t\\dC\\d\">(\\d+)</td>\\s+"
+ "<td class=\"t\\dC\\d\">(.*?)</td>\\s+"
+ "<td class=\"t\\dC\\d\">(\\d+) \\(.*?\\)</td>\\s+"
+ "<td class=\"t\\dC\\d\">(.*?)</td>\\s+"
+ "<td class=\"t\\dC\\d\">(.*?)</td>\\s+"
+ "<td class=\"t\\dC\\d\"><strong>([\\d\\-]+)</strong></td>\\s+"
+ "<td class=\"t\\dC\\d\"><strong>([\\d,]+)</strong></td>\\s+"
+ "<td class=\"t\\dC\\d\"><a href=\"\""
);
// group numbers in the above regular expressions
private static final int RANK_GROUP = 1;
private static final int ORDINAL_GROUP = 2;
private static final int LAST_NAME_GROUP = 3;
private static final int FIRST_NAME_GROUP = 4;
private static final int CURRENT_PRICE_GROUP = 5;
private static final int ROUNDS_PLAYED_GROUP = 6;
private static final int CURRENT_SELECTIONS_GROUP = 7;
private static final int LAST_RANK_GROUP = 8;
private static final int MVP_GROUP = 9;
private static final int AVERAGE_SCORE_GROUP = 10;
private static final int ROUND_SCORE_GROUP = 11;
private static final int TOTAL_SCORE_GROUP = 12;
// private static final Pattern ROUND_NUMBER_PATTERN = Pattern.compile("<div class=\"gamestatus_roun\">(\\d+)</div>");
private static final Pattern SELECTIONS_PATTERN = Pattern.compile(
"Current competition ranking:</td>\\s+"
+ "<td.*?><strong>(.*?)</strong></td></tr>\\s+"
+ "<tr><td.*?>Total \\# of selections:</td>\\s+"
+ "<td.*?><strong>(.*?)</strong></td>"
);
private static final int RANKING_GROUP = 1;
private static final int SELECTIONS_GROUP = 2;
/**
* The renderer of this game manager.
*/
private FFGTableCellRenderer tableCellRenderer;
/**
* Flags whether a current update is occuring
*/
private final AtomicBoolean updating = new AtomicBoolean(false);
/**
* Which players are played in the latest round (i.e. active).
*/
private final Set<AFLPlayer> playersActive;
/**
* The price table associated with this game manager.
*/
private final VSPriceTable<E> priceTable;
/**
* The file containing the statistics for this game manager.
*/
private File statsFile;
/**
* Used in the property change listener.
*/
private int roundsCompleted = 0;
/**
* Variables in the VS formula.
* Multiplier is basically the average $/avg (over the past 3 rounds)
* Percentage is the proportion of the difference b/w the current price and the multiplier price to move to. It has a linear relationship to the multiplier.
*/
private double multiplier;
private double scale;
// private double percentage;
/**
* Filters, sorting and columns
*/
private MatchMode oldMatchMode = MatchMode.ALL;
private final List<FilterHolder> oldFilters = new ArrayList<FilterHolder>();
private final List<SortHolder> oldSort = new ArrayList<SortHolder>();
private final List<Integer> hiddenColumnIndices = new ArrayList<Integer>();
// for updating.
private static final int N_THREADS = 4;
private ExecutorService executor;
private final AtomicInteger processedPlayersCount = new AtomicInteger(0);
/**
* Value holder class for filters
*/
private static class FilterHolder {
private final int keyIndex;
private final int operatorIndex;
private final Object value;
public FilterHolder(int keyIndex, int operatorIndex, Object value) {
this.keyIndex = keyIndex;
this.operatorIndex = operatorIndex;
this.value = value;
}
public int getKeyIndex() {
return keyIndex;
}
public int getOperatorIndex() {
return operatorIndex;
}
public Object getValue() {
return value;
}
}
/**
* Value holder class for sorts
*/
private static class SortHolder {
private final int columnIndex;
private final int comparatorIndex;
private final boolean reverse;
public SortHolder(int columnIndex, int comparatorIndex, boolean reverse) {
this.columnIndex = columnIndex;
this.comparatorIndex = comparatorIndex;
this.reverse = reverse;
}
public int getColumnIndex() {
return columnIndex;
}
public int getComparatorIndex() {
return comparatorIndex;
}
public boolean isReverse() {
return reverse;
}
}
/**
* Creates a new instance of VSGameManager
*/
public VSGameManager() {
super();
setMultiplier(getVSGameType().getDefaultMultiplier());
// percentage = getVSGameType().getDefaultPercentage();
playersActive = new HashSet<AFLPlayer>();
for (AFLPlayer player : getVSGameType().getPlayers()) {
VSGameStats stats = getStatsForPlayer(player);
stats.addToPricesAt(0, getStartingValueForPlayer(player));
}
priceTable = new VSPriceTable<E>(this);
setSaved(false);
}
/**
* Creates a new instance of VSGameManager from the given <code>statsFile</code>
* @param statsFile The file containing the statistics.
* @throws FFGPanelException error occurs in reading the file.
*/
public VSGameManager(File statsFile) throws FFGPanelException {
super();
setMultiplier(getVSGameType().getDefaultMultiplier());
// percentage = getVSGameType().getDefaultPercentage();
playersActive = new HashSet<AFLPlayer>();
setStatsFile(statsFile);
loadStats();
priceTable = new VSPriceTable<E>(this);
restoreTableState();
setSaved(true);
}
public VSGameManager(VSGameManager<E> toCopy) {
super();
setMultiplier(getVSGameType().getDefaultMultiplier());
playersActive = new HashSet<AFLPlayer>();
// for (AFLPlayer player : getVSGameType().getPlayers()) {
// VSGameStats stats = getStatsForPlayer(player);
// stats.addToPricesAt(0, getStartingValueForPlayer(player));
// }
for (Entry<AFLPlayer, E> entry : toCopy.playerStats.entrySet()) {
E stats = entry.getValue();
}
priceTable = new VSPriceTable<E>(this);
setSaved(false);
}
public void downloadStats() throws UpdateScoresException {
Scanner roundScanner = null;
Scanner fileScanner = null;
try {
final String gameTypeName = getVSGameType().name();
String roundFile = String.format(ROUND_FILE_FORMAT, gameTypeName);
URL url = new URL(String.format(BASE_STATS_URL, YEAR, gameTypeName, roundFile));
roundScanner = new Scanner(url.openStream());
if (!roundScanner.hasNextLine()) {
throw new UpdateScoresException(String.format("Could not load %s from URL %s", roundFile, url.toString()), true);
}
int roundNumber = Integer.parseInt(roundScanner.nextLine());
if (roundNumber < getRoundsCompleted()) {
int result = JOptionPane.showConfirmDialog(null, String.format("The current file has stats up to round %d, but the latest file on the website has stats up to round %d.%nDo you wish to continue?", getRoundsCompleted(), roundNumber), "Confirm Download", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.NO_OPTION) {
return;
}
} else if (roundNumber == getRoundsCompleted()) {
int result = JOptionPane.showConfirmDialog(null, String.format("The current file and the latest file on the website has stats up to round %d.%nThey may contain the exact same stats.%n Do you wish to continue?", roundNumber), "Confirm Download", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.NO_OPTION) {
return;
}
}
String statsFileName = String.format("%s-R%d.ffz", gameTypeName, roundNumber);
URL fileURL = new URL(String.format(BASE_STATS_URL, YEAR, gameTypeName, statsFileName));
fileScanner = new Scanner(new GZIPInputStream(fileURL.openStream()));
fileScanner.useDelimiter(GET_ALL_STRINGS_PATTERN);
if (!fileScanner.hasNext()) {
throw new UpdateScoresException(String.format("Could not load %s from URL %s", statsFileName, fileURL.toString()), true);
}
// System.out.println("fileURL = '" + fileURL + "'");
String stats = fileScanner.next();
importStats(new StringReader(stats));
}
catch (IOException ioe) {
throw new UpdateScoresException(ioe, true);
}
finally {
if (roundScanner != null) {
roundScanner.close();
}
if (fileScanner != null) {
fileScanner.close();
}
}
}
/**
* Restores filters and sorting.
*/
private void restoreTableState() {
// hidden columns
if (!hiddenColumnIndices.isEmpty()) {
final List<FFGTableColumn<E, ?>> allColumns = priceTable.getAllTableColumns();
for (int index : hiddenColumnIndices) {
if (index < allColumns.size()) {
final int finalIndex = index;
EventQueue.invokeLater(new Runnable() {
// @Override
public void run() {
allColumns.get(finalIndex).setHidden(true);
}
});
}
}
hiddenColumnIndices.clear();
}
// filters
if (!oldFilters.isEmpty()) {
List<FilterKey<E, ?>> allFilterColumns = priceTable.getFilterTableColumns();
List<FFGTableFilter<E>> newFilters = new ArrayList<FFGTableFilter<E>>();
for (FilterHolder filterHolder : oldFilters) {
FilterKey<E, ?> key = allFilterColumns.get(filterHolder.getKeyIndex());
FilterOperator operator = key.getFilterOperators().get(filterHolder.getOperatorIndex());
newFilters.add(new FFGTableFilter<E>(key, operator, filterHolder.getValue()));
}
priceTable.setFilters(newFilters, oldMatchMode);
oldFilters.clear();
}
//sorting
if (!oldSort.isEmpty()) {
final TableComparatorChooser<E> chooser = priceTable.getTableComparatorChooser();
for (SortHolder sortHolder : oldSort) {
final SortHolder finalHolder = sortHolder;
EventQueue.invokeLater(new Runnable() {
// @Override
public void run() {
chooser.appendComparator(finalHolder.getColumnIndex(), finalHolder.getComparatorIndex(), finalHolder.isReverse());
}
});
}
oldSort.clear();
}
}
/**
* Abstract method for creating a sub class of {@link VSGameStats} for the particular comp.
* @param player The player for whom the stats are being created.
* @return <E> The subclass of {@link VSGameStats}
*/
protected abstract E createGameStatsSub(AFLPlayer player);
// /**
// * The number of rounds in this competition.
// * @return as above
// */
// protected abstract int getMaxRoundNumber();
@Override
/**
* Creates the subclass of {@link VSGameStats} and adds a property change listener to it.
*/
protected E createGameStats(AFLPlayer player) {
E stats = createGameStatsSub(player);
stats.addPropertyChangeListener(this);
return stats;
}
/**
* The renderer for the table.
* @return as above
*/
@Override
public FFGTableCellRenderer getBaseTableCellRenderer() {
if (tableCellRenderer == null) {
tableCellRenderer = new VSTableCellRenderer(this, getVSGameType().getEvenColor(), getVSGameType().getOddColor());
}
return tableCellRenderer;
}
/**
* The file name containing the statistics for this game manager
* @return as above
*/
public String getStatsFileName() {
return statsFile == null ? "Untitled" : statsFile.getName();
}
/**
* The file path containing the statistics for this game manager
* @return as above
*/
public String getStatsFileAbsoluteName() {
return statsFile == null ? "Untitled" : statsFile.getAbsolutePath();
}
/**
* The file object containing the statistics for this game manager
* @return as above
*/
public File getStatsFile() {
return statsFile;
}
/**
* Sets the file containing the statistics for this game manager
* @param newFile The stats file
*/
public void setStatsFile(File newFile) {
File oldFile = statsFile;
statsFile = newFile;
propChangeSupport.firePropertyChange(STATS_FILE_PROPERTY, oldFile, statsFile);
}
/**
* Loads the statistics from the current stats file
* @throws FFGPanelException if there were problems reading and parsing the statistics from the file.
*/
private void loadStats() throws FFGPanelException {
if (updating.compareAndSet(false, true)) {
Scanner scanner = null;
try {
scanner = new Scanner(statsFile);
// String year = scanner.nextLine();
String initialLine = scanner.nextLine();
String realFirstLine = initialLine;
try {
VSGameType.valueOf(initialLine);
realFirstLine = scanner.nextLine();
} catch (IllegalArgumentException iae) {
}
String[] firstLine = realFirstLine.split(",");
int roundNumber = Integer.parseInt(firstLine[0]);
if (firstLine.length > 1) {
setMultiplier(Double.parseDouble(firstLine[1]));
}
setRoundsCompleted(roundNumber);
int nPlayers = Integer.parseInt(scanner.nextLine());
scanner.nextLine(); // headings
while (nPlayers-- > 0) {
String line = scanner.nextLine();
Scanner lineScanner = new Scanner(line).useDelimiter(",");
int playerID = Integer.parseInt(lineScanner.next());
AFLPlayer curPlayer = AFLPlayer.getPlayerByOrdinal(playerID);
E stats = getStatsForPlayer(curPlayer);
//prices
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
String next = lineScanner.next();
if (i < roundNumber) {
stats.addToPricesAt(i, Integer.parseInt(next));
} else if (roundNumber == 0) {
stats.addToPricesAt(0, Integer.parseInt(next));
}
}
lineScanner.next(); // blank
//scores
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
String next = lineScanner.next();
if (i < roundNumber) {
stats.addToScoresAt(i, Integer.parseInt(next));
} else if (roundNumber == 0) {
stats.addToScoresAt(0, Integer.parseInt(next));
}
}
lineScanner.next(); // blank
//selections
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
String next = lineScanner.next();
if (i < roundNumber) {
stats.addToSelectionsAt(i, Integer.parseInt(next));
} else if (roundNumber == 0) {
stats.addToSelectionsAt(0, Integer.parseInt(next));
}
}
// lineScanner.next(); // blank
//
// //ranks
//// for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
// for (int i = 0; i < roundNumber; i++) {
// stats.addToRanksAt(i, Integer.parseInt(lineScanner.next()));
// }
}
// hidden columns
String[] indices = scanner.nextLine().split("\\,");
for (String index : indices) {
if (index.length() > 0) {
hiddenColumnIndices.add(Integer.parseInt(index));
}
}
// filters
int nFilters = Integer.parseInt(scanner.nextLine());
oldMatchMode = MatchMode.valueOf(scanner.nextLine());
while (nFilters-- > 0) {
String filterInfo = scanner.nextLine();
String[] values = filterInfo.split(TAB);
int filterKeyIndex = Integer.parseInt(values[0]);
int operatorIndex = Integer.parseInt(values[1]);
String value = values[2];
Object obj = FilterStringTranslator.fromString(value);
oldFilters.add(new FilterHolder(filterKeyIndex, operatorIndex, obj));
}
// sorting
int nSortColumns = Integer.parseInt(scanner.nextLine());
while (nSortColumns-- > 0) {
String sortInfo = scanner.nextLine();
String[] values = sortInfo.split(TAB);
int columnIndex = Integer.parseInt(values[0]);
int comparatorIndex = Integer.parseInt(values[1]);
boolean reverse = Boolean.parseBoolean(values[2]);
oldSort.add(new SortHolder(columnIndex, comparatorIndex, reverse));
}
} catch (FileNotFoundException ex) {
throw new FFGPanelException("Could not find stats file: " + statsFile.getAbsolutePath());
} catch (RuntimeException rte) {
rte.printStackTrace();
throw new FFGPanelException(String.format("Invalid file:%n%s%nIt is likely this is a stats file, and should be IMPORTED (under the Actions menu) rather than OPENED.", rte.getMessage()), rte);
} finally {
if (scanner != null) {
scanner.close();
}
updating.set(false);
}
} else {
throw new FFGPanelException("Cannot load stats while update is in progress");
}
}
/**
* Writer stats to the given <code>writer</code>
* @param writer as above
* @throws java.io.IOException If an error occurred during writing of the stats.
*/
@Override
public void exportStats(Writer writer) throws IOException {
if (updating.get()) {
throw new IOException("Cannot export while an update is occurring.");
}
PrintWriter printWriter = null;
try {
if (writer instanceof PrintWriter) {
printWriter = (PrintWriter) writer;
} else {
printWriter = new PrintWriter(new BufferedWriter(writer));
}
// printWriter.println(YEAR);
printWriter.println(getVSGameType().name());
printWriter.println(String.format("%d,%.9f", getRoundsCompleted(), getMultiplier()));
List<AFLPlayer> allPlayers = getVSGameType().getPlayers();
printWriter.println(allPlayers.size());
// headings
printWriter.print("Id");
// price headings
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.print(",Price");
printWriter.print(i + 1);
}
printWriter.print(",");
// score headings
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.print(",Score");
printWriter.print(i + 1);
}
printWriter.print(",");
// selection headings
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.print(",Selection");
printWriter.print(i + 1);
}
printWriter.print(",");
// rank headings
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.print(",Rank");
printWriter.print(i + 1);
}
printWriter.println();
for (AFLPlayer player : allPlayers) {
printWriter.print(player.getOrdinal());
E gameStats = getStatsForPlayer(player);
// prices
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.printf(",%d", gameStats.getPriceAt(i));
}
printWriter.print(",");
// scores
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.printf(",%d", gameStats.getScoreAt(i));
}
printWriter.print(",");
// selections
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
printWriter.printf(",%d", gameStats.getSelectionAt(i));
}
// printWriter.print(",");
// // ranks
// for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
// printWriter.printf(",%d", gameStats.getRankAt(i));
// }
printWriter.println();
}
}
finally {
if (printWriter != null) {
printWriter.close();
}
}
}
/**
* Read stats from the reader
* @param reader as above
* @throws java.io.IOException if an error occurred during reading of the stats
*/
@Override
public void importStats(Reader reader) throws IOException {
if (updating.compareAndSet(false, true)) {
Scanner scanner = null;
try {
scanner = new Scanner(reader);
// scanner.nextLine(); // year
String initialLine = scanner.nextLine();
String realFirstLine = initialLine;
try {
VSGameType savedGameType = VSGameType.valueOf(initialLine);
if (savedGameType != getVSGameType()) {
throw new IOException(String.format("Incompatible stats file. Current game type '%s' does not match saved game type '%s'", getVSGameType(), savedGameType));
}
realFirstLine = scanner.nextLine();
} catch (IllegalArgumentException iae) {
throw new IOException("Unknown VS game type = " + initialLine);
}
String[] firstLine = realFirstLine.split(",");
int roundNumber = Integer.parseInt(firstLine[0]);
if (firstLine.length > 1) {
setMultiplier(Double.parseDouble(firstLine[1]));
}
setRoundsCompleted(roundNumber);
int nPlayers = Integer.parseInt(scanner.nextLine());
scanner.nextLine(); // headings
while (nPlayers-- > 0) {
String line = scanner.nextLine();
Scanner lineScanner = new Scanner(line).useDelimiter(",");
int playerID = Integer.parseInt(lineScanner.next());
AFLPlayer curPlayer = AFLPlayer.getPlayerByOrdinal(playerID);
E stats = getStatsForPlayer(curPlayer);
stats.clear();
//prices
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
String next = lineScanner.next();
if (i < roundNumber) {
stats.addToPricesAt(i, Integer.parseInt(next));
}
}
lineScanner.next(); // blank
//scores
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
String next = lineScanner.next();
if (i < roundNumber) {
stats.addToScoresAt(i, Integer.parseInt(next));
}
}
lineScanner.next(); // blank
//selections
for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
String next = lineScanner.next();
if (i < roundNumber) {
stats.addToSelectionsAt(i, Integer.parseInt(next));
}
}
// lineScanner.next(); // blank
//
// //ranks
// for (int i = 0; i < getVSGameType().getMaxRoundNumber(); i++) {
// stats.addToRanksAt(i, Integer.parseInt(lineScanner.next()));
// }
}
}
finally {
setSaved(false);
updating.set(false);
if (scanner != null) {
scanner.close();
}
}
} else {
throw new IOException("Cannot import stats while updating is occurring.");
}
}
/**
* Return the number of rounds completed, according to the statistics
* @return as above
*/
public int getRoundsCompleted() {
return roundsCompleted;
}
/**
* Sets the number of rounds completed
* @param roundsCompleted The number of rounds completed.
*/
public void setRoundsCompleted(int roundsCompleted) {
int oldRoundsCompleted = this.roundsCompleted;
this.roundsCompleted = roundsCompleted;
propChangeSupport.firePropertyChange(ROUNDS_COMPLETED_PROPERTY, oldRoundsCompleted, roundsCompleted);
}
/**
* Save the current statistics to the current statistics file
* @throws SaveException If there were problems occuring during the save.
*/
public void save() throws SaveException {
saveAs(statsFile);
}
/**
* Save stats to the given <code>file</code>
* @param file The file to save to
* @throws ffg.gui.SaveException Any error occurring while saving.
*/
@Override
public void saveAs(File file) throws SaveException {
if (updating.get()) {
throw new SaveException("Cannot save while an update is occurring.");
}
// rename the old file
File movedFile = new File(file.getAbsolutePath() + ".bak");
if (movedFile.exists()) {
movedFile.delete();
}
file.renameTo(movedFile);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException ex) {
throw new SaveException("Could not save to " + file.getAbsolutePath() + ". Reason: " + ex.getMessage(), ex);
}
}
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(new BufferedWriter(new FileWriter(file)));
StringWriter str = new StringWriter();
exportStats(str);
printWriter.print(str.toString());
// hidden columns
for (Iterator<Integer> iterator = priceTable.getHiddenColumnIndices().iterator(); iterator.hasNext(); ) {
printWriter.print(iterator.next());
if (iterator.hasNext()) {
printWriter.print(",");
}
}
printWriter.println();
// filters
List<FFGTableFilter<E>> filters = priceTable.getTableFilters();
List<FilterKey<E, ?>> allFilterColumns = priceTable.getFilterTableColumns();
printWriter.println(filters.size());
printWriter.println(priceTable.getMatchMode().name());
for (FFGTableFilter<E> filter : filters) {
FilterKey<E, ?> filterKey = filter.getKey();
int keyIndex = allFilterColumns.indexOf(filterKey);
int operatorIndex = filterKey.getFilterOperators().indexOf(filter.getFilterOperator());
Object filterValue = filter.getValue();
String valueString = FilterStringTranslator.toString(filterValue);
printWriter.printf("%d\t%d\t%s\n", keyIndex, operatorIndex, valueString);
}
// sorting
TableComparatorChooser<E> comparatorChooser = priceTable.getTableComparatorChooser();
List<Integer> sortingColumns = comparatorChooser.getSortingColumns();
printWriter.println(sortingColumns.size());
for (int column : sortingColumns) {
int comparatorIndex = comparatorChooser.getColumnComparatorIndex(column);
boolean reverse = comparatorChooser.isColumnReverse(column);
printWriter.printf("%d\t%d\t%s\n", column, comparatorIndex, reverse);
}
printWriter.close();
setSaved(true);
setStatsFile(file);
} catch (IOException ex) {
throw new SaveException("Could not save to " + file.getAbsolutePath() + ". Reason: " + ex.getMessage(), ex);
} finally {
if (printWriter != null) {
printWriter.close();
}
}
}
/**
* Returns <code>true</code> if the player is considered 'active', that is has he played in the last round.
* @param player the player to test.
* @return as above
*/
@Override
public boolean isPlayerActive(AFLPlayer player) {
if (getRoundsCompleted() == 0) {
return true;
} else {
return playersActive.contains(player);
}
}
/**
* Implements the {@link PropertyChangeListener} interface
* @param evt
*/
// @Override
public void propertyChange(PropertyChangeEvent evt) {
// calculate whether player was active in the last round.
if (VSGameStats.SCORES_PROPERTY.equals(evt.getPropertyName())) {
VSGameStats stats = (VSGameStats) evt.getSource();
int lastRound = stats.getLastRoundPlayed();
if (lastRound == getRoundsCompleted()) {
playersActive.add(stats.getPlayer());
} else {
playersActive.remove(stats.getPlayer());
}
}
}
/**
* Returns the {@link DTPriceTable} associated with this game manager.
* @return
*/
public VSPriceTable getPriceTable() {
return priceTable;
}
public void viewPlayerPage(E stats) throws BareBonesBrowserLaunch.BrowserLaunchException {
VSGameType gameType = getVSGameType();
BareBonesBrowserLaunch.launch(String.format(PLAYER_RESEARCH_FORMAT, gameType.getBaseURL(), gameType.getGameId(stats.getPlayer())));
}
/**
* Updates the player's round number <code>roundNumber</code> score
* @param player The player to update
* @param roundNumber The round number to update
* @throws ffg.game.UpdateScoresException An error occurs retrieving from the website
*/
private void updatePlayer(AFLPlayer player, int roundNumber) throws UpdateScoresException {
System.out.println("updating = " + player);
E stats = getStatsForPlayer(player);
Scanner playerScanner = null;
DecimalFormat priceFormat = FFGConstants.getCommaDecimalFormat();
try {
// URL url = new URL(String.format(getURLFormat(), player.getOrdinal()));
Integer id = getVSGameType().getGameId(player);
if (id == null) {
return;
}
URL url = new URL(String.format(PLAYER_RESEARCH_FORMAT, getVSGameType().getBaseURL(), id));
playerScanner = new Scanner(url.openStream());
playerScanner.useDelimiter(GET_ALL_STRINGS_PATTERN);
String htmlContent = playerScanner.next();
// int beginIndex = htmlContent.indexOf(BEGIN_RESULTS_STRING) + BEGIN_RESULTS_STRING.length() + 1;
// int endIndex = htmlContent.indexOf(END_RESULTS_STRING);
// htmlContent = htmlContent.substring(beginIndex, endIndex);
Matcher selectionsMatcher = SELECTIONS_PATTERN.matcher(htmlContent);
if (selectionsMatcher.find()) {
// int rank = Integer.parseInt(selectionsMatcher.group(RANKING_GROUP));
// stats.addToRanksAt(roundNumber - 1 - 1, rank);
int selections = priceFormat.parse(selectionsMatcher.group(SELECTIONS_GROUP)).intValue();
stats.addToSelectionsAt(roundNumber - 1 - 1, selections);
System.out.println("roundNumber = '" + roundNumber + "'");
System.out.println("selections = '" + selections + "'");
// System.out.println("rank = '" + rank + "'");
} else {
System.err.println(htmlContent);
throw new UpdateScoresException("No selections found for player " + player, false);
}
Matcher rowMatcher = ROW_PATTERN.matcher(htmlContent);
while (rowMatcher.find()) {
int round = Integer.parseInt(rowMatcher.group(ROW_ROUND));
int price = priceFormat.parse(rowMatcher.group(ROW_PRICE)).intValue();
int score = Integer.parseInt(rowMatcher.group(ROW_SCORE));
stats.addToPricesAt(round - 1, price);
stats.addToScoresAt(round - 1, score);
System.out.println("round = '" + round + "'");
System.out.println("price = '" + price + "'");
System.out.println("score = '" + score + "'");
}
stats.fillPricesOutToRound(roundNumber - 1);
propChangeSupport.firePropertyChange(NUMBER_EDITED_PROPERTY, null, processedPlayersCount.incrementAndGet());
propChangeSupport.firePropertyChange(PLAYER_EDITING_PROPERTY, null, player);
} catch (ParseException ex) {
ex.printStackTrace();
throw new UpdateScoresException(ex, false);
} catch (MalformedURLException ex) {
ex.printStackTrace();
throw new UpdateScoresException(ex, false);
} catch (IOException ex) {
ex.printStackTrace();
throw new UpdateScoresException(ex, false);
} finally {
if (playerScanner != null) {
playerScanner.close();
}
}
}
/**
* Updates the last round by retrieving the scores from the DT website.
* @return <code>true</code> if successful
* @throws UpdateScoresException if there were complications arising from reading the
* statistics from the website, or if the current set of statistics do not match the
* statistics from the website
*/
public boolean updateLast() throws UpdateScoresException {
if (updating.compareAndSet(false, true)) {
try {
// get the current round number
Scanner scanner = null;
try {
URL roundNumberURL = new URL(getVSGameType().getBaseURL());
scanner = new Scanner(roundNumberURL.openStream());
scanner.useDelimiter(GET_ALL_STRINGS_PATTERN);
Matcher roundMatcher = getVSGameType().getRoundNumberPattern().matcher(scanner.next());
if (roundMatcher.find()) {
int roundNumber = Integer.parseInt(roundMatcher.group(1));
if (roundNumber == getVSGameType().getMaxRoundNumber()) {
// roundNumber--;
// problem is both round 21 and round 22 display round 22. need to look at the word 'game closed'
} else if (roundNumber - 1 != getRoundsCompleted() + 1) {
throw new UpdateScoresException(String.format("Expecting to retrieve data for Round %d but the web site has only data up to Round %d\nPlease find the latest data file and load it.", getRoundsCompleted() + 1, roundNumber - 1), true);
} else {
roundNumber--;
}
setRoundsCompleted(roundNumber);
} else {
throw new UpdateScoresException("Couldn't find current round number", true);
}
} catch (MalformedURLException ex) {
throw new UpdateScoresException(ex, false);
} catch (IOException ioe) {
throw new UpdateScoresException(ioe, false);
} finally {
if (scanner != null) {
scanner.close();
}
}
if (!updating.get()) {
return false;
}
setSaved(false);
playersActive.clear();
processedPlayersCount.set(0);
AtomicInteger atomicInteger = new AtomicInteger(0);
List<Callable<Void>> callables = new ArrayList<Callable<Void>>(N_THREADS);
for (int i = 0; i < N_THREADS; i++) {
callables.add(new UpdatePageCallable(atomicInteger));
}
try {
executor = Executors.newFixedThreadPool(N_THREADS);
List<Future<Void>> futures = executor.invokeAll(callables);
for (Future<Void> f : futures) {
try {
f.get();
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
catch (ExecutionException ee) {
ee.printStackTrace();
}
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
double newMultiplier = calculateFormulaParams().multiplier;
setMultiplier(newMultiplier);
// setPercentage(percentageForMultiplier(newMultiplier));
return executor != null;
} finally {
updating.set(false);
}
} else {
return false;
}
}
/**
* A {@link Callable} that updates from a queue of players.
*/
private class UpdatePlayerCallable implements Callable<Void> {
private final Queue<AFLPlayer> playerQueue;
private final int roundNumber;
public UpdatePlayerCallable(Queue<AFLPlayer> playerQueue, int roundNumber) {
this.playerQueue = playerQueue;
this.roundNumber = roundNumber;
}
// @Override
public Void call() throws UpdateScoresException {
AFLPlayer player;
while (!Thread.currentThread().isInterrupted() && (player = playerQueue.poll()) != null) {
updatePlayer(player, roundNumber);
}
System.out.println("Thread.currentThread().isInterrupted() = '" + Thread.currentThread().isInterrupted() + "'");
return null;
}
}
/**
* Update stats from a html page of scores
* @param htmlContent contains many player's scores from the last round
* @throws ffg.game.UpdateScoresException if any error occured reading from the scores.
*/
private void updatePage(String htmlContent) throws UpdateScoresException {
Matcher matcher = PLAYER_ROUND_PATTERN.matcher(htmlContent);
DecimalFormat decimalFormat = FFGConstants.getCommaDecimalFormat();
int roundIndex = getRoundsCompleted() - 1;
// System.out.println(htmlContent);
// System.out.println("--------------------");
while (matcher.find()) {
int ordinal = Integer.parseInt(matcher.group(ORDINAL_GROUP));
AFLPlayer player = getVSGameType().getPlayer(ordinal);
E stats = getStatsForPlayer(player);
// System.out.println("player = '" + player + "'");
try {
stats.addToPricesAt(roundIndex, decimalFormat.parse(matcher.group(CURRENT_PRICE_GROUP)).intValue());
stats.addToSelectionsAt(roundIndex, decimalFormat.parse(matcher.group(CURRENT_SELECTIONS_GROUP)).intValue());
// stats.addToRanksAt(roundIndex, decimalFormat.parse(matcher.group(RANK_GROUP)).intValue());
} catch (ParseException ex) {
throw new UpdateScoresException(ex, false);
}
stats.addToScoresAt(roundIndex, Integer.parseInt(matcher.group(ROUND_SCORE_GROUP)));
stats.fillPricesOutToRound(roundIndex);
propChangeSupport.firePropertyChange(NUMBER_EDITED_PROPERTY, null, processedPlayersCount.incrementAndGet());
propChangeSupport.firePropertyChange(PLAYER_EDITING_PROPERTY, null, player);
}
System.out.println("end");
}
/**
* {@link Callable} that updates from a page.
*/
private class UpdatePageCallable implements Callable<Void> {
private final AtomicInteger pageNumber;
/**
* Creates a new instance of UpdatePageCallable
* @param pageNumber The counter, for multithreaded purposes.
*/
public UpdatePageCallable(AtomicInteger pageNumber) {
this.pageNumber = pageNumber;
}
// @Override
public Void call() throws UpdateScoresException {
int number;
int nRoundScorePages = (int) Math.ceil(getPlayers().size() / (double) getVSGameType().getPlayerPerRoundPage());
while (!Thread.currentThread().isInterrupted() && ((number = pageNumber.incrementAndGet()) <= nRoundScorePages)) {
URL url = null;
try {
url = new URL(String.format(FFGConstants.ROUND_SCORES_FORMAT, getVSGameType().getBaseURL(), number));
} catch (MalformedURLException ex) {
throw new UpdateScoresException(ex, false);
}
Scanner scanner = null;
try {
scanner = new Scanner(url.openStream());
scanner.useDelimiter(GET_ALL_STRINGS_PATTERN);
String htmlContent = scanner.next();
if (!Thread.currentThread().isInterrupted()) {
System.out.println("updating page " + number);
updatePage(htmlContent);
}
}
catch (IOException ex) {
throw new UpdateScoresException(ex, false);
} finally {
if (scanner != null) {
scanner.close();
}
}
}
return null;
}
}
/**
* Updates all rounds by retrieving the scores from the DT website.
* @return
* @throws UpdateScoresException if there were complications arising from reading the
* statistics from the website
*/
public boolean updateAll() throws UpdateScoresException {
if (updating.compareAndSet(false, true)) {
try {
int roundNumber = getRoundsCompleted();
setRoundsCompleted(0);
for (E dtStats : playerStats.values()) {
dtStats.clear();
}
playersActive.clear();
Scanner scanner = null;
try {
URL roundNumberURL = new URL(getVSGameType().getBaseURL());
scanner = new Scanner(roundNumberURL.openStream());
scanner.useDelimiter(GET_ALL_STRINGS_PATTERN);
Matcher roundMatcher = getVSGameType().getRoundNumberPattern().matcher(scanner.next());
if (roundMatcher.find()) {
roundNumber = Integer.parseInt(roundMatcher.group(1));
setRoundsCompleted(roundNumber - 1);
}
} catch (MalformedURLException ex) {
throw new UpdateScoresException(ex, false);
} catch (IOException ioe) {
throw new UpdateScoresException(ioe, false);
} finally {
if (scanner != null) {
scanner.close();
}
}
if (!updating.get()) {
return false;
}
Collection<AFLPlayer> curPlayers = getPlayers();
Queue<AFLPlayer> queue = new ConcurrentLinkedQueue<AFLPlayer>(curPlayers);
setSaved(false);
processedPlayersCount.set(0);
List<Callable<Void>> callables = new ArrayList<Callable<Void>>(N_THREADS);
for (int i = 0; i < N_THREADS; i++) {
callables.add(new UpdatePlayerCallable(queue, roundNumber));
}
List<Future<Void>> futures = null;
try {
executor = Executors.newFixedThreadPool(N_THREADS);
long now = System.currentTimeMillis();
futures = executor.invokeAll(callables);
System.out.printf("time = %d\n", (System.currentTimeMillis() - now));
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
if (futures != null) {
for (Future<Void> f : futures) {
try {
f.get();
}
catch (ExecutionException ee) {
if (ee.getCause() instanceof UpdateScoresException) {
throw ((UpdateScoresException) ee.getCause());
} else {
throw new UpdateScoresException(ee, false);
}
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
double newMultiplier = calculateFormulaParams().multiplier;
setMultiplier(newMultiplier);
// setPercentage(percentageForMultiplier(newMultiplier));
return executor != null;
} finally {
updating.set(false);
}
} else {
return false;
}
}
/**
* The Dream Team rows are alternating colors.
*/
public static class VSTableCellRenderer extends AlternateRowTableCellRenderer {
private final Color defaultForeground;
private final int defaultHorizontalAlignment;
private final int defaultIconTextGap;
public VSTableCellRenderer(VSGameManager manager, Color even, Color odd) {
super(manager, even, odd);
defaultForeground = getForeground();
defaultHorizontalAlignment = getHorizontalAlignment();
defaultIconTextGap = getIconTextGap();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
// set defaults
setFont(getNormalFont());
setEnabled(true);
setForeground(defaultForeground);
setHorizontalAlignment(defaultHorizontalAlignment);
setIcon(null);
setDisabledIcon(null);
setIconTextGap(defaultIconTextGap);
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return this;
}
}
/**
* The current multiplier
* @return as above
*/
public double getMultiplier() {
return multiplier;
}
public double getScale() {
return scale;
}
/**
* Sets the current multiplier
* @param multiplier The new current multiplier
*/
public void setMultiplier(double multiplier) {
double oldMultiplier = this.multiplier;
if (Double.compare(oldMultiplier, multiplier) != 0) {
this.multiplier = multiplier;
scale = getVSGameType().getDefaultMultiplier() / (3 * multiplier + getVSGameType().getDefaultMultiplier());
propChangeSupport.firePropertyChange(MULTIPLIER_PROPERTY, oldMultiplier, multiplier);
setSaved(false);
}
}
// /**
// * The current percentage
// * @return as above
// */
// public double getPercentage() {
// return percentage;
// }
//
// /**
// * Sets the current percentage
// * @param percentage The new current percentage
// */
// public void setPercentage(double percentage) {
// double oldPercentage = this.percentage;
// this.percentage = percentage;
// propChangeSupport.firePropertyChange(PERCENTAGE_PROPERTY, oldPercentage, percentage);
// if (Double.compare(oldPercentage, percentage) != 0) {
// setSaved(false);
// }
// }
/**
* Calculate the multiplier from the stats. It is simply, for all eligible players
* (eligible being played in the last round and has played in at least 3 rounds)
* sum of their current price / sum of their 3 round averages.
* @return The new multiplier
*/
public FormulaParams calculateFormulaParams() {
int totalPrices = 0;
int totalAggregates = 0;
int round = getRoundsCompleted();
int scoresToGet = round < 3 ? round : 3;
int count = 0;
int roundNo = round == 1 ? 0 : round - 1 - 1;
for (E stats : playerStats.values()) {
if (stats.isEligible(round)) {
count++;
totalPrices += stats.getPriceAt(roundNo);
int lastScores = stats.getLastNScores(scoresToGet);
totalAggregates += lastScores;
System.out.println(lastScores / (double) scoresToGet);
}
}
System.out.println("count = '" + count + "'");
System.out.println("totalPrices = '" + totalPrices + "'");
System.out.println("totalAggregates = '" + totalAggregates + "'");
if (totalAggregates == 0) {
// return getVSGameType().getDefaultMultiplier();
return new FormulaParams(count, totalPrices, 0, getVSGameType().getDefaultMultiplier());
} else {
// return totalPrices / (totalAggregates / (double) scoresToGet);
double totalThreeRoundAverages = (totalAggregates / (double) scoresToGet);
return new FormulaParams(count, totalPrices, totalThreeRoundAverages, totalPrices / totalThreeRoundAverages);
}
}
public static class FormulaParams {
public final int numPlayers;
public final int totalPrices;
public final double totalThreeRoundAverages;
public final double multiplier;
public FormulaParams(int numPlayers, int totalPrices, double totalThreeRoundAverages, double multiplier) {
this.numPlayers = numPlayers;
this.totalPrices = totalPrices;
this.totalThreeRoundAverages = totalThreeRoundAverages;
this.multiplier = multiplier;
}
}
// /**
// * Using the slope and intercept, calculate the percentage from the given <code>multiplier</code>.
// * Given there is a linear relationship from these two variables.
// * @param multiplier
// * @return the new percentage
// */
// public double percentageForMultiplier(double multiplier) {
// return getVSGameType().getSlope() * multiplier + getVSGameType().getIntercept();
// }
/**
* Stop any current updates
*/
public void cancelUpdate() {
updating.set(false);
if (executor != null) {
executor.shutdownNow();
executor = null;
}
}
/**
* Returns <code>true</code> if some kind of update is currently occurring.
* @return
*/
public boolean isUpdating() {
return updating.get();
}
/**
* Returns the type of virtual sports game.
* @return as above
*/
public abstract VSGameType getVSGameType();
/**
* Given the player, return the positions for this particular game type
* @param player The player in question
* @return as above
*/
@Override
public AFLPositions getPositionsForPlayer(AFLPlayer player) {
return getVSGameType().getPositionsForPlayer(player);
}
/**
* Given the player, return the staring price for this particular game type
* @param player The player in question
* @return as above
*/
@Override
public int getStartingValueForPlayer(AFLPlayer player) {
return getVSGameType().getStartingValueForPlayer(player);
}
/**
* Given the team, return the icon for this particular game type
* @param team The team in question
* @return as above
*/
@Override
public Icon getIconForTeam(AFLTeam team) {
return getVSGameType().getTeamIcon(team);
}
/**
* Given the team, return the disabled icon for this particular game type
* @param team The team in question
* @return as above
*/
@Override
public Icon getIconForTeamDisabled(AFLTeam team) {
return getVSGameType().getTeamDisabledIcon(team);
}
/**
* Name to be displayed
* @return The display name of this game type
*/
public String getFullName() {
return getVSGameType().toString();
}
/**
* Given the player, return the game id for this particular game type
* @param player The player in question
* @return as above
*/
@Override
public Integer getGameId(AFLPlayer player) {
return getVSGameType().getGameId(player);
}
@Override
public E getStatsForPlayer(AFLPlayer player) {
return super.getStatsForPlayer(player);
}
// public static void main(String[] args) throws Exception {
// Scanner scanner = new Scanner(new File("W:\\test.txt"));
// scanner.useDelimiter(GET_ALL_STRINGS_PATTERN);
// String s = scanner.next();
// scanner.close();
// Matcher m = PLAYER_ROUND_PATTERN.matcher(s);
// int counter = 1;
// while (m.find()) {
// System.out.println(counter++);
// System.out.println("m.group(3) = '" + m.group(3) + "'");
// System.out.println("m.group(4) = '" + m.group(4) + "'");
// }
// }
}
|