Notifications
Clear all

Scoreboard/Leaderboard

9 Posts
2 Users
0 Reactions
1,141 Views
(@bwinter)
Member
Joined: 2 years ago
Posts: 3
Topic starter  

I'm working on an Arduino-based game for kids (somewhat like an "escape-room" type challenge). It'll be a time-based challenge, with the duration simply measured by a start/stop trigger (to derive the CurrentScore). I'd also like to be able to track scoring (HighScore), and possibly a leader-board.  Tracking the HighScore is rather simple (I think):  If CurrentScore < HighScore, then CurrentScore = HighScore.

However, I'm stumped on how to implement the following:

  • I'd like to track the player-initials (and possibly date time stamp) of the high score.  Obviously, I would have an input device (such as a TFT touchscreen) that I would use to both input initials and display the score.  I guess I could simply expand the code above to also switch CurrentScoreInitials with HighScoreInitials--but I'm wondering if there's a better method.
  • I'd also like to possibly keep a leaderboard (of the top scores), keeping both the time, initials and date time stamp.  This would be used to display/track the top 5-10 scores, sorted.

Is there any elegant method to track this information?  I'm somewhat aware of how to "data-log" to an SD card--but not sure if this is the right path to investigate.

My programming-level is probably low-level intermediate--I have a good understanding of the basic Arduino programming, but I'm wondering if there are other Arduino functions out there that I simply haven't been exposed to yet.


This topic was modified 2 years ago by bwinter

   
Quote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

bwinter I think your estimate of your programming creds may be optimistic given you said

 Tracking the HighScore is rather simple (I think):  If CurrentScore < HighScore, then CurrentScore = HighScore.

That is two errors. It should be

  if CurrentScore > HighScore then HighScore = CurrentScore

Not to worry, I have over 50 years experience and am autistic so make errors like that all the time.

You probably will need to add some more reliable (regular SD cards are NOT reliable) semi-permanent memory. An inexpensive small USB memory stick, EEPROM, and probably some choices I have overlooked. SD can be an option if it is the special High Wear types used for security cameras.

All your other requirements are easy enough and your design choices are possible but may not be ideal in that TFT screens and handwriting initials is more art than science. A mini keyboard even on screen is a more reliable method and obviously any time keeping is trivial for an MCU/MPU.

A 'leader board' is a classic Data Processing problem with several solutions. Since it is only a small number, an array that is looped over comparing existing numbers to a new entry and copying from either old or newest to the new array is a brute force approach, while some sort of in memory sort is another. I have a long history with this and used a 3rd approach I call 'Named Doubly Linked Lists. it is modelled after a certain OS list manager but I got rid of all my code after I retired thinking I would never use it again so although I recently started to resurrect it, it is far from complete and will take me months to years to complete as I can only work a few days a week for a few hours a day due to health issues. I recommend either doing an array type approach or looking for a sort library.

Good luck.

 

 

 

 


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
(@bwinter)
Member
Joined: 2 years ago
Posts: 3
Topic starter  

Thanks for the reply!

Actually since this would be a speed-based challenge, the lowest time would be the "high score" (as far as the leaderboard goes).  So I would think that the < is the appropriate operator--but you're correct on the second mistake.  I actually haven't written this sketch yet (let alone tested)--it was just an off-the-cuff example of what I was thinking.

As far as inputting the initials, I was planning to use some type of TFT keyboard and certainly not trying to interpret hand-written letters (that's nowhere in my realm of possibility).

Regarding the array:  that was something I was considering, but my entry-level array-knowledge in Arduino is generally limited to using an array to "set a bank of pins to high/low."  But 1) I wasn't sure if arrays could be sorted (I haven't come across that yet), and 2) so far I've only thought an array contain a single variable-type (such as TIME), so it wasn't clear how I'd link to the other arrays (such as INITIALS) after any sort.



   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@bwinter Please use the Reply link at the bottom right so the poster gets an email to notify them.

I saw SCORE not time. Some sort of cultural difference?

You could still use a TFT, just write out the letters and then when the user taps a letter the touch API should be good enough to give the correct answer, but of course you could do an 'did you mean X? followed by a large YES and NO tappable area. OR several other paradigms are possible.

An array is just a chunk of memory, what it contains is up to the programmer. Often it is just a pointer to a data structure. You don't sort the array, you create a second array, then loop over the first array (already sorted from earlier) comparing that data to the now best score data and when you find where it logically belongs, put that address (of the new best score) into the output array. then finish copying the next n-1 entries. It's -1 since the input might be 5+1(new) being copied into 5 where 5 is whatever you decide is the number of leaders to keep.

Clear?

I will prepare a sample sketch demonstrating these techniques.


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@bwinter As I promised. Here is Ver 1 (there will be more) of a sketch to give you an idea about structs and arrays. This is basic, I will fancy it up a bit in a few minutes so stay tuned for more.

struct sLeaderBoard {
  int score = 0;
  char init1 = ' ';
  char init2 = ' ';
};

struct sLeaderBoard LeaderBoard[5];

void setup() {
  Serial.begin(115200);
  while (!Serial.availableForWrite()) {}

// There are several ways to initialize an array, this is one
// Notice array index and members can be out of order
  LeaderBoard[1].score = 12000;
  LeaderBoard[0].init1 = 'M';
  LeaderBoard[1].init2 = 'E';

  LeaderBoard[0].init2 = 'Y';
  LeaderBoard[1].init1 = 'N';
  LeaderBoard[0].score = 17000;
}

void loop() {
  // put your main code here, to run repeatedly:
  int numLeaders = 2;
  int indxLeaders = 0;

  for (indxLeaders = 0; indxLeaders < numLeaders; ++indxLeaders){
    Serial.print(" Initials="); 
    Serial.print(LeaderBoard[indxLeaders].init1);
    Serial.print(LeaderBoard[indxLeaders].init2);
    Serial.print(" score=");
    Serial.println(LeaderBoard[indxLeaders].score);
  }
  delay(10000);
}

 

 

 


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
(@bwinter)
Member
Joined: 2 years ago
Posts: 3
Topic starter  

@zander 

This is making sense.  I haven't been exposed to STRUCT yet, so I think this is pointing me in the right direction for what functions I need to study more.  What you've provided above is enough for me to keep moving forward--thanks!



   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@bwinter I am working on a little more. I am old so work slow.


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@bwinter I think this example will give you a leg up with arrays of structures and maintaining a leaderboard. This is one solution, there are many more. Some folks may jump in and give you their take on how to do this. I have no desire to compete so it will be up to you to figure out what to do with any other suggestions. Most of the code is for testing, I leave it to you to extract the core ideas. Those ideas are how to create a leaderboard data structure using an array of structures, how to add new scores and how to print the leaderboard. You will need to change the Serial prints to whatever is needed for your leaderboard display. Follow the concepts here and do what I call 'divide and conquer'.

Good luck

struct sLeaderBoard {
  int score  = 0;
  char Init1 = ' ';
  char Init2 = ' ';
};

// A typedef is convenient when you need to create multiple versions/copies of a same datatype 
//   it is the same as int or long or char
typedef struct sLeaderBoard sLeaderBoard_t;

const int numLeaders = 3;   // many ways of doing this but this is a simple 3 entry leaderboard to test concepts

sLeaderBoard_t OldLeaderBoard[numLeaders];
sLeaderBoard_t NewLeaderBoard[numLeaders];
sLeaderBoard_t CurrentPlayer;

void setup() {
  
  Serial.begin(115200);
  while (Serial.available()) Serial.read(); // flush Serial so wait for any key will wait
  while (!Serial.availableForWrite()) {}    // Wait until Serial port is ready for output
}

void loop() {

  WaitForAnyKey("Press any key to continue");

  CreateTestPlayer(12000, 'Y', '2' );
  DetermineNewLeaders();              // now first entry will end up 2nd
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();

  CreateTestPlayer( 8000, 'Z', '3' );
  DetermineNewLeaders();              // now second entry will end up 3rd
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();

  CreateTestPlayer(17000, 'X', '1' );
  DetermineNewLeaders();              // now first entry
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();

  delay(5000);
  Serial.printf("\n\nStart test1 Current too small for top 3");
  CreateTestPlayer(7000, 'A', 'a');   // current player = 7000 (less than lowest)
  DetermineNewLeaders();    //  s/b old = 17000, 12000, 8000 // then new = 17000, 12000, 8000
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();
  delay(5000);

  Serial.printf("\n\nStart test2 Current goes between 12000 and 8000");
  CreateTestPlayer(9000, 'B', 'b');   // current player = 9000 should replace 8000
  DetermineNewLeaders();    //  s/b old = 17000, 12000, 8000  // then new = 17000, 12000, 9000
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();
  delay(5000);

  Serial.printf("\n\nStart test3 Current goes between 17000 and 12000");
  CreateTestPlayer(13000, 'C', 'c');    // current player = 13000 should push 9000 off and go between 12000 and 17000
  DetermineNewLeaders();    //  s/b old = 17000, 12000, 9000  // then new = 17000, 13000, 12000
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();
  delay(5000);

  Serial.printf("\n\nStart test4 Current is highest score");
  CreateTestPlayer(18000, 'D', 'd');    // current player = 18000 should push 12000 off and is new highest at 18000
  DetermineNewLeaders();    //  s/b old = 17000, 13000, 12000 // then new = 18000, 17000, 13000
  Printall();
  CopyNewLeaderBoardToOldLeaderBoard();
  delay(5000);
}

void WaitForAnyKey(String Msg){

  Serial.println(" ");
  Serial.println(Msg);
  while(!Serial.available()) {};  // wait for a key
  Serial.read();                  // read the key and throw away
}

void CreateTestPlayer(int Score, char Init1, char Init2){

  CurrentPlayer.score = Score;
  CurrentPlayer.Init1 = Init1;
  CurrentPlayer.Init2 = Init2 ;

  Serial.printf("\n\nAdding %c%c %i\n", Init1, Init2, Score);
}

void CopyNewLeaderBoardToOldLeaderBoard(){

  for (int indxLeaders = 0; indxLeaders < numLeaders; ++indxLeaders){
    OldLeaderBoard[indxLeaders] = NewLeaderBoard[indxLeaders];  // Allow compiler do job of copying all fields of the structure
  }
}

void DetermineNewLeaders(){
  int indxOld = 0;  // loop over OldLeaderBoard with manual control in order to insert a higher score
  int indxNew = 0;  // loop over NewLeaderBoard in for loop as this is the target and must be numLeaders in size

  for (int indxNew = 0; indxNew < numLeaders; indxNew++){
    if (OldLeaderBoard[indxOld].score > CurrentPlayer.score){   // Is Old greater than Current
      NewLeaderBoard[indxNew] = OldLeaderBoard[indxOld];        // YES it is so copy Old to New
      indxOld++;                                                // Advance the Old index
    }
    else {                                                      // NO it is not
      NewLeaderBoard[indxNew] = CurrentPlayer;                  // so copy Current to New
      CurrentPlayer = {};                                       // Make Current smallest so it is not copied again
    }
  }
}

void Printall(){

  Serial.printf("\nOld Leader Board");
  for (int indxLeaders = 0; indxLeaders < numLeaders; indxLeaders++){
    Serial.printf("\nInitials=%c%c score=%i", 
      OldLeaderBoard[indxLeaders].Init1, OldLeaderBoard[indxLeaders].Init2, OldLeaderBoard[indxLeaders].score);
  }

  Serial.printf("\n\nNew Leader Board");
  for (int indxLeaders = 0; indxLeaders < numLeaders; indxLeaders++){
    Serial.printf("\nInitials=%c%c score=%i", 
      NewLeaderBoard[indxLeaders].Init1, NewLeaderBoard[indxLeaders].Init2, NewLeaderBoard[indxLeaders].score);
  }
}

 

 

 


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@bwinter Hmm, I don't see my post and code, I see it as all blank. Here is the original post and now I will attach a zip file.

====

 


First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote