Query on clarification about toOcean() method - java

This query can be answered only after going through the history of previous query where the Part I of the solution is discussed.
Following is the solution, I wrote for Part IIa and PartIIb, I need clarification before writing PartIIc i.e., toOcean() method.
/* 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 int 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.sizeOfRun = 0;
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.
*/
public void restartRuns() {
this.sizeOfRun = 0;
}
/**
* 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.
*/
public TypeAndSize nextRun() {
TypeAndSize obj = null;
if(this.sizeOfRun > 0){
obj = this.list.nTh(this.sizeOfRun);
this.sizeOfRun--;
}
return obj;
}
}
==========
/* DList2.java */
package Project1;
/**
* A DList2 is a mutable doubly-linked list. Its implementation is
* circularly-linked and employs a sentinel (dummy) node at the sentinel
* of the list.
*/
class DList2 {
/**
* sentinel references the sentinel node.
*
* DO NOT CHANGE THE FOLLOWING FIELD DECLARATIONS.
*/
protected DListNode2 sentinel;
protected long size;
/* DList2 invariants:
* 1) sentinel != null.
* 2) For any DListNode2 x in a DList2, x.next != null.
* 3) For any DListNode2 x in a DList2, x.prev != null.
* 4) For any DListNode2 x in a DList2, if x.next == y, then y.prev == x.
* 5) For any DListNode2 x in a DList2, if x.prev == y, then y.next == x.
* 6) size is the number of DListNode2s, NOT COUNTING the sentinel
* (denoted by "sentinel"), that can be accessed from the sentinel by
* a sequence of "next" references.
*/
/**
* DList2() constructor for an empty DList2.
*/
public DList2() {
this.sentinel = new DListNode2();
this.sentinel.next = this.sentinel;
this.sentinel.prev = this.sentinel;
this.size = 0;
}
/**
* insertFront() inserts an object of type TypeAndSizeAndHungerAndStarveTime at the front of a DList2.
*/
void insertFront(TypeAndSize.Species runType, int runLength) {
DListNode2 newNode = new DListNode2(runType, runLength);
newNode.next = this.sentinel.next;
this.sentinel.next.prev = newNode;
this.sentinel.next = newNode;
this.sentinel.next.prev = this.sentinel;
this.size++;
}
/**
* nTh() returns the nTh node
* #param nTh
* #return
*/
TypeAndSize nTh(int nTh){
DListNode2 node = this.sentinel.prev;
int index = 1;
while(index < nTh ){
node = node.prev;
}
return node.runObject;
}
}
============================
/* DListNode2.java */
package Project1;
/**
* A DListNode2 is a node in a DList2 (doubly-linked list).
*/
class DListNode2 {
/**
* item references the item stored in the current node.
* prev references the previous node in the DList.
* next references the next node in the DList.
*
* DO NOT CHANGE THE FOLLOWING FIELD DECLARATIONS.
*/
TypeAndSize runObject;
DListNode2 prev;
DListNode2 next;
/**
* DListNode2() constructor.
*/
DListNode2() {
this.runObject = null;
this.prev = null;
this.next = null;
}
DListNode2(TypeAndSize.Species runType, int runLength) {
this.runObject = new TypeAndSize(runType, runLength);
this.prev = null;
this.next = null;
}
}
===================================
/* TypeAndSize.java */
/* DO NOT CHANGE THIS FILE. */
/* YOUR SUBMISSION MUST WORK CORRECTLY WITH _OUR_ COPY OF THIS FILE. */
package Project1;
/**
* Each TypeAndSize object represents a sequence of identical sharks, fish,
* or empty cells. TypeAndSizes are your way of telling the test program
* what runs appear in your run-length encoding. TypeAndSizes exist solely
* so that your program can return two integers at once: one representing
* the type (species) of a run, and the other representing the size of a run.
*
* TypeAndSize objects are not appropriate for representing your run-length
* encoding, because they do not represent the degree of hunger of a run of
* sharks.
*
* #author Jonathan Shewchuk
*/
class TypeAndSize {
Species type; // runType EMPTY, SHARK, or FISH
int size; // Number of cells in the run for that runType.
enum Species{EMPTY,SHARK,FISH}
/**
* Constructor for a TypeAndSize of specified species and run length.
* #param species is Ocean.EMPTY, Ocean.SHARK, or Ocean.FISH.
* #param runLength is the number of identical cells in this run.
* #return the newly constructed Critter.
*/
TypeAndSize(Species species, int runLength) {
if (species == null) {
System.out.println("TypeAndSize Error: Illegal species.");
System.exit(1);
}
if (runLength < 1) {
System.out.println("TypeAndSize Error: runLength must be at least 1.");
System.exit(1);
}
this.type = species;
this.size = runLength;
}
}
======================================
For reference, complete skeleton of code for assignment is given in link
In the given link, following paragraph says:
Part II(c): Implement a toOcean() method in the RunLengthEncoding class, which converts a run-length encoding to an Ocean object. To accomplish this, you will need to implement a new addShark() method in the Ocean class, so that you can specify the hunger of each shark you add to the ocean. This way, you can convert an Ocean to a run-length encoding and back again without forgetting how hungry each shark was.
My question:
In PartIIa and PartIIb of the solution written in RunLenghtEncoding() 5 argument constructor, I am not capturing hungerLevel property of Shark due to the reason mentioned in method comments -
Any run of sharks is treated as a run of newborn sharks (which are equivalent to sharks that have just eaten).
I would like to know, What exactly toOcean() method mean, when. i do not capture hungerLevel of Shark runType. Am I suppose to convert the Compressed form of Ocean to an 'existing Ocean' or 'new Ocean'? Please help me, am stuck here.
Note: This is self learning course published in 2006. No mentor available for this course. Also suggest me, if this is the right place to discuss such queries

It says that
Any run of sharks is treated as a run of newborn sharks (which are equivalent to sharks that have just eaten).
So when you have to re-create the Ocean, you can treat any run of sharks as sharks with hungerLevel 0. In Part III you do have to keep track of the hungerLevel, however. But for Part II c they leave that out for the moment being.

Related

Error "Cannot reduce the visibility of a inherited method" while implementing interface

I just installed the ta4j Technical Analysis lib .
It has a interface class called TimeSeries.
When I try to implement the first method in TimeSeries
String getName()
I get the following error:
Cannot reduce the visabilty of a inhearted methed from TimeSeries
implaments org.ta4jcore.Timeserios.GetName
My Code
import org.ta4j.core.*;
public class cMyChartVal implements TimeSeries {
/**
* #return the name of the series
*/
String getName()
{
return "TestSet";
}
.....
.....
}
TimeSeries interface class
package org.ta4j.core;
import java.io.Serializable;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* Sequence of {#link Bar bars} separated by a predefined period (e.g. 15 minutes, 1 day, etc.)
* </p>
* Notably, a {#link TimeSeries time series} can be:
* <ul>
* <li>the base of {#link Indicator indicator} calculations
* <li>constrained between begin and end indexes (e.g. for some backtesting cases)
* <li>limited to a fixed number of bars (e.g. for actual trading)
* </ul>
*/
public interface TimeSeries extends Serializable {
/**
* #return the name of the series
*/
String getName();
/**
* #param i an index
* #return the bar at the i-th position
*/
Bar getBar(int i);
/**
* #return the first bar of the series
*/
default Bar getFirstBar() {
return getBar(getBeginIndex());
}
/**
* #return the last bar of the series
*/
default Bar getLastBar() {
return getBar(getEndIndex());
}
/**
* #return the number of bars in the series
*/
int getBarCount();
/**
* #return true if the series is empty, false otherwise
*/
default boolean isEmpty() {
return getBarCount() == 0;
}
/**
* Warning: should be used carefully!
* <p>
* Returns the raw bar data.
* It means that it returns the current List object used internally to store the {#link Bar bars}.
* It may be:
* - a shortened bar list if a maximum bar count has been set
* - a extended bar list if it is a constrained time series
* #return the raw bar data
*/
List<Bar> getBarData();
/**
* #return the begin index of the series
*/
int getBeginIndex();
/**
* #return the end index of the series
*/
int getEndIndex();
/**
* #return the description of the series period (e.g. "from 12:00 21/01/2014 to 12:15 21/01/2014")
*/
default String getSeriesPeriodDescription() {
StringBuilder sb = new StringBuilder();
if (!getBarData().isEmpty()) {
Bar firstBar = getFirstBar();
Bar lastBar = getLastBar();
sb.append(firstBar.getEndTime().format(DateTimeFormatter.ISO_DATE_TIME))
.append(" - ")
.append(lastBar.getEndTime().format(DateTimeFormatter.ISO_DATE_TIME));
}
return sb.toString();
}
/**
* Sets the maximum number of bars that will be retained in the series.
* <p>
* If a new bar is added to the series such that the number of bars will exceed the maximum bar count,
* then the FIRST bar in the series is automatically removed, ensuring that the maximum bar count is not exceeded.
* #param maximumBarCount the maximum bar count
*/
void setMaximumBarCount(int maximumBarCount);
/**
* #return the maximum number of bars
*/
int getMaximumBarCount();
/**
* #return the number of removed bars
*/
int getRemovedBarsCount();
/**
* Adds a bar at the end of the series.
* <p>
* Begin index set to 0 if if wasn't initialized.<br>
* End index set to 0 if if wasn't initialized, or incremented if it matches the end of the series.<br>
* Exceeding bars are removed.
* #param bar the bar to be added
* #see TimeSeries#setMaximumBarCount(int)
*/
void addBar(Bar bar);
/**
* Returns a new TimeSeries implementation that is a subset of this TimeSeries implementation.
* It holds a copy of all {#link Bar bars} between <tt>startIndex</tt> (inclusive) and <tt>endIndex</tt> (exclusive)
* of this TimeSeries.
* The indices of this TimeSeries and the new subset TimeSeries can be different. I. e. index 0 of the new TimeSeries will
* be index <tt>startIndex</tt> of this TimeSeries.
* If <tt>startIndex</tt> < this.seriesBeginIndex the new TimeSeries will start with the first available Bar of this TimeSeries.
* If <tt>endIndex</tt> > this.seriesEndIndex the new TimeSeries will end at the last available Bar of this TimeSeries
* #param startIndex the startIndex
* #param endIndex the endIndex
* #return a new BaseTimeSeries with Bars from startIndex to endIndex-1
* #throws IllegalArgumentException e.g. if endIndex < startIndex
*/
TimeSeries getSubSeries(int startIndex, int endIndex);
}
All method declarations in an interface, including static methods, are implicitly public. And it's usually omitted.
The implementation class should keep this modifer, instead of using default class method modifer( package level).
You can change it to:
public String getName()
{
return "TestSet";
}

Getting the unicode value of the first character in a string

I am basically being asked to take the Unicode value of a string, multiply it by 10% and add whatever level the object currently has. It's frustrating because as it turns out I have the logic down including the code yet I still get an error that says: expected:<0> but was:<8>. Any suggestions, maybe it's just a slight nuance I have to make in the logic, although I'm fairly certain it's right. Take note of the getLevel method because that's where the error is
public class PouchCreature implements Battleable {
private String name;
private int strength;
private int levelUps;
private int victoriesSinceLevelUp;
/**
* Standard constructor. levelUps and victoriesSinceLevelUp start at 0.
*
* #param nameIn desired name for this PouchCreature
* #param strengthIn starting strength for this PouchCreature
*/
public PouchCreature(String nameIn, int strengthIn) {
this.name = nameIn;
this.strength = strengthIn;
this.levelUps = 0;
this.victoriesSinceLevelUp = 0;
}
/**
* Copy constructor.
*
* #param other reference to the existing object which is the basis of the new one
*/
public PouchCreature(PouchCreature other) {
this.name=other.name;
this.strength=other.strength;
this.levelUps=other.levelUps;
this.victoriesSinceLevelUp=other.victoriesSinceLevelUp;
}
/**
* Getter for skill level of the PouchCreature, which is based on the
* first character of its name and the number of levelUps it has.
* Specifically, the UNICODE value of the first character in its name
* taken %10 plus the levelUps.
*
* #return skill level of the PouchCreature
*/
public int getLevel() {
int value = (int)((int)(getName().charAt(0)) * 0.1);
return value + this.levelUps;
}
You've said you're supposed to increase the value by 10%. What you're actually doing, though, is reducing it 90% by taking just 10% of it (and then truncating that to an int). 67.0 * 0.1 = 6.7, which when truncated to an int is 6.
Change the 0.1 to 1.1 to increase it by 10%:
int value = (int)((int)(getName().charAt(0)) * 1.1);
// --------------------------------------------^
There, if getName() returns "Centaur" (for instance), the C has the Unicode value 67, and value ends up being 73.
We need to see the code you're calling the class with and that is generating your error message. Why is it expecting 0? 8 seems like a valid return value from the information you've given.

Is this MVC method a good way to program a Sudoku Game?

I'm building a Sudoku Game. I came here to get some help because I'm completely stuck in my code. I'm not asking for you to complete my code, I know that's not your job. Just few hints as what to do next would be great!
I use MVC and Swing Components for GUI to make the code lighter. I divided each field and method so I can understand what to do next but I'm confused. I'm particularly having trouble understanding how to do the following methods:
initializeGrid
chooseGameDifficulty
makeMove
cancelMove
Model
public class GameSudokuModel {
// states -- fields
Scanner userInput = new Scanner (System.in); // accept user input
// int levelDifficulty = 0; // level of difficulties
int [] gridSize ; // Sudoku 9x9 == 81 cells -- used to initialize grid or solve puzzle --
int [] subGridSize ; // a sub-grid = 9 cells
int gameMove = 0; // calculate the total number of moves per game // ++makeMove and --cancelMove
int [] gameCell = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // a cell contain a list of choices numbers 1-9
int currentGameTime = 0; // calculates the total time to complete a puzzle
String currentPlayerName = userInput.nextLine(); // player name
// end of fields
//behaviors -- methods
/******************************************************
*
* Method calculateGameTime (initialiserGrille)
*
*
* Calculates time
*
* The stopwatch starts when the player makes ​​his first move
*
*
*
******************************************************/
public class calculateGameTime{
}
/******************************************************
*
* Method initializeGrid (initialiserGrille)
*
*
* Used to initialize a grid
*
* Reset the grid ( back to the original Sudoku grid ) using the list of moves .
*
*
*
*
*
******************************************************/
public class initializeGrid {
}
/******************************************************
*
* Method levelDifficulty
*
*
* Established the parameters of level of difficulty
*
*
* #param beginner
* #param expert
* #return
******************************************************/
public int levelDifficulty (int beginner, int expert){
while(true)
{
int levelDifficulty = 0;
levelDifficulty= userInput.nextInt();
System.out.println (" ");
if(levelDifficulty < beginner || levelDifficulty> expert){
System.out.print (" You must choose 1, 2 or 3." + "Please try again : ");
System.out.println (" ");
}
else
return levelDifficulty;
}
}
/****************************************************
* Method chooseGameDifficulty (chosisirNiveauDifficulte)
*
* The method makes possible to choose the level of complexity of a grid
*
* (1) beginner: the player starts the game with a grid made ​​up to 75% (81 * 0.75)
*
* (2) Intermediate : the player starts the game with a grid made ​​up to 50% (81 * 0.50)
*
* (3) Expert : the player starts the game with a grid made ​​up to 25% (81 * 0.25)
*
* Numbers are set randomly on the grid every new game
*
* #param beginner
* #param intermediate
* #param expert
******************************************************/
public void chooseGameDifficulty(int beginner, int intermediate, int expert){
boolean entreeValide;
int levelDifficulty;
String reponse;
levelDifficulty= levelDifficulty(beginner,expert); // call function levelDifficulty()
if(levelDifficulty==beginner)
//get easy level grid (getter)
//set easy level grid (setter)
if(levelDifficulty==intermediate)
//get intermediate level grid (getter)
//set intermediate level grid (setter)
if(levelDifficulty==expert)
//get expert level grid (getter)
//set easy expert grid (setter)
}
/****************************************************
* Method solvePuzzle (resoudrePuzzle)
*
* This method makes possible to solve the entire grid meaning all the 81 cells
*
******************************************************/
public class solvePuzzle {
}
/****************************************************
* Method makeMove (fairePlacement)
*
* Save a record of the player's actions on the grid.
*
*
*
* (1) make move on the grid ;
* (2) save moves in an array list
*
******************************************************/
public class makeMove {
//choose a cell , enter a number on the cell and confirm the selection
// adds move to the array list
int makeMove = gameMove++;
}
/****************************************************
* Method cancelMove (annulerPlacement)
*
*
*
* (1) retrieve the last instance in the arraylist (using the remove method and the size method to determine the number of elements )
* (2) cancel the move in the grid.
*
******************************************************/
public class cancelMove {
//choose a cell , remove the number on the cell and confirm the cancellation
//substracts move from array list
int cancelMove = gameMove--;
}
}
initializeGrid and chooseGameDifficulty aren't really features of the model. The model maintains the current state of the data and the rules uses to manage it.
Technically, these features should be functions of some kind of factory that given a difficult level will return a instance of the model
public class SudokuFactory {
public enum Difficulty {
HARD,
MODERATE,
EASY
}
public SudokuModel createModel(Difficulty difficult) {
// Make a new model based on the rules for your difficulty
// settings
}
}
The model would then simply contain the information and functionality to manage it
You should also avoid static where practically possible, it should never be used as a cross class communication mechanism, if you need to share data, you should pass it. static just makes the whole thing a lot more difficult to manage and debug
The view would get the information from the user (like the difficulty level), which would be used by the controller to build a new model. The model would then be passed to a new controller, which would generate a new view, which should present the current state of the model.
The controller would then respond to changes in the view, updating the model and the controller would respond to changes in the model and update the view.
You should also prefer using interfaces over implementation
So, based on my (rather pathetic) understanding of Sudoku you could use a model as simple as ...
public interface SudokuModel {
public void setValueAt(int value, int row, int col) throws IllegalArgumentException;
public int getValueAt(int row, int col);
}
Now, me, personally, I'd have an implementation that had two buffers, one which represents the actual game/solution and one which represents the player data (pre-filled based on the difficulty level), now you could have a single buffer, but you'd have constantly scan the grid to see if the new value was valid and I'm just too lazy ;)

Optimising Code an Array of Strings for a History in Java

I am seeking guidance in the respect of optimising code. The code I have written is for a text-based game in which you type in commands into a command bar. One feature I wished to incorporate into my interface was the ability to scroll through a history of one's last 100 commands entered using the up and down arrow keys so that it would be more convenient for the user to play the game.
I have designed a class in which uses a String[] that will store each new entry in the second position (Array[1]) and move all entries back one position while the first position of the array (Array[0]) is just a blank, empty string. The code initialises the array to have 101 values to compensate for the first position being a blank line.
When a user inputs 0 - 100 in that order, it should then give me the reverse of the order (almost like a last in, first out kind of situation, but storing the last 100 values as opposed to removing them once they are accessed), and since 0 - 100 is 101 values, the last value will be overwritten.
Thus, scrolling up through the history, it would give me 100, 99, 98, ..., 2, 1. If I were to select 50 from the list, it would then be 50, 100, 99, ..., 3, 2. The code indeed does this.
The code is listed below:
public class CommandHistory {
private String[] history;
private final int firstIndex = 1;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
history = new String[101];
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity + 1];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
private int size() {
return history.length;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
history[0] = "";
if (!command.equals("")) {
for (int i = firstIndex; i < size();) {
if (history[i] == null) {
history[i] = command;
break;
} else {
for (int j = size() - 1; j > firstIndex; j--) {
history[j] = history[j - 1];
}
history[firstIndex] = command;
break;
}
}
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex > 0) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex >= 0 && (history[currentIndex + 1] != null)) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = firstIndex; i < size(); i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size(); i++) {
history += this.history[i];
}
return history;
}
}
In my interface class, once the user types something into the command bar and hits enter, it will get the text currently stored in the bar, uses the add method to add it to the history, parses the command via another class, and then sets the text in the bar to blank.
Pressing the up arrow calls the next method which scrolls up the list, and the down arrow calls the previous method which scrolls down the list.
It seems to work in every way I wish it to, but I was wondering if there was some way to optimise this code or perhaps even code it in a completely different way. I am making this game to keep myself practiced in Java and also to learn new and more advanced things, so I'd love to hear any suggestions on how to do so.
The comments to your question have already pointed out that you are somehow trying to reinvent the wheel by implementing functionality that the standard Java class library already provides to some extent (see LinkedList/Queue and Arraylist). But since you say you want to keep yourself practiced in Java I guess it is perfectly fine if you try to implement your own command history from scratch.
Here are some of my observations/suggestions:
1) It is not necessary and very counter-intuitive to declare a final first index of 1. It would be easy to start with a default index of 0 and add corresponding checks where necessary.
2) Forget about your private size() method - it is just returning the length of the internal array anyway (i.e. the initial capacity+1). Instead consider adding a public size() method that returns the actual number of added commands and internally update the actual size when adding new commands (see e.g. java.util.ArrayList for reference).
3) At the moment every call to add(String command) will set history[0] = "", which is not necessary. If you want the first index to be "", set it in the constructor. This is also a clear sign, that it would perhaps be better to start with an initial index of 0 instead of 1.
4) A minor issue: "if (!command.equals(""))" during your add method is perhaps OK for such a specialized class but it should definitely be commented in the documentation of the method. Personally I would always let the calling class decide if an empty "" command is considered valid or not. Also this method will throw an undocumented NullPointerException, when null is used as an argument. Consider changing this to "if (!"".equals(command))" or throw an IllegalArgumentException if null is added.
5) "if (history[i] == null)" during the add method is completely unnecessary, if you internally keep a pointer to the actual size of the commands - this is actually a special case that will only be true, when the very first command is added to the command history (i.e. when it's actual size == 0).
6) Having two nested for loops in your add method implementation is also unnecessary, if you keep a pointer to the actual size (see example below)
7) I would reconsider if it is necessary to keep a pointer to the current index in the command history. Personally I would avoid storing such a pointer and leave these details to the calling class - i.e. remove the previous and next methods and either provide a forward/backward Iterator and/or a random access to the index of the available commands. Interestingly, when this functionality is removed from your command history class, it actually comes down to either an implementation of a LinkedList or an ArrayList- whichever way you go. So in the end using one of the built in Java collections would actually be the way to go.
8) Last but nor least I would reconsider if it is useful to insert added commands at the beginning of the list - I believe it would be more natural to append them to the end as e.g. ArrayList does. Adding the commands to the end would make the swapping of all current commands during each call to add() unnecessary...
Here are some of the suggested changes to your class (not really tested...)
public class CommandHistory {
private String[] history;
private int size;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
this(100);
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
public int size() {
return size;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
if (!"".equals(command)) {
if (this.size < history.length) {
this.size++;
}
for (int i = size-1; i >0; i--) {
history[i] = history[i-1];
}
history[0] = command;
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex >= 0 && currentIndex < size-1) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex > 0 && currentIndex < size) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = 0; i < size; i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size; i++) {
history += this.history[i] + ", ";
}
return history;
}
}
Well, I guess I have invested far too much time for this, but I learned quite a bit myself on the way - so thanks ;-)
Hope some of this is useful for you.

Trend analysis using iterative value increments

We have configured iReport to generate the following graph:
The real data points are in blue, the trend line is green. The problems include:
Too many data points for the trend line
Trend line does not follow a Bezier curve (spline)
The source of the problem is with the incrementer class. The incrementer is provided with the data points iteratively. There does not appear to be a way to get the set of data. The code that calculates the trend line looks as follows:
import java.math.BigDecimal;
import net.sf.jasperreports.engine.fill.*;
/**
* Used by an iReport variable to increment its average.
*/
public class MovingAverageIncrementer
implements JRIncrementer {
private BigDecimal average;
private int incr = 0;
/**
* Instantiated by the MovingAverageIncrementerFactory class.
*/
public MovingAverageIncrementer() {
}
/**
* Returns the newly incremented value, which is calculated by averaging
* the previous value from the previous call to this method.
*
* #param jrFillVariable Unused.
* #param object New data point to average.
* #param abstractValueProvider Unused.
* #return The newly incremented value.
*/
public Object increment( JRFillVariable jrFillVariable, Object object,
AbstractValueProvider abstractValueProvider ) {
BigDecimal value = new BigDecimal( ( ( Number )object ).doubleValue() );
// Average every 10 data points
//
if( incr % 10 == 0 ) {
setAverage( ( value.add( getAverage() ).doubleValue() / 2.0 ) );
}
incr++;
return getAverage();
}
/**
* Changes the value that is the moving average.
* #param average The new moving average value.
*/
private void setAverage( BigDecimal average ) {
this.average = average;
}
/**
* Returns the current moving average average.
* #return Value used for plotting on a report.
*/
protected BigDecimal getAverage() {
if( this.average == null ) {
this.average = new BigDecimal( 0 );
}
return this.average;
}
/** Helper method. */
private void setAverage( double d ) {
setAverage( new BigDecimal( d ) );
}
}
How would you create a smoother and more accurate representation of the trend line?
This depends on the behavior of the item you are measuring. Is this something that moves (or changes) in a manner that can be modeled?
If the item is not expected to change, then your trend should be the underlying mean value of the entire sample set, not just the past two measurements. You can get this using Bayes theorem. The running average can be calculated incrementally using the simple formula
Mtn1 = (Mtn * N + x) / (N+1)
where x is the measurement at time t+1, Mtn1 is the mean a time t+1, Mtn is the mean at time t, and N is the number of measurements taken by time t.
If the item you are measuring fluctuates in a manner that can be predicted by some underlying equation, then you can use a Kalman filter to provide a best estimate of the next point based on the previous (recent) measurements and the equation that models the predicted behavior.
As a starting point, the Wikipedia entry on Bayesian estimators and Kalman Filters will be helpful.
Resulting Image
The result is still incomplete, however it clearly shows a better trend line than that in the question.
Calculation
There were two key components missing:
Sliding window. A List of Double values that cannot grow beyond a given size.
Calculation. A variation on the accept answer (one less call to getIterations()):
((value - previousAverage) / (getIterations() + 1)) + previousAverage
Source Code
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import net.sf.jasperreports.engine.fill.AbstractValueProvider;
import net.sf.jasperreports.engine.fill.JRFillVariable;
import net.sf.jasperreports.engine.fill.JRIncrementer;
/**
* Used by an iReport variable to increment its average.
*/
public class RunningAverageIncrementer
implements JRIncrementer {
/** Default number of tallies. */
private static final int DEFAULT_TALLIES = 128;
/** Number of tallies within the sliding window. */
private static final int DEFAULT_SLIDING_WINDOW_SIZE = 30;
/** Stores a sliding window of values. */
private List<Double> values = new ArrayList<Double>( DEFAULT_TALLIES );
/**
* Instantiated by the RunningAverageIncrementerFactory class.
*/
public RunningAverageIncrementer() {
}
/**
* Calculates the average of previously known values.
* #return The average of the list of values returned by getValues().
*/
private double calculateAverage() {
double result = 0.0;
List<Double> values = getValues();
for( Double d: getValues() ) {
result += d.doubleValue();
}
return result / values.size();
}
/**
* Called each time a new value to be averaged is received.
* #param value The new value to include for the average.
*/
private void recordValue( Double value ) {
List<Double> values = getValues();
// Throw out old values that should no longer influence the trend.
//
if( values.size() > getSlidingWindowSize() ) {
values.remove( 0 );
}
this.values.add( value );
}
private List<Double> getValues() {
return values;
}
private int getIterations() {
return getValues().size();
}
/**
* Returns the newly incremented value, which is calculated by averaging
* the previous value from the previous call to this method.
*
* #param jrFillVariable Unused.
* #param tally New data point to average.
* #param abstractValueProvider Unused.
* #return The newly incremented value.
*/
public Object increment( JRFillVariable jrFillVariable, Object tally,
AbstractValueProvider abstractValueProvider ) {
double value = ((Number)tally).doubleValue();
recordValue( value );
double previousAverage = calculateAverage();
double newAverage =
((value - previousAverage) / (getIterations() + 1)) + previousAverage;
return new BigDecimal( newAverage );
}
protected int getSlidingWindowSize() {
return DEFAULT_SLIDING_WINDOW_SIZE;
}
}

Categories