Home arrow Java arrow Page 5 - Storing and Retrieving Data
JAVA

Storing and Retrieving Data


Storing bytes of data locally on a device equipped for the Mobile Internet Device Profile (MIDP) is easy. MIDP allows you to store arrays of bytes. But what if the data you need to store isn't in the form of bytes? And how can you make the data small enough so that storing it on a device with a relatively small amount of memory is not a problem? That's where this article comes in. It is excerpted from the book J2ME Games with MIDP2, written by Carol Hamer (Apress, 2004; ISBN: 1590593820).

Author Info:
By: Apress Publishing
Rating: 4 stars4 stars4 stars4 stars4 stars / 24
May 12, 2005
TABLE OF CONTENTS:
  1. · Storing and Retrieving Data
  2. · Serializing More Complex Data Using Streams
  3. · Using Data Types and Byte Arithmetic
  4. · Applying Data Storage to a Game
  5. · Converting an array of bytes into a dungeon
  6. · Creating the Complete Example Game
  7. · DungeonManager.java
  8. · Doors and keys

print this article
SEARCH DEVARTICLES

Storing and Retrieving Data - Converting an array of bytes into a dungeon
(Page 5 of 8 )

Listing 5-5. BoardDecoder.java

  package net.frog_parrot.dungeon;
 
import javax.microedition.lcdui.*;
  import javax.microedition.lcdui.game.*;
 
import net.frog_parrot.util.DataConverter;
  /**
    * This class contains the data for the map of the dungeon.
    *
   
* @author Carol Hamer
   
*/
  public class BoardDecoder {
    //-----------------------------------------------------    // fields
  /**
   
* The coordinates of where the player starts on the map
    * in terms of the array indices.
   
*/
  private int[] myPlayerSquare;
   
/**
   
* The coordinates of the goal (crown).
    
*/
  private int[] myGoalSquare;
 
/**
    * The coordinates of the doors.
   
the there should be two in arow of each color,
   
* following the same sequence as the keys.
   
*/
  private int[][] myDoors;
 
/**
   
* The coordinates of the Keys.
   
they should be of each color,
   
* following the same sequence as the doors.
      */
  private int[][] myKeys;
  /**
   
* The coordinates of the stone walls of the maze,
   
* encoded bit by bit.
   
*/
  private TiledLayer myLayer;
 
/**
    * The data in bytes that gives the various boards.
   
* This was created using EncodingUtils...
   
* This is a two-dimensional array: Each of the four
   
* main sections corresponds to one of the four
   
* possible boards.
    */
  private static byte[][] myData = {
      { 0, 0, -108, -100, -24, 65, 21, 58, 53, -54, -116, -58, -56,
      -84, 115, -118,
      -1, -1, -128, 1, -103, -15, -128, 25, -97, -127, -128, 79, -14,
      1, -126, 121, -122, 1, -113, -49, -116, 1, -100, -3, -124, 5,
      -25, -27, -128, 1, -1, -1 },
      { 0, 1, 122, 90, -62, 34, -43, 72, -59, -29, 56, -55, 98, 126,
      -79, 61,
      -1, -1, -125, 1, -128, 17, -26, 29, -31, 57, -72, 1, -128, -51,
      -100, 65, -124, 57, -2, 1, -126, 13, -113, 1, -97, 25, -127,
      -99, -8, 1, -1, -1 },
      { 0, 2, 108, -24, 18, -26, 102, 30, -58, 46, -28, -88, 34,
      -98, 97, -41,
      -1, -1, -96, 1, -126, 57, -9, 97, -127, 69, -119, 73, -127,
      1, -109, 59, -126, 1, -26, 103, -127, 65, -103, 115, -127,
      65, -25, 73, -128, 1, -1, -1 },
     
{ 0, 3, -114, 18, -34, 27, -39, -60, -76, -50, 118, 90, 82,
      -88, 34, -74,
      -1, -1, -66, 1, -128, 121, -26, 125, -128, -123, -103, 29,
      -112, 1, -109, 49, -112, 1, -116, -31, -128, 5, -122, 5,
      -32, 13, -127, -51, -125, 1, -1, -1 },
 
};

  //-------------------------------------------------------  // initialization

  /**
    * Constructor fills data fields by interpreting
   
* the data bytes.
    */
  public BoardDecoder(int boardNum) throws Exception {
    // you start by selecting the two dimensional
    // array corresponding to the desired board:
    byte[] data = myData[boardNum];
    // The first two bytes give the version number and
    // the board number, but you ignore them because
    // they are assumed to be correct.
    // The third byte of the first array is the first one
    // you read: it gives the player's starting coordinates:
    myPlayerSquare = DataConverter.decodeCoords(data[2]);
    // the next byte gives the coordinates of the crown:  
    myGoalSquare = DataConverter.decodeCoords(data[3]);
    // the next 4 bytes give the coordinates of the keys: 
    myKeys = new int[4][];
    for(int i = 0; i < myKeys.length; i++) {
      myKeys[i] = DataConverter.decodeCoords(data[i + 4]);
    }
    // the next 8 bytes give the coordinates of the doors:     myDoors = new int[8][];
    for(int i = 0; i < myDoors.length; i++) {
      
myDoors[i] = DataConverter.decodeCoords(data[i + 8]);
    }
    // now you create the TiledLayer object that is the
    // background dungeon map:
   
myLayer = new TiledLayer(16, 16,
          Image.createImage("/images/stone.png"), 
          DungeonManager.SQUARE_WIDTH, DungeonManager.SQUARE_WIDTH);
   
// now you call an internal utility that reads the array
    // of data that gives the positions of the blocks in the
    // walls of this dungeon:
    decodeDungeon(data, myLayer, 16);
  }
  //-------------------------------------------------------  // get/set data
  /**
    * @return the number of boards currently stored in 
      * this class.
    */
  public static int getNumBoards() {
   
return(myData.length);
  }
 
/**
    * get the coordinates of where the player starts on the map
   
* in terms of the array indices.
    */
    public int[] getPlayerSquare() {
      
return(myPlayerSquare);
    }
  /**
    * get the coordinates of the goal crown
   
* in terms of the array indices.
    */
  public int[] getGoalSquare() {
   
return(myGoalSquare);
  }
 
/**
   
* get the tiled layer that gives the map of the dungeon.
    */
  public TiledLayer getLayer() {
   
return(myLayer);
  }
  /**
    * Creates the array of door sprites. (call this only once to avoid
   
* creating redundant sprites).
    */
 
DoorKey[] createDoors() {
    DoorKey[] retArray = new DoorKey[8];
    for(int i = 0; i < 4; i++) {
     
retArray[2*i] = new DoorKey(i, false, myDoors[2*i]);
     
retArray[2*i + 1] = new DoorKey(i, false, myDoors[2*i + 1]);
    }
    return(retArray);
 
}
 
/**
    * Creates the array of key sprites. (call this only once to avoid
      * creating redundant sprites.)
    */
 
DoorKey[] createKeys() {
    DoorKey[] retArray = new DoorKey[4];
    for(int i = 0; i < 4; i++) {
     
retArray[i] = new DoorKey(i, true, myKeys[i]);
    }
    return(retArray);
 
}
  //-------------------------------------------------------  // decoding utilities
  /**
   
* Takes adungeon given as a byte array and uses it
   
* to set the tiles of atiled layer.
    *
   
* The TiledLayer in this case is a16x16 grid
      * in which each square can be either blank
   
* (value of 0) or can be filled with a stone block
   
* (value of 1). Therefore each square requires only 
    * one bit of information. Each byte of data in
   
* the array called "data" records the frame indices
    * of eight squares in the grid.
    */
 
private static void decodeDungeon(byte[] data, TiledLayer dungeon,
        int offset) throws Exception {
    if(data.length + offset < 32) {
      throw(new Exception(
                "BoardDecoder.decodeDungeon-->not enough data!!!"));
      }
      // a frame index of zero indicates a blank square
      // (this is always true in a TiledLayer).
      // This TiledLayer has only one possible (nonblank)
      // frame, so a frame index of 1 indicates a stone block
      int frame = 0;
      // Each of the 32 bytes in the data array records
      // the frame indices of eight block in the 16x16
      // grid. Two bytes give one row of the dungeon,
      // so you have the array index go from zero to 16
      // to set the frame indices for each of the 16       
rows.
      for(int i = 0; i < 16; i++) {
     
// The flag allows you to look at each bit individually
      // to determine if it is one or zero. The number 128
      // corresponds to the highest bit of a byte, so you
      // start with that one.
      int flag = 128;
      // Here you check two bytes at the same time
      // (the two bytes together correspond to one row
      // of the dungeon). You use a loop that checks
      // the bytes bit by bit by performing a bitwise
      // and (&) between the data byte and a flag:
      for(int j = 0; j < 8; j++) {
       
if((data[offset + 2*i] & flag) != 0) {
          frame = 1;
        } else {
         
frame = 0;
        } dungeon.setCell(j, i, frame);
        if((data[offset + 2*i + 1] & flag) != 0) {
         
frame = 1;
        } else {
         
frame = 0;
        }
        dungeon.setCell(j + 8, i, frame);
        // move the flag down one bit so you can
        // check the next bit of data on the next pass
        // through the loop:
        flag = flag >> 1;
     
}
    }
  }

}

In case you’re wondering where the byte arrays in the previous class came from, Listing 5-6 shows the utility class I used to encode them. Keep in mind that this class is merely a tool I used to create the data for the game. Once the data has been created, this class is no longer used. It would certainly not be distributed to users with the game. Listing 5-6 shows EncodingUtils.java.

Listing 5-6. EncodingUtils.java

  package net.frog_parrot.dungeon;
 
import net.frog_parrot.util.DataConverter;
  /**
    * This class contains the data for the map of the dungeon.
   
* This is autility class that allows a developer to write
   
* the data for aboard in a simple format, then this class
   
* encodes the data in aformat that the game can use.
    * 
   
* note that the data that this class encodes is hard-coded.
   
* that is because this class is intended to be used only a
   
* few times to encode the data. Once the board data has been
   
* encoded, it never needs to be encoded again. The encoding
   
* methods used in this class could be generalized to be used
   
* to create aboard editor that would allow a user to easily
   
* create new boards, but that is an exercise for another day...
    *
   
* @author Carol Hamer
   
*
   public class EncodingUtils {
   
//-----------------------------------------------------    // fields
  /**
   
* data for which squares are filled and which are blank.
    * 0 = empty
    * 1 = filled
    */ 
  private int[][] mySquares = {
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
    { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1 },
    { 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1 },
    { 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   
{ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1 },
   
{ 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 },
   
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1 },
   
{ 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },
   
{ 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
   
{ 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1 },
   
{ 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1 },
   
{ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
  };
  /**
   
* The coordinates of where the player starts on the map
    * in terms of the array indices.
   
*/
  private int[] myPlayerSquare = { 7, 10 };
 
/**
   
* The coordinates of the goal (crown).
   
*/
  private int[] myGoalSquare = { 5, 10 };
  //-------------------------------------------------------
  // get/set data
 
/**
    * Creates the array of door sprites. (call this only once to avoid
   
* creating redundant sprites).
    */
  int[][] getDoorCoords() {
    int[][] retArray = new int[8][];
   
for(int i = 0; i < retArray.length; i++) {
      
retArray[i] = new int[2];
    }
    // red
    retArray[0][0] = 12;
    retArray[0][1] = 5;
    retArray[1][0] = 14;
    retArray[1][1] = 3;
    // green
    retArray[2][0] = 3;
    retArray[2][1] = 8;
   
retArray[3][0] = 12;
   
retArray[3][1] = 9;
   
// blue
   
retArray[4][0] = 6;
   
retArray[4][1] = 2;
   
retArray[5][0] = 7;
   
retArray[5][1] = 14;
   
// yellow
   
retArray[6][0] = 11;
   
retArray[6][1] = 1;
   
retArray[7][0] = 3;
   
retArray[7][1] = 13;
   
return(retArray);
 
}
 
/** 
    * Creates the array of key sprites. (call this only once to avoid
   
* creating redundant sprites.)
    */
 
int[][] getKeyCoords() {
    
int[][] retArray = new int[4][];
   
for(int i = 0; i < retArray.length; i++) {
     
retArray[i] = new int[2];
    }
    // red
    retArray[0][0] = 12;
    retArray[0][1] = 2;
    // green
    retArray[1][0] = 2;
    retArray[1][1] = 2;
    // blue
    retArray[2][0] = 13;
    retArray[2][1] = 5;
    // yellow
    retArray[3][0] = 4;
    retArray[3][1] = 8;
    return(retArray);
 
}
 
//-------------------------------------------------------  // encoding / decoding utilities
  /**
   
* Encodes the entire dungeon.
    */
 
byte[][] encodeDungeon() {
    byte[][] retArray = new byte[2][];
    retArray[0] = new byte[16];
    // the first byte is the version number:
    retArray[0][0] = 0;
    // the second byte is the board number:
    retArray[0][1] = 0;
    // the player's start square:
    retArray[0][2] = DataConverter.encodeCoords(myPlayerSquare);
    // the goal (crown) square:
    retArray[0][3] = DataConverter.encodeCoords(myGoalSquare);
    //encode the keys:
    int[][] keyCoords = getKeyCoords();
    for(int i = 0; i < keyCoords.length; i++) {
     
retArray[0][i + 4] = DataConverter.encodeCoords(keyCoords[i]);
    }
    //encode the doors:
    int[][] doorCoords = getDoorCoords();
    for(int i = 0; i < doorCoords.length; i++) {
     
retArray[0][i + 8] = DataConverter.encodeCoords(doorCoords[i]);
    }
    //encode the maze:
    try {
     
retArray[1] = encodeDungeon(mySquares);
    } catch(Exception e) {
     
e.printStackTrace();
    } return(retArray);
 
}
  /**
    * Takes adungeon given in terms of an array of ones and zeros
   
* and turns it into an array of bytes.
      * WARNING: the array MUST BE 16x16.
    */
  static byte[] encodeDungeon(int[][] dungeonMap) throws Exception {
    if((dungeonMap.length != 16) || (dungeonMap[0].length != 16)) { 
      throw(new Exception("EncodingUtils.encodeDungeon-->must be 16x16!!!"));
    }
    byte[] retArray = new byte[32];
    for(int i = 0; i < 16; i++) {
      retArray[2*i] = DataConverter.encode8(dungeonMap[i], 0);
      
retArray[2*i + 1] = DataConverter.encode8(dungeonMap[i], 8);
    }
    return(retArray);
  }
  //-------------------------------------------------------  // main prints the bytes to standard out.
  // (note that this class is not intended to be run as a MIDlet)
 
/**
   
* Prints the byte version of the board to standard out.     */
  public static void main(String[] args) {
   
try {
      EncodingUtils map = new EncodingUtils();
      byte[][] data = map.encodeDungeon();  
      System.out.println("EncodingUtils.main-->dungeon encoded");
      System.out.print("{\n  " + data[0][0]);
      for(int i = 1; i < data[0].length; i++) {
       
System.out.print(", " + data[0][i]);
      }
      for(int i = 1; i < data[1].length; i++) {
       
System.out.print(", " + data[1][i]);
      }
      System.out.println("\n};");
   
} catch(Exception e) {
      e.printStackTrace();
    }
  }

}


blog comments powered by Disqus
JAVA ARTICLES

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials