Top scores list

From Gardenwiki

Jump to: navigation, search

Particularly for single-person games, players appreciate being able to compare scores. The top scores list will be cleared each time a new jar file is uploaded for the game, and each time the Game Gardens server is reset, but it is still a nice touch to include.

This is the way that Pickerel implements keeping track of top scores.

Contents

TopScore class

First, make an object class for a single line in the top scores table. Example:

 public class PickerelTopScore
       extends SimpleStreamableObject
       implements Comparable<PickerelTopScore>
 {
   public PickerelTopScore() {}
  
   public PickerelTopScore(String player, int score) {
       this.player     = player;
       this.score      = score;
   }
   
   // documentation inherited from interface Comparable
   public int compareTo(PickerelTopScore other) {
       return other.score - score;
   }
   
   public String   player;
   public int      score;
 }

Modify the Game Object

The next step is to add a field to the game object to store an array of top scores. Add a line similar to the following, then run ant gendobj.

   public PickerelTopScore[] topScores;

TopScore panel

We need a panel to display the top scores list to the players. This is the way Pickerel does it:

 public class PickerelTopScoresPanel extends JPanel
       implements PlaceView {
   public PickerelTopScoresPanel(ToyBoxContext ctx) {
       _ctx  =ctx;
       _model=new TopScoresTableModel();
       
       setLayout(new BorderLayout());
       
       _table=new JTable(_model);
       SafeScrollPane scroll=new SafeScrollPane(_table, 250, 0);
       scroll.setBorder(null);
       add(scroll, BorderLayout.CENTER);
   }
   
   // From PlaceView
   public void willEnterPlace(PlaceObject place) {
       _gameobj=(PickerelObject)place;
       _gameobj.addListener(_model);
       _model.init();
   }
   
   // From PlaceView
   public void didLeavePlace(PlaceObject place) {
       if (_gameobj!=null) {
           _gameobj.removeListener(_model);
           _gameobj=null;
       }
   }
   
   private class TopScoresTableModel extends AbstractTableModel
           implements AttributeChangeListener {
       public void init() {
           fireTableDataChanged();
       }
       
       // From AttributeChangeListener
       public void attributeChanged(AttributeChangedEvent event) {
           String name=event.getName();
           
           if (name.equals(PickerelObject.TOP_SCORES)) {
               fireTableDataChanged();
               SwingUtil.sizeToContents(_table);
           }
       }   // end attributeChanged
       
       // From AbstractTableModel
       public String getColumnName(int col) {
           switch (col) {
               case COL_POINTS:
                   return "Score";
               case COL_PLAYER:
                   return "Player";
               default:
                   break;
           }
           
           return null;
       }   // end getColumnName
       
       // From AbstractTableModel
       public Class getColumnClass(int col) {
           switch (col) {
               case COL_POINTS:
                   return Integer.class;
               case COL_PLAYER:
               default:
                   return String.class;
           }
       }   // end getColumnClass
       
       // From AbstractTableModel
       public int getColumnCount() {
           return 2;
       }
       
       // From AbstractTableModel
       public int getRowCount() {
           if ((_gameobj==null)
           || (_gameobj.topScores==null)) {
               return 0;
           }
           return _gameobj.topScores.length;
       }   // end getRowCount
       
       // From AbstractTableModel
       public Object getValueAt(int row, int col) {
           if ((_gameobj==null)
           || (_gameobj.topScores==null)) {
               return null;
           }
           
           PickerelTopScore top_score=_gameobj.topScores[row];
           if (top_score==null) {
               return null;
           }
           switch (col) {
               case COL_POINTS:
                   return top_score.score;
               case COL_PLAYER:
                   return top_score.player;
               default:
                   return null;
           }
       }   // end getValueAt
   }   // end TopScoresTableModel
   
   private static final int COL_POINTS  =0;
   private static final int COL_PLAYER  =1;
   
   private ToyBoxContext       _ctx;
   private PickerelObject      _gameobj;
   private JTable              _table;
   private TopScoresTableModel _model;
 }

Modify the Game Panel

Now add the TopScoresPanel to the game panel. There are many ways to do this. Pickerel uses a JTabbedPane, which also holds other displays.

       _tabbed_pane = new JTabbedPane();
       _tabbed_pane.addTab("Top Scores",
               new PickerelTopScoresPanel(ctx));

Modify the Game Manager

There are four modifications to make to the Game Manager. We need to create a static array to hold the top scores, initialize the game object's topScores array, add a method to update the topScores array, and invoke that method when a game finishes.

Pickerel has multiple difficulty levels, so it uses a two-dimensional array in the game manager, passing only the relevant difficulty level's top scores to the game object. Here are the sections of PickerelManager that relate to the top scores.

Static Array

   private static PickerelTopScore[][] _top_scores
           = new PickerelTopScore[MAX_DIFFICULTY][MAX_TOP_SCORES];

Initialization

Either in the gameWillStart() method or in a separate initialization method that is invoked by gameWillStart(), add this line. _difficulty is the difficulty level (an int). We use (_difficulty - 1) because our lowest difficulty level is 1, but the first index of the array is 0.

       _gameobj.setTopScores(_top_scores[_difficulty - 1]);

Updating the Top Scores

Here is a method that can be used to update the top scores lists. There are many other ways to do the same task.

   private void recordFinalScore() {
       int score=_gameobj.score;
       
           String player  =_gameobj.players[0].toString();
           
           Vector<PickerelTopScore> newTopScores =
                   new Vector<PickerelTopScore>();
           int ptr = -1;
           
           /**  populate the vector
            *   remember the pointer to this player's
            *   score if it is already in top score list
            *   for this difficulty level
            */
           for (int ii = 0; ii < MAX_TOP_SCORES; ii++) {
               PickerelTopScore thisScore =
                       _top_scores[_gameobj.difficulty - 1][ii];
               if (thisScore != null) {
                   newTopScores.addElement(thisScore);
                   if (thisScore.player.equals(player))
                       ptr = ii;
               }
           }       // end for
           
           // if we found the player, then just update *his* score if need be
           // otherwise, add new score for him at bottom
           if (ptr > -1) {
               PickerelTopScore thisScore = newTopScores.elementAt(ptr);
               if (thisScore.score < score)
                   newTopScores.elementAt(ptr).score = score;
           } else {
               newTopScores.addElement(new PickerelTopScore(player, score));
           }
           
           PickerelTopScore[] scoreArray = new PickerelTopScore[newTopScores.size()];
           newTopScores.toArray(scoreArray);
           Arrays.sort(scoreArray);
           
           // now update the official top scores list for this
           // difficulty level.  Extra entries in the vector
           // get ignored.
           int max = Math.min(scoreArray.length, MAX_TOP_SCORES);
           try {
               for (int i = 0; i < max; i++) {
                   _top_scores[_gameobj.difficulty - 1][i] = scoreArray[i];
               }
           } catch (ArrayIndexOutOfBoundsException e){
               log.warning("Top scores list not completely updated; " +
                       "ArrayIndexOutOfBoundsException");
           }
       _gameobj.setTopScores(_top_scores[_gameobj.difficulty - 1]);
   } // end recordFinalScore

Invoking recordFinalScore()

The last step is to invoke recordFinalScore() when a game ends. This can be done in gameDidEnd(), or in a separate method that checks for an end game condition (if you already use one).

Personal tools