Subversion Repositories sokoban

Rev

Blame | Last modification | View Log | RSS feed

  1. package gdi1sokoban.highscores;
  2.  
  3. import gdi1sokoban.exceptions.InternalFailureException;
  4.  
  5. import java.io.BufferedReader;
  6. import java.io.BufferedWriter;
  7. import java.io.File;
  8. import java.io.FileNotFoundException;
  9. import java.io.FileReader;
  10. import java.io.FileWriter;
  11. import java.io.IOException;
  12. import java.lang.Math;
  13. import java.net.URI;
  14. import java.net.URISyntaxException;
  15. import java.util.ArrayList;
  16. import java.util.Collections;
  17. import java.util.Comparator;
  18. import java.util.HashMap;
  19.  
  20. /**
  21.  * Highscore class
  22.  *
  23.  * manages a highscore list
  24.  * @author Victor
  25.  */
  26. public class Highscores {
  27.         private int MAX_HIGHSCORES = 10; // max. number of highscores
  28.         private final HashMap<Integer, ArrayList<HighscoreEntry>> highscores = new HashMap<Integer, ArrayList<HighscoreEntry>>(); // array list for saving level specific highscores
  29.         private File hsFile; // file handle of highscores.txt
  30.        
  31.         /**
  32.          * Highscores constructor
  33.          *
  34.          * @param hsFilename the file in which highscores are saved
  35.          * @throws InternalFailureException
  36.          */
  37.         public Highscores(final String hsFilename) throws InternalFailureException {
  38.                 try {
  39.                         hsFile = new File(new URI(hsFilename));
  40.                 } catch (final URISyntaxException e) {
  41.                         throw new InternalFailureException("Highscore file I/O error");
  42.                 }
  43.                 if (! hsFile.exists())
  44.                         try {
  45.                                 hsFile.createNewFile();
  46.                         } catch (final IOException e) {
  47.                                 throw new InternalFailureException("Highscore file I/O error");
  48.                         }
  49.                 try {
  50.                         loadHighscores();
  51.                 } catch (final FileNotFoundException e) {
  52.                         throw new InternalFailureException("Highscore file not found");
  53.                 } catch (final IOException e) {
  54.                         throw new InternalFailureException("Highscore file I/O error");
  55.                 }
  56.         }
  57.        
  58.         /**
  59.          * Highscores constructor
  60.          *
  61.          * @param hsFilename the file in which highscores are saved
  62.          * @param maxHighscores max. number of highscores in list
  63.          * @throws InternalFailureException
  64.          */
  65.         public Highscores(final String hsFilename, final int maxHighscores) throws InternalFailureException {
  66.                 this(hsFilename);
  67.                 MAX_HIGHSCORES = maxHighscores; // max. no. of highscores foreach level
  68.         }
  69.        
  70.         /**
  71.          * processes the highscore data file and transforms it into the internal format
  72.          * @throws IOException
  73.          * @throws FileNotFoundException
  74.          */
  75.         private void loadHighscores() throws IOException, FileNotFoundException {
  76.                 final BufferedReader in = new BufferedReader(new FileReader(hsFile));
  77.                 String line = null;
  78.                 while ((line = in.readLine()) != null) { // read highscore file line by line
  79.                         if (line.trim().isEmpty())
  80.                                 break;
  81.                         final String[] hsData = line.split("\t"); // split highscore line
  82.                         String myLevel = "";
  83.                         String myPlayerName = "";
  84.                         String mySteps = "0";
  85.                         String myTime = "0";
  86.                         if (hsData.length >= 1) myLevel = hsData[0];
  87.                         if (hsData.length >= 2) myPlayerName = hsData[1];
  88.                         if (hsData.length >= 3) mySteps = hsData[2];
  89.                         if (hsData.length >= 4) myTime = hsData[3];
  90.                         try {
  91.                                 addHighscore(Integer.valueOf(myLevel), myPlayerName, mySteps, myTime); // add the highscore into internal format
  92.                         } catch (final NumberFormatException e) { // wrong number format
  93.                                 System.err.println("Fehler: Falscher Eingabewert! Eingabe muss eine gültige Zahl sein!");
  94.                         }
  95.                 }
  96.                 in.close();
  97.                 try {
  98.                         saveHighscores(); // save the highscore list
  99.                 } catch (final InternalFailureException e) {
  100.                         System.err.println("Interner Fehler: Konnte Highscoredatei nicht speichern");
  101.                 }
  102.         }
  103.  
  104.         /**
  105.          * adds a highscore data structure into the highscore list
  106.          * @param levelNr level no.
  107.          * @param hsRow highscore row to be added
  108.          */
  109.         private void addHighscore(final int levelNr, final HighscoreEntry hsRow) {
  110.                 if (highscores.get(levelNr) == null)
  111.                         highscores.put(levelNr, new ArrayList<HighscoreEntry>(0));
  112.                 highscores.get(levelNr).add(hsRow); // add the highscore
  113.                 sortHighscores(); // sort the highscore list
  114.         }
  115.  
  116.         /**
  117.          * creates a highscore data structure out of a set of strings
  118.          * and adds the structure to the highscore list
  119.          * @param myLevel game level
  120.          * @param myPlayerName player name
  121.          * @param mySteps steps made
  122.          * @param myTime time passed by (in sec.)
  123.          */
  124.         public void addHighscore(final int myLevel, String myPlayerName, final String mySteps, final String myTime) {
  125.                 if (myPlayerName == null) // abort if player name is null
  126.                         return;
  127.                 if (myPlayerName.isEmpty()) // set to default if player name is empty
  128.                         myPlayerName = "Dabbes";
  129.                 try {
  130.                         final HighscoreEntry hsRow = new HighscoreEntry(myPlayerName, mySteps, myTime); // create a highscore entry structure
  131.                         addHighscore(myLevel, hsRow); // add the highscore
  132.                 } catch (final NumberFormatException e) { // wrong number format
  133.                         System.err.println("Highscore-Fehler: Falscher Eingabewert! Eingabe muss eine gültige Zahl sein!");
  134.                 }
  135.         }
  136.  
  137.         /**
  138.          * sorts the highscore lists of every level with help of
  139.          * an instance of HighscoreEntryComparator
  140.          */
  141.         private void sortHighscores() {
  142.                 final Comparator<HighscoreEntry> hsComp = new HighscoreEntryComparator(); // create an instance of the highscore entry comparator
  143.                 // loop every level
  144.                 for (final Integer hsLevel : highscores.keySet()) {
  145.                         Collections.sort(highscores.get(hsLevel), hsComp); // sort the highscore list for this level
  146.                         // loop all highscore entries for this level
  147.                         for (int i = MAX_HIGHSCORES; i < highscores.get(hsLevel).size(); i++)
  148.                                         // every level may have MAX_HIGHSCORE highscore entries, cut elements
  149.                                         highscores.get(hsLevel).remove(i);
  150.                 }              
  151.         }
  152.        
  153.         /**
  154.          * resets the highscore list
  155.          */
  156.         public void resetHighscores() {
  157.                 highscores.clear();
  158.                 try {
  159.                         saveHighscores();
  160.                 } catch (final InternalFailureException e) {
  161.                         System.err.println("Interner Fehler: Konnte Highscoredatei nicht speichern");
  162.                 }
  163.         }
  164.        
  165.         /**
  166.          * transforms a list of level highscores into an array of strings
  167.          * @param levelNo level number
  168.          * @return string table
  169.          */
  170.         public String[][] toStringArray(final int levelNo) {
  171.                 final ArrayList<HighscoreEntry> lvlHighscores = highscores.get(levelNo); // get level-specific highscores
  172.                 if (lvlHighscores == null)
  173.                         return new String[0][0]; // no highscores available
  174.                 final int arraySize = Math.min(MAX_HIGHSCORES, lvlHighscores.size()); // ensure that only MAX_HIGHSCORES highscores are processed
  175.                 final String[][] ret = new String[arraySize][4];
  176.                 for (int i = 0; i < arraySize; i++) { // loop every higscore, build the string array
  177.                 ret[i][0] = Integer.valueOf(i+1).toString();
  178.                 ret[i][1] = lvlHighscores.get(i).getPlayerName();
  179.                 ret[i][2] = Integer.valueOf(lvlHighscores.get(i).getSteps()).toString();
  180.                 ret[i][3] = Integer.valueOf(lvlHighscores.get(i).getTime()).toString();
  181.                 }
  182.                 return ret;
  183.         }
  184.        
  185.         /**
  186.          * returns all available levels in a sorted string list
  187.          * @return level list
  188.          */
  189.         public String[] getLevels() {
  190.                 final ArrayList<Integer> levelList = new ArrayList<Integer>(0);
  191.                 levelList.addAll(highscores.keySet()); // put the hashmap's keys (level no.) into a list and sort it
  192.                 Collections.sort(levelList); // sort level numbers
  193.                 final String [] levelArray = new String[levelList.size()];
  194.                 for (int i = 0; i < levelList.size(); i++)
  195.                         levelArray[i] = Integer.valueOf(levelList.get(i)).toString();
  196.                 return levelArray;
  197.         }
  198.        
  199.         /**
  200.          * transforms the highscores into the highscore file format
  201.          */
  202.         @Override
  203.         public String toString() {
  204.                 final StringBuffer retStr = new StringBuffer(100);
  205.                 final ArrayList<Integer> levelList = new ArrayList<Integer>(0);
  206.                 levelList.addAll(highscores.keySet()); // put the hashmap's keys (level no.) into a list and sort it
  207.                 Collections.sort(levelList);
  208.                 for (final int hsLevel : levelList)
  209.                         for (final HighscoreEntry row : highscores.get(hsLevel))
  210.                                 retStr.append(hsLevel).append("\t").append(row.toString()).append("\n");
  211.                 return retStr.toString().trim();
  212.         }
  213.  
  214.         /**
  215.          * saves the highscore list into file
  216.          * @throws InternalFailureException
  217.          */
  218.         public void saveHighscores() throws InternalFailureException {
  219.                 final String hsContent = this.toString();      
  220.             FileWriter fw;
  221.             BufferedWriter bw;
  222.             try {
  223.               fw = new FileWriter(hsFile);
  224.               bw = new BufferedWriter(fw);
  225.               bw.write(hsContent); // write file content
  226.               bw.close();
  227.               fw.close();
  228.             } catch (final IOException e) {
  229.                 throw new InternalFailureException("Highscore file I/O error");
  230.             } catch (final NullPointerException e) {
  231.                 throw new InternalFailureException("Highscore file I/O error");
  232.             }          
  233.         }
  234.  
  235.         /**
  236.          * checks if a given combination of level no. and steps count
  237.          * is better or equal than a given existing highscore entry
  238.          * @param steps steps made
  239.          * @param time time needed to solve level
  240.          * @param existingEntry existing highscore entry
  241.          * @return false if existing highscore is better or equal than given highscore
  242.          */
  243.         private boolean checkHighscore(final int steps, final int time, final HighscoreEntry existingEntry) {
  244.                 // transform level and steps information into highscore entry structure
  245.                 final HighscoreEntry checkEntry = new HighscoreEntry("", String.valueOf(steps), String.valueOf(time));
  246.                 final Comparator<HighscoreEntry> hsComp = new HighscoreEntryComparator();
  247.                 // return the comparison result
  248.                 return (hsComp.compare(existingEntry, checkEntry) > 0);
  249.         }
  250.        
  251.         /**
  252.          * checks if a given combination of level no. and steps count
  253.          * leads to a new highscore
  254.          * @param level game level no.
  255.          * @param steps steps count
  256.          * @param time time needed to solve level
  257.          * @return true if new highscore, false if not
  258.          */
  259.         public boolean isHighscore(final int level, final int steps, final int time) {
  260.                 if (highscores.get(level) == null)
  261.                         return true;
  262.                 if (highscores.get(level).size() < MAX_HIGHSCORES) // if highscore count is less than MAX_HIGHSCORES
  263.                         return true;
  264.                 return checkHighscore(steps, time, highscores.get(level).get(MAX_HIGHSCORES - 1)); // return the comparison result
  265.         }
  266.        
  267.         /**
  268.          * calculates the place of a combination of
  269.          * level no. and steps count in highscore list
  270.          * @param level game level no.
  271.          * @param steps steps made
  272.          * @param time time needed to solve level
  273.          * @return calculated ranking in highscore list
  274.          */
  275.         public int getPlace(final int level, final int steps, final int time) {
  276.                 int place = 1; // initial ranking
  277.                 if (highscores.get(level) == null)
  278.                         return place;
  279.                 for (final HighscoreEntry row: highscores.get(level)) {
  280.                         if (checkHighscore(steps, time, row)) // level+steps combination is better than actual row?
  281.                                 return place; // return this rank
  282.                         place++;
  283.                 }
  284.                 return place; // if no highscores were made we assume 1st place, else last place
  285.         }
  286.        
  287.         /**
  288.          * returns the highscore count for a specified level
  289.          * @param level level no.
  290.          * @return highscore count
  291.          */
  292.         public int getHighscoreCount(final int level) {
  293.                 if (highscores.get(level) == null)
  294.                         return 0;
  295.                 return highscores.get(level).size();
  296.         }
  297.        
  298.         /**
  299.          * counts ALL global highscore entries
  300.          * @return
  301.          */
  302.         public int getAllHighscoresCount() {
  303.                 int count = 0;
  304.                 for (final ArrayList<HighscoreEntry> hsData : highscores.values())
  305.                         count += hsData.size();
  306.                 return count;
  307.         }
  308. }