Top scores list
From Gardenwiki
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).

