/* RunLengthEncoding.java */
package Project1;
/**
* The RunLengthEncoding class defines an object that run-length encodes an
* Ocean object. Descriptions of the methods you must implement appear below.
* They include constructors of the form
*
* public RunLengthEncoding(int i, int j, int starveTime);
* public RunLengthEncoding(int i, int j, int starveTime,
* int[] runTypes, int[] runLengths) {
* public RunLengthEncoding(Ocean ocean) {
*
* that create a run-length encoding of an Ocean having width i and height j,
* in which sharks starve after starveTime timesteps.
*
* The first constructor creates a run-length encoding of an Ocean in which
* every cell is empty. The second constructor creates a run-length encoding
* for which the runs are provided as parameters. The third constructor
* converts an Ocean object into a run-length encoding of that object.
*
* See the README file accompanying this project for additional details.
*/
class RunLengthEncoding {
/**
* Define any variables associated with a RunLengthEncoding object here.
* These variables MUST be private.
*/
private DList2 list;
private long sizeOfRun;
private int width;
private int height;
private int starveTime;
/**
* The following methods are required for Part II.
*/
/**
* RunLengthEncoding() (with three parameters) is a constructor that creates
* a run-length encoding of an empty ocean having width i and height j,
* in which sharks starve after starveTime timesteps.
* @param i is the width of the ocean.
* @param j is the height of the ocean.
* @param starveTime is the number of timesteps sharks survive without food.
*/
public RunLengthEncoding(int i, int j, int starveTime) {
this.list = new DList2();
this.list.insertFront(TypeAndSize.Species.EMPTY, i*j);
this.sizeOfRun = 1;
this.width = i;
this.height = j;
this.starveTime = starveTime;
}
/**
* RunLengthEncoding() (with five parameters) is a constructor that creates
* a run-length encoding of an ocean having width i and height j, in which
* sharks starve after starveTime timesteps. The runs of the run-length
* encoding are taken from two input arrays. Run i has length runLengths[i]
* and species runTypes[i].
* @param i is the width of the ocean.
* @param j is the height of the ocean.
* @param starveTime is the number of timesteps sharks survive without food.
* @param runTypes is an array that represents the species represented by
* each run. Each element of runTypes is Ocean.EMPTY, Ocean.FISH,
* or Ocean.SHARK. Any run of sharks is treated as a run of newborn
* sharks (which are equivalent to sharks that have just eaten).
* @param runLengths is an array that represents the length of each run.
* The sum of all elements of the runLengths array should be i * j.
*/
public RunLengthEncoding(int i, int j, int starveTime,
TypeAndSize.Species[] runTypes, int[] runLengths) {
this.list = new DList2();
this.width = i;
this.height = j;
this.starveTime = starveTime;
if(runTypes.length != runLengths.length){
System.out.println("lengths are unequal");
}else{
for(int index=0; index < runTypes.length; index++){
this.list.insertFront(runTypes[index], runLengths[index]);
this.sizeOfRun++;
}
}
}
/**
* restartRuns() and nextRun() are two methods that work together to return
* all the runs in the run-length encoding, one by one. Each time
* nextRun() is invoked, it returns a different run (represented as a
* TypeAndSize object), until every run has been returned. The first time
* nextRun() is invoked, it returns the first run in the encoding, which
* contains cell (0, 0). After every run has been returned, nextRun()
* returns null, which lets the calling program know that there are no more
* runs in the encoding.
*
* The restartRuns() method resets the enumeration, so that nextRun() will
* once again enumerate all the runs as if nextRun() were being invoked for
* the first time.
*
* (Note: Don't worry about what might happen if nextRun() is interleaved
* with addFish() or addShark(); it won't happen.)
*/
/**
* restartRuns() resets the enumeration as described above, so that
* nextRun() will enumerate all the runs from the beginning.
*/
publicprivate void restartRuns() {
this.sizeOfRun = this.list.size;
}
/**
* nextRun() returns the next run in the enumeration, as described above.
* If the runs have been exhausted, it returns null. The return value is
* a TypeAndSize object, which is nothing more than a way to return two
* integers at once.
* @return the next run in the enumeration, represented by a TypeAndSize
* object.
*/
publicprivate TypeAndSize nextRun() {
TypeAndSize obj = null;
if(this.sizeOfRun > 0){
obj = this.list.nTh(this.sizeOfRun);
this.sizeOfRun--;
}
return obj;
}
/**
* toOcean() converts a run-length encoding of an ocean into an Ocean
* object. You will need to implement the three-parameter addShark method
* in the Ocean class for this method's use.
* @return the Ocean represented by a run-length encoding.
*/
public Ocean toOcean() {
Ocean sea = new Ocean(this.width, this.height);
TypeAndSize obj = null;
TypeAndSize singleArray[] = new TypeAndSize[this.width*this.height];
int savedIndex = 0;
/*Convert Doubly linked ist to 1d array */
while((obj = nextRun()) != null){
for(int index = savedIndex; index < (savedIndex + obj.runLength); index++){
/* TypeAndSize class is just data storage but not an abstraction */
singleArray[index].runLength = obj.runLength;
singleArray[index].type = obj.type;
}
savedIndex += obj.runLength;
}
/* Convert 1d array to 2d array Ocean */
for(int index =0; index < singleArray.length; index++){
if(singleArray[index].type == TypeAndSize.Species.EMPTY){
// Do nothing because ocean is created with empty objects.
}else if(singleArray[index].type == TypeAndSize.Species.FISH){
sea.addFish(index/this.width, Utility.mod(index, this.width));
}else if(singleArray[index].type == TypeAndSize.Species.SHARK){
sea.addShark(index/this.width, Utility.mod(index, this.width), 0);
}
}
this.restartRuns();
return sea;
}
}