So Biff wants to have a high score table in his game 

Written by Lachie Dazdarian (September, 2007)

Introduction

On more than one occasion I was inquired by a programming newbie about a 
set of routines that load a high score table from an external file, input a 
new high score properly, and then save the modified high scores table.
Using the same set of routines for high scores since the days of Ball 
Blazing Fantasy, I decided to write a tutorial on them and implement some 
lacking flexibility (plus few fixes) there, something that was long needed 
to be done but wasn't due the fact the routines did their job perfectly.
The tutorial will also point you out to some useful (for high scores table 
managing) additional routines, like the name inputting and file encryption 
ones, not written by me.

Let's do it!

It's fairly obvious we'll need two separate subroutines, one for 
loading/reading our high score table, and one for writing/modifying it.
We'll start with loading/reading of a high score table, as that part is 
easier and a logical start.
The subroutine for reading a high score table should work relatively 
simple. It will open a file which contains name and score entries, storing 
them in appropriate variables and then printing them on the screen, this 
part being most dependent on the developer's wishes and needs (the method 
of printing, position of the high score table, its formatting, etc.).
First, we should create a text file containing our name and score entries. 
Create a file named 'high_scores.dat', open it with Notepad and input this:

   FRED
   10000
   BILL
   9000
   SARAH
   8000
   BOB
   7000
   RED
   6000
   SUE
   5000
   DAVID
   4000
   GREG
   3000
   TIM
   2000
   GEORGE
   1000

It contains 10 high score entries, formatted with name followed by the 
accompanying score. I find this formatting the most suitable for editing, 
although you can pick one where all the names all listed first, and then 
followed by all the scores. Still, no important benefits from any type of 
these two formattings, so we'll work with the one I stared with.

This file will be used with the following 'ReadHighScore' subroutine.

Let's start our main program with some needed initiation statements:

   #include "fbgfx.bi"
   Using FB

   Const num_of_entries = 10

'num_of_entries' will flag the number of score entries (names or scores in 
the high score table), and should correspond with the number of entries in 
the 'high_score.dat' file (not lines, but high score ENTRIES!).

We should now declare our subroutine with:

   Declare Sub ReadHighScore (highscore_file As String)

The 'highscore_file' variable will flag the file you want for the 
'ReadHighScore' subroutine to open. Not necessary to declare the subroutine 
like this, but this adds some flexibility to it.

After this, we should declare the following variables:

   Dim Shared workpage As Integer
   Dim Shared hname(num_of_entries) As String
   Dim Shared hscore(num_of_entries) As String

'workpage' variable is not related to this tutorial and will be used to 
swap screen work pages inside the loop where the high score table will be 
drawn. 'hname' array will hold the name entries, while 'hscore' array will 
hold the score entries from the high score table.

Finally, let's initialize our screen and work/visible pages with:

    ScreenRes 640, 480, 32, 2, GFX_ALPHA_PRIMITIVES+GFX_WINDOWED
   ScreenSet 1, 0

Following this code we should place this:

   ReadHighScore "high_scores.dat"
   End

   Sub ReadHighScore (highscore_file As String)

   End Sub

You can compile this code, but nothing will happen as the 'ReadHighScore' 
subroutine is empty. Let's fill it up!
We need to start it by opening the 'high_scores.dat' file and reading the 
needed data from it. Please refer to FreeBASIC's OPEN statement for info on 
file opening in FreeBASIC if not familiar with it.

As we want to open the file using a FREE file handle, we need to dimension 
a variable that will hold this information and pass it into it. Use this 
code:

   Dim free_filehandle As Integer

   free_filehandle = FreeFile

We should now open the high score file with:

   Open highscore_file For Input As #free_filehandle

After the file is opened for reading (FOR INPUT), let's use a for loop to 
retrieve all the data from it and store it in our 'hname' and 'hscore' 
variables:

   For count_entry As Integer = 1 To num_of_entries
   Input #free_filehandle, hname(count_entry)
   Input #free_filehandle, hscore(count_entry)
   ' If the end of file is reached, exit the FOR loop.
   If EOF(free_filehandle) Then Exit For
   Next count_entry

Note how the 'count_entry' variable is used and how for each entry the name 
is stored FOLLOWED by the accompanying score. 'hname(1)' will flag the name 
with the top score, while 'hscore(1)' the top score. 
'hname(num_of_entries)' will flag the name with the lowest score, while 
'hscore(num_of_entries)' the lowest score in the high score table.

Don't forget now to close the file with:

   Close #free_filehandle 

All we need now is a loop that will display all these names and scores, 
nicely arranged in a table.

   Do

   ScreenLock
   ScreenSet workpage, workpage Xor 1

   Line (0,0)-(639,479), RGBA(0, 0, 0, 255), BF

   Draw String (285, 120), "TOP SCORES", RGBA(255,255, 255, 255)

   For count_entry As Integer = 1 To num_of_entries
   Draw String (270, 140 + count_entry * 12), hname(count_entry), RGBA(255,255, 255, 250-count_entry*10)
   Draw String (340, 140 + (count_entry) * 12), hscore(count_entry), RGBA(255,255, 255, 250-count_entry*10)
   Next count_entry

   Draw String (245, 400), "Press ESCAPE to exit", RGBA(255,255, 255, 220)

   workpage Xor = 1
   ScreenUnlock

   Sleep 10

   Loop Until MultiKey(SC_ESCAPE)

A simple DO...LOOP that ends when the user pushes ESCAPE.
I used Draw String to print the names and the scores. Another FOR loop is 
used to loop through the name and score entries, and to display them lower 
score under the next higher one (note how the Y position of the text to 
display is connected with the 'count_entry' variable - increase 12 to get 
more space between scores vertically). I also used a small trick to display 
each next score with lower translucency (last parameter in the RGBA 
function).

After placing all this code in the 'ReadHighScore' subroutine, you can 
compile it and the desired result will appear on the screen.

Now when we are done with the easy part of the problem, let's move onto 
writing new entries into our high score table.

I constructed the 'WriteHighScore' subroutine like this:

   Sub WriteHighScore (highscore_file As String, users_score As Integer)

Which means it will be called with a high scores table file and a score we 
want to input. If this score evaluates to be lower that the lowest in the 
high score table, no code will be executed.

This subroutine should start with the following code:

   Dim free_filehandle As Integer

   Dim startwrite As Integer

   free_filehandle = FreeFile

   Open highscore_file For Input As #free_filehandle

   For count_entry As Integer = 1 To num_of_entries
   Input #free_filehandle, hname(count_entry)
   Input #free_filehandle, hscore(count_entry)
   ' If the end of file is reached, exit the FOR loop.
   If EOF(free_filehandle) Then Exit For
   Next count_entry

   Close #free_filehandle

As you see it starts as the 'ReadHighScore' subroutine. In order to 
evaluate the user's score and alter the very high score table we need to 
open the file containing our high score entries and store them in 
appropriated variables. 'startwrite' variable will flag where the new entry 
is to be placed inside the high score table (on which position).
The code that follows should be opened with an IF clause that will execute 
the code inside it only if the user's score is higher than the lowest score 
in the high score table (naturally):

   If users_score > hscore(num_of_entries) Then

   For check_score As Integer = 1 To num_of_entries

   If users_score > hscore(check_score) Then
   InputName
   ' Record the position where the new score is
   ' to placed and exit FOR loop.
   startwrite = check_score
   Exit For
   End If

   Next check_score

The FOR loop 'goes' through the high score entries from the highest to the 
lowest, and when an entry with a lower score is found this is the place 
(flagged with 'startwrite' and 'check_score') where our new entry will be 
recorded. For example, in the first loop the program checks for 'hscore(1)' 
- the top score in the high score table. If the user's score ends up being 
higher than it, it's obvious the user's score is the new top score and 
'startwrite' needs to be 1. 'InputName' is a subroutine we'll create later, 
and inside it the user will be...inputting his name. :P

What follows is the 'nexus' of our routine, the code that places the new 
high score entry on the proper position, and bumps all the lower ones one 
position down.

Check the following code:

   If startwrite = num_of_entries Then
   hscore(startwrite) = users_score
   hname(startwrite) = playername
   Else
    
   For write_pos As Integer = (num_of_entries - 1) To startwrite Step -1
   hscore(write_pos + 1) = hscore(write_pos)
   hname(write_pos + 1) = hname(write_pos)
   Next write_pos
   hscore(startwrite) = users_score
   hname(startwrite) = playername
   End If

First condition checks if the new entry is the lowest (last) in the high 
score table. If this is the case, we don't need to bump down any entries 
with a lower score as there are none, but only replace the lowest score 
entry with the new one.
If this is NOT the case, a FOR loop is executed which loops from the lowest 
high score entry to the new high score entry (flagged with 'startwrite'), 
meaning, from bottom to top.

For example, if our high score table has 10 entries and the new entry needs 
to be placed on position 5, the loop goes from 9 to 5. When "write_pos" is 
9, values from 'hscore(9)' and 'hname(9)' are passed to 'hscore(9+1)' and 
'hname(9+1)'. When 'write_pos' is 8, values from 'hscore(8)' and 'hname(8)' 
are passed to 'hscore(8+1)' and 'hname(8+1)'. And so on.

After the FOR loop we need to input the new entry on its appropriate 
position (flagged with 'startwrite'), new entry being set with 
'users_score' and 'playername', where 'playername' will be inputted inside 
the 'InputName' sub.

The last thing in the 'WriteHighScore' sub we need to do is to store the 
new high score entries back to file:

   free_filehandle = FreeFile

   Open highscore_file For Output As free_filehandle
   For count_entry As Integer = 1 To num_of_entries
   Print #free_filehandle, hname(count_entry)
   Print #free_filehandle, hscore(count_entry)
   Next count_entry
   Close free_filehandle

Note how FOR OUTPUT is used and PRINT for writing data into external files.
After this I placed a 'ReadHighScore' call and closed with END IF as I find 
it good that a new high score table should display after a new entry has 
been inputted in it.
All we need now is to create the 'InputName' sub like this:

   Sub InputName

   ScreenSet workpage, workpage Xor 1
   ScreenSet 0,0
   Line (0,0)-(639,479), RGBA(0, 0, 0, 255), BF
   Locate 12, 17
   Input ; "Please input your name: ", playername

   End Sub

Of course, this will look totally different in your game. Perhaps you'll 
ask the player to input his/her name on a different place in the game (like 
when he/she starts a new game). Just have in mind you need one.

To test the routines just place...

   ReadHighScore "high_scores.dat"
   WriteHighScore "high_scores.dat", 4500
   End

...after first SCREENSET (outside subroutines). Change the second parameter 
with 'WriteHighScore' call to input different scores on different locations 
in the high score table. I'm sure you are aware that when calling 
'WriteHighScore' the second parameter mustn't be hard-coded with a static 
number, but with a variable in which you'll store player's score, whatever 
that may be in your case (ie. 'Player.Score').

What's next?

The only other things I wish to share regarding this issue is related to 
high score encryption and better name inputting routine. As both routines 
I'm using are not by me, I will only brush off them and provide them in an 
example program you can easily use for your own needs.
Encryption is done using two functions, 'neoENCpass' and 'neodeENCpass'. 
One for encryption and one for decryption. They are called with a string 
(high score entry string in our case) and password, password being any 
string you choose and the same must be used for encrypting and decrypting 
(of course).
Just after you retrieve an string entry from a file you decrypt it like 
this:

   Input #free_filehandle, hname(count_entry)
   neoENCdepass SAdd(hname(count_entry)), Len(hname(count_entry)), "yourpass"

With 'hscore' variables, being INTEGER, we need to use a temporary STRING 
variable which has to be decrypted and then pass its value to 'hscore'.
The only annoying feature of this method is the fact you need a separate 
source code to encrypt/decrypt your high score files, as the routines 
inside a project will work only if the high score file is previously 
encrypted. I provided a small program which does this encrypting for you. 
It is recommended you keep a backup of your high score file in a separate 
folder (I also provided this in the zip downloads), even if not encrypting 
it.
Instead of encryption you can use BINARY files, which I don't know how to 
use at this moment (don't have time to learn; I'm submitting the tutorial 
in the nick of time), and which also AREN'T the same as ENCRYPTION. 
Encrypted files using these routines  people can only decrypt if they know 
the password (well, most people), while BINARIES can be read by anyone 
having your source. Ah yes, when providing your source code to public be 
sure to change the encryption passwords inside it.
Anyway, you might not need or prefer encryption at all. But I personally 
like having my high score/script files encrypted so than not every Dick and 
Tom can change/read them with Notepad. Unencrypted high scores might kill 
the challenge to beat them with some players.
Name inputting routine I won't go describing as that's irrelevant. You have 
to code, read it. It's much better than plain INPUT (you can use it with 
custom font printing libraries) and allows you to limit the number of 
characters in the name. The routine was done by 'Ryan Szrama', and all 
thanks go to him.
Download the extended example (with encryption and better name inputting): 
http://lachie.phatcode.net/Downloads/Managing_A_High_Score_Table.zip

And that's it for this tutorial.
Until next time, have fun!

A tutorial written by Lachie D. (mailto CHR$(58) lachie13 CHR$(64) yahoo 
CHR$(46) com ; http://lachie.phatcode.net - The Maker Of Stuff)
