Creation of a high score table (Part I): using a CArray
25 Feb 2003 - 20:19

This tutorial present the use of a CArray... class to maintain a sorted list of structure representing the high score table of a game.

Data Structure Description

The structure that will hold a player score - which is also an element of the array - is a TScore defined like this:

class TScore
{
public :
   TScore();
   TScore(TInt aScore,const TDesC& aName);
...
public :
        TInt     iValue;
private:
        TBuf<15> iName;
};

You may notice that iValue is not a private member of TScore - which may look unusual for a data member. This is needed because iValue is used as a key for sorting the score table and needs visibility to be used inside by TKeyArray... as we will see in the Adding an element paragraph.

A TScore object has a fixed-length (this is an implementation choice). To hold our high-score table, we will create an array of TScore. I have selected the CArrayFixSeg<TScore> to do this:
-  CArrayFixSeg because of the fixed length of a TScore element
-  CArrayFixSeg because I want to be able to insert element easily in the middle of my list.
-  CArrayFixSeg<TScore> because it holds TScore elements

The array is defined as a private member of my CGameclass which hold my game data and which is define as follow:

class CGame : public CBase
{
public :
   static CGame *NewL();
   static CGame *NewLC();
   void ScoreDisplay();
   void AddScoreL(const TScore& aScore);
   void ResetScore();
   ~CGame();

private:
   void ConstructL();

private:
   CArrayFixSeg<TScore> *iScoreTable;
};

Table creation

The table is allocated in CGame second-phase constructor and is deleted in the destructor:

void CGame::ConstructL()
{
   iScoreTable=new(ELeave)CArrayFixSeg<TScore>(5);
}

CGame::~CGame()
{
   delete iScoreTable;
}

The 5 value is the granularity of my table segment (allocation is made 5 elements by 5 elements). This value shall be selected depending on the final number of elements the table will contain. 5 is suitable for medium table sizes (> 10 elements, < 50 elements).

Adding an element in the table

We want to keep our table sorted by score (...which is quite common for a score table!!!!). Our new element - a TScore object - can be added in any position of the table. This depends of its iValue data member.

This is done quite easily in Symbian by defining a TKeyArray... object (here a TKeyArrayFix because the table is a CArrayFixSeg). The key is created by giving the key data member as a parameter of its constructor (_FOFF(TScore,iValue)) and how we want to make the comparison (ECmpTInt because we compare integers):

TKeyArrayFix byScore(_FOFF(TScore,iValue),ECmpTInt);

The TScore object is then inserted in the list by a simple call to InsertIsqAllowDuplicatesL giving the TKeyArray... object as ordering parameter:

iScoreTable->InsertIsqAllowDuplicatesL(aScore,byScore);

The use of InsertIsqL could also be used for list where the key value shall be unique as it leaves when you try to insert an element with an existing key value. This is definitely not the case in our example since two players can have the same score.

One limiting point on using these functions is that the table can only be sorted in increasing order which is not generally the case for a high score table. This problem is solved by the way TScore stores the score value in the iValue member:

TScore::TScore(TInt aValue,const TDesC& aName)
{
        iName = aName;
        iValue= -aValue;
}
By storing the negative value, the key order will be reversed and our table will be sortec in "decreasing" order. The primitives that return the score value do of course return a positive score :

TInt TScore::Score()
{
        return(-iValue);
}

Up to that point, we have just added an element to our table but we did not make any test about the table size. Since we do not have yet unlimited amount of memory in a mobile phone, we will then limit the size of our table to a predefined constant KTableScoreSize and delete any extra element (i.e. the element at position KTableScoreSize since the first element is at position zero):

if(iScoreTable->Count()>KTableScoreSize)
   iScoreTable->Delete(KTableScoreSize);

And finally, we will delete any unused memory segment that could have been created by the element insertion (the granularity of 5 used for our table implies that each new insertion in the middle of our table causes the allocation of a block of 5 element from which only 1 will be used):

iScoreTable->Compress();

Finally the whole code for the insertion function:

void CGame::AddScoreL(const TScore& aScore)
{
   //
   // Insert the element in the Score table
   // (using an Integer key on iValue)
   //
   TKeyArrayFix byScore(_FOFF(TScore,iValue),ECmpTInt);
   iScoreTable->InsertIsqAllowDuplicatesL(aScore,byScore);
   //
   // Remove the last player from the table if necessary
   // and compress the table (to free unused space)
   //
   if(iScoreTable->Count()>KTableScoreSize)
       iScoreTable->Delete(KTableScoreSize);
   iScoreTable->Compress();
}

Removing an element

The suppression of a single element has been shown above using the Delete primitive. We can also remove the all the elements in a single call using this primitive:

iScoreTable->Delete(0,iScoreTable->Count());

The zero value is the position of the first element to delete. The second parameter is the number of element to delete (here the number of element in the table).

Displaying the table

The whole table can be displayed using a simple foo loop:

for(i=0;i<count;i++)
{
   score=(&(*iScoreTable)[i])->Score(name);
   console->Printf(_L("%d. %05d - "),i+1,score);
   console->Printf(name);
   console->Printf(_L("\n"));
}

Score() is a public TScore function defined as TInt Score(TPlayerName& aName);.

 
score1.zip
score1.zip


Creation of a high score table (Part II): saving to a file store>>


Tutorial posted February 25th, 2003 by Anonymous

copyright 2003-2009 NewLC SARL