Version of Robotron arcade game using LPC1768, a Gameduino shield, a serial EEPROM (for high scores), two microswitch joysticks and two buttons plus a box to put it in. 20 levels of mayhem.

Dependencies:   25LCxxx_SPI CommonTypes Gameduino mbed

Revision:
0:5fa232ee5fdf
Child:
2:bb0f631a6068
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HighScoreTable.cpp	Tue Jun 04 20:16:33 2013 +0000
@@ -0,0 +1,259 @@
+/*
+ * SOURCE FILE : HighScoreTable.cpp
+ *
+ * Definition of class HighScoreTable.
+ * Maintains a table of high scores with a name and a score for each entry in the table.
+ *
+ * The table is stored in EEPROM at an address specified when you call the constructor.
+ * The table is structured as follows. This shows a table with a capacity of 3.
+ *
+ * Byte offset      Usage
+ * -----------      -----
+ * 0                Index of highest score in data that follows
+ * 1                Index of second highest score in data that follows
+ * 2                Index of third highest score in data that follows
+ *
+ * 3                First character of player's name
+ * 4                Second character of player's name
+ * 5                Third character of player's name
+ * 6                LSB of score (in BCD)
+ * 7                Byte 1 of score (in BCD)
+ * 8                Byte 2 of score (in BCD)
+ * 9                MSB of score (in BCD)
+ * 10               Unused
+ *
+ * 11               First character of player's name
+ * 12               Second character of player's name
+ * 13               Third character of player's name
+ * 14               LSB of score (in BCD)
+ * 15               Byte 1 of score (in BCD)
+ * 16               Byte 2 of score (in BCD)
+ * 17               MSB of score (in BCD)
+ * 18               Unused
+ *
+ * 19               First character of player's name
+ * 20               Second character of player's name
+ * 21               Third character of player's name
+ * 22               LSB of score (in BCD)
+ * 23               Byte 1 of score (in BCD)
+ * 24               Byte 2 of score (in BCD)
+ * 25               MSB of score (in BCD)
+ * 26               Unused
+ *
+ * So, assuming the capacity of the table is N, the first N bytes form an index which is used to locate
+ * items in the remaining N*8 bytes that follow. This is done so that inserting a new entry only involves
+ * overwriting one name/score record and updating the index. You don't have to re-write all the records
+ * that move down the table to make room for the new one.
+ *
+ */
+
+#include "HighScoreTable.h"
+
+/***************/
+/* CONSTRUCTOR */
+/***************/
+// Pass pointer to an I2C EEPROM which contains the high scores.
+HighScoreTable::HighScoreTable( I2CEEPROM *e ) :
+    eeprom( e )
+{
+}
+
+/**************/
+/* DESTRUCTOR */
+/**************/
+HighScoreTable::~HighScoreTable() {
+}
+
+/****************************************/
+/* VALIDATE EEPROM USED FOR HIGH SCORES */
+/****************************************/
+// Checks EEPROM used for high scores and
+// if any of it looks like nonsense it
+// rewrites the whole table with defaults.
+void HighScoreTable::ValidateEEPROM( void ) {
+  // Check if contents of EEPROM make sense.
+  // If not then rewrite EEPROM with defaults.
+  if( ! EEPROMValid() ) {
+    WriteEEPROMDefaults();
+  }
+}
+
+/**********************************************/
+/* DETERMINE POSITION OF A SCORE IN THE TABLE */
+/**********************************************/
+// Pass score in score.
+// Returns position in table (0 is top score).
+// If position returned is >= capacity of table then score is not high
+// enough to place in table.
+UInt8 HighScoreTable::GetPositionInTable( UInt32 score ) const {
+  // Look through table for a score less than the one passed.
+  PlayerName name;
+  UInt32 tableScore = (UInt32)0;
+  for( UInt8 i = 0; i < capacity; ++i ) {
+    Get( i, &name, &tableScore );
+    if( tableScore < score ) {
+      // Found a score that is less.
+      // Return index at which it was found.
+      return i;
+    }
+  }
+  // No score found that is less than the one passed.
+  // Return capacity of table to indicate not found.
+  return capacity;
+}
+
+/*********************************/
+/* ADD ENTRY TO HIGH SCORE TABLE */
+/*********************************/
+// Pass position in table to put entry in pos.
+// Pass name of player in name.
+// Pass score in score.
+void HighScoreTable::Add( UInt8 pos, const PlayerName *name, UInt32 score ) {
+    // Read the entire index, high scores and names out of EEPROM.
+    // Going to do manipulations in RAM to minimise the number of
+    // writes we need to do to EEPROM. Remember every time we write
+    // a single byte a whole page is written so might as well
+    // write a whole page in one go. Only drawback is more RAM
+    // is required.
+    UInt8 buffer[ memoryUsed ];
+    if( eeprom->ReadBytes( eepromAddress, buffer, memoryUsed ) ) {
+        // Fetch index for lowest score in the table.
+        UInt8 index = buffer[ capacity - 1 ];
+        // Make sure index is within range.
+        if( index < capacity ) {
+            // Point to section of buffer that contains name and score for
+            // lowest score.
+            UInt8 *address = buffer + capacity + ( index << 3 );
+            // Copy characters of name into buffer.
+            for( UInt8 i = 0; i < PlayerName::Length; ++i ) {
+                *address++ = name->Name[ i ];
+            }
+            // Copy bytes of score into buffer.
+            *((UInt32*)address) = score;
+            address += 4;
+            // Move all entries in the index below insertion point down one
+            // to make room for new entry.
+            for( UInt8 i = capacity - 1; i > pos; --i ) {
+                buffer[ i ] = buffer[ i - 1 ];
+            }
+            // Insert index of newly written record at insertion point.
+            buffer[ pos ] = index;
+            // Write the buffer back to EEPROM.
+            eeprom->WriteBytes( eepromAddress, buffer, memoryUsed ); 
+        }
+    }
+}
+
+/****************************/
+/* GET ENTRY FROM THE TABLE */
+/****************************/
+// Pass position to fetch from in pos.
+// Player name is returned in object pointed to by name.
+// Player score is returned in integer pointed to by score.
+void HighScoreTable::Get( UInt8 pos, PlayerName *name, UInt32 *score ) const {
+    // Write default values to name and score.
+  for( UInt8 i = 0; i < PlayerName::Length; ++i ) {
+    name->Name[ i ] = (UInt8)'X';
+  }
+  name->Name[ PlayerName::Length ] = 0;
+    *score = 0;
+  // Fetch index from EEPROM.
+  UInt8 index;
+    if( ! eeprom->ReadBytes( eepromAddress + pos, &index, 1 ) ) {
+        return;
+    }
+  // Point to appropriate part of data table.
+  UInt16 address = eepromAddress + capacity + ( index << 3 );
+  // Read out characters and store in name.
+    if( ! eeprom->ReadBytes( address, (UInt8*)name->Name, PlayerName::Length ) ) {
+        return;
+    }
+  name->Name[ PlayerName::Length ] = 0;
+    address += PlayerName::Length;
+  // Read out score.
+    eeprom->ReadBytes( address, (UInt8*)score, 4 );
+}
+
+/********************************/
+/* DETERMINE IF EEPROM IS VALID */
+/********************************/
+// Returns true if EEPROM is valid.
+bool HighScoreTable::EEPROMValid( void ) {
+  UInt8 b, b2;
+  // Check all entries in the index are within range and are unique.
+  for( UInt8 i = 0; i < capacity; ++i ) {
+    // Read byte from EEPROM.
+        if( ! eeprom->ReadBytes( eepromAddress + i, &b, 1 ) ) {
+            return false;
+        }
+    // Check index read is less than capacity.
+    if( b >= capacity ) {
+      return false;
+    }
+    // Check if any of the following bytes in the index have
+    // the same value.
+    for( UInt8 j = i + 1; j < capacity; ++j ) {
+            if( ! eeprom->ReadBytes( eepromAddress + j, &b2, 1 ) ) {
+                return false;
+            }
+      if( b == b2 ) {
+        return false;
+      }
+    }
+  }
+  // Check all entries in the data part of the table are valid.
+  UInt16 address = eepromAddress + capacity;
+  for( UInt8 i = 0; i < capacity; ++i ) {
+    // Check name consists only of uppercase letters.
+    for( UInt8 j = 0; j < PlayerName::Length; ++j ) {
+            // Read byte from EEPROM.
+            if( ! eeprom->ReadBytes( address++, &b, 1 ) ) {
+                return false;
+            }
+      if( ( b < PlayerName::MinChar ) || ( b > PlayerName::MaxChar ) ) {
+        return false;
+      }
+    }
+    // Check score consists only of valid BCD numbers.
+    for( UInt8 j = 0; j < 4; ++j ) {
+            // Read byte from EEPROM.
+            if( ! eeprom->ReadBytes( address++, &b, 1 ) ) {
+                return false;
+            }
+      if( ( ( b & 0x0F ) > 0x09 ) || ( ( b & 0xF0 ) > 0x90 ) ) {
+        return false;
+      }
+    }
+    // Skip over unused byte.
+    address++;
+  }
+  // EEPROM is valid
+  return true;
+}
+
+/****************************/
+/* WRITE DEFAULTS TO EEPROM */
+/****************************/
+// This may take a second or two to execute!
+void HighScoreTable::WriteEEPROMDefaults( void ) {
+    UInt8 buffer[ memoryUsed ];
+  // Write index with ascending integers.
+    UInt8 *ptr = buffer;
+  for( UInt8 i = 0; i < capacity; ++i ) {
+    *ptr++ =  i;
+  }
+  // Write data table with zero score entries.  
+  for( UInt8 i = 0; i < capacity; ++i ) {
+    // Write a name of "AAA".
+    for( UInt8 j = 0; j < PlayerName::Length; ++j ) {
+            *ptr++ = (UInt8)'A';
+    }
+        // Write a score of zero.
+    *((UInt32*)ptr) = 0;
+        ptr += 4;
+        // Write zero to unused byte.
+        *ptr++ = 0;    
+    }
+    // Write the buffer to EEPROM.
+    eeprom->WriteBytes( eepromAddress, buffer, memoryUsed ); 
+}