I've been asked to make this more space efficient, I'm assuming that I need to use loops but I'm not entirely sure how to, some help would be appreciated.
(For reference the aim of this piece of code is to display a graph into another program using data which has already been collected in the other program)
public class StudentChart
{
public StudentChart(int[] moduleMarks) //Constructor
{
Bar y = new Bar();
y.makeVisible();
y.changeSize(1, 100);
y.moveVertical(100);
y.moveHorizontal(-1);
y.changeColour(Colour.BLACK);
//y-axis is produced
Bar x = new Bar();
x.makeVisible();
x.changeSize(200,1);
x.moveVertical(200);
x.changeColour(Colour.BLACK);
//x-axis is produced
draw(moduleMarks);
printSummary(moduleMarks);
}
public static void draw(int[] moduleMarks)
{
int a = moduleMarks[0];
int b = moduleMarks[1];
int c = moduleMarks[2];
int d = moduleMarks[3];
int e = moduleMarks[4];
int f = moduleMarks[5];
//stores module marks from array as variables to be used later
Bar mod1 = new Bar();
Bar mod2 = new Bar();
Bar mod3 = new Bar();
Bar mod4 = new Bar();
Bar mod5 = new Bar();
Bar mod6 = new Bar();
mod1.makeVisible();
mod2.makeVisible();
mod3.makeVisible();
mod4.makeVisible();
mod5.makeVisible();
mod6.makeVisible();
//Bars are initialised and made visible
mod1.moveVertical(200-a);
mod2.moveVertical(200-b);
mod3.moveVertical(200-c);
mod4.moveVertical(200-d);
mod5.moveVertical(200-e);
mod6.moveVertical(200-f);
//Bars are moved based on their height so that they touch the x-axis
mod1.changeSize(15, a);
mod2.changeSize(15, b);
mod3.changeSize(15, c);
mod4.changeSize(15, d);
mod5.changeSize(15, e);
mod6.changeSize(15, f);
//Bar height changes depending on the module marks
mod1.moveHorizontal(0);
mod2.moveHorizontal(35);
mod3.moveHorizontal(70);
mod4.moveHorizontal(105);
mod5.moveHorizontal(140);
mod6.moveHorizontal(175);
//Bars are moved across so can be seen on chart
if (a<35)
{
mod1.changeColour(Colour.RED);
}
if (a>= 35 && a<40)
{
mod1.changeColour(Colour.YELLOW);
}
if (a>= 40 && a<70)
{
mod1.changeColour(Colour.GREEN);
}
if (a>= 70)
{
mod1.changeColour(Colour.MAGENTA);
}
if (b<35)
{
mod2.changeColour(Colour.RED);
}
if (b>= 35 && a<40)
{
mod2.changeColour(Colour.YELLOW);
}
if (b>= 40 && a<70)
{
mod2.changeColour(Colour.GREEN);
}
if (b>= 70)
{
mod2.changeColour(Colour.MAGENTA);
}
if (c<35)
{
mod3.changeColour(Colour.RED);
}
if (c>= 35 && a<40)
{
mod3.changeColour(Colour.YELLOW);
}
if (c>= 40 && a<70)
{
mod3.changeColour(Colour.GREEN);
}
if (c>= 70)
{
mod3.changeColour(Colour.MAGENTA);
}
if (d<35)
{
mod4.changeColour(Colour.RED);
}
if (d>= 35 && a<40)
{
mod4.changeColour(Colour.YELLOW);
}
if (d>= 40 && a<70)
{
mod4.changeColour(Colour.GREEN);
}
if (d>= 70)
{
mod4.changeColour(Colour.MAGENTA);
}
if (e<35)
{
mod5.changeColour(Colour.RED);
}
if (e>= 35 && a<40)
{
mod5.changeColour(Colour.YELLOW);
}
if (e>= 40 && a<70)
{
mod5.changeColour(Colour.GREEN);
}
if (e>= 70)
{
mod5.changeColour(Colour.MAGENTA);
}
if (f<35)
{
mod6.changeColour(Colour.RED);
}
if (f>= 35 && a<40)
{
mod6.changeColour(Colour.YELLOW);
}
if (f>= 40 && a<70)
{
mod6.changeColour(Colour.GREEN);
}
if (f>= 70)
{
mod6.changeColour(Colour.MAGENTA);
}
//Colour changes depending on module mark
//Could be improved
}
public static void printSummary(int[] moduleMarks)
{
for(int i =0; i<moduleMarks.length; i=i+1)
{
System.out.println("Module "+ (i+1) + " " + moduleMarks[i]);
}
//Prints module marks in a table
}
public static void main(String[] args)
{
}
}
You could do something like this:
int horizontalMovement = 0;
for (int moduleMark : moduleMarks) {
Bar bar = new Bar();
bar.makeVisible();
bar.moveVertical(200 - moduleMark);
bar.changeSize(15, moduleMark);
bar.moveHorizontal(horizontalMovement);
horizontalMovement = horizontalMovement + 35;
if (moduleMark < 35) {
bar.changeColour(Colour.RED);
} else if (moduleMark < 40) {
bar.changeColour(Colour.YELLOW);
} else if (moduleMark < 70) {
bar.changeColour(Colour.GREEN);
} else {
bar.changeColour(Colour.MAGENTA);
}
}
There are lots of things you can do here:
int a = moduleMarks[0];
int b = moduleMarks[1];
int c = moduleMarks[2];
int d = moduleMarks[3];
int e = moduleMarks[4];
int f = moduleMarks[5];
Here you can make an int array:
int modules[] = new int[moduleMarks.length()];
Then you can go through with a for-loop and add:
for(int i = 0; i < 6; i++) {
modules[i] = moduleMarks[i];
}
You can do similar changes to the rest of your code using this as inspiration.
Right now this code will only work if the elements in moduleMarks is only 6 elements long. If there were 9 elements in that array only the first 6 would be drawn. Sorta makes this code not terribly useful. However, you can write code that will accept an array of any length and respond accordingly.
You've been given a hint how to start that process in the code you were given:
for(int i =0; i<moduleMarks.length; i=i+1)
{
System.out.println("Module "+ (i+1) + " " + moduleMarks[i]);
}
That will iterate over every member of moduleMark regardless of length. So you need to apply this idea to the rest of your program so that whatever draw is doing will work regardless of moduleMark's size.
You'll see there is a lot of code repeated for each element corresponding to a member in moduleMark. Use loops to reduce that repeated code. You'll have to model things with data (ie arrays) in order to rewrite that code using for loops.
I'm trying not to give the answer away because this is clearly homework and you should figure it out yourself.
Put your bars into an array (Bar[]). Keep your moduleMarks in the array that they originally came in. Then you can use a loop or set of nested loops:
for(int i = 0; i < modArray.size; ++i) {
modArray[i].makeVisible();
modArray[i].moveVertical(200-moduleMarks[i]);
modArray[i].changeSize(15, moduleMarks[i]);
modArray[i].moveHorizontal(35*i);
//etc.
}
Related
I am making 3D-Objects made of Triangles. These Triangles have 3 Vectors.
Now I have a file with alot of numbers... If a line starts with "v" the line has x-, y-, and z-coordinates of a Vector.
If a line starts with "f" the line has the line of the Vector in the .txt file that I need for my Triangle.
The file starts with all "v"s first and then continues with the "f"s.
Example: (the number at the beginning is just the line)
21 v 1.2000 0.20000 -1.0000 -> Vector1(1.2, 0.2, -1)
22 v 1.2000 0.20000 1.00000 -> Vector2(1.2, 0.2, 1)
23 v -1.200 -0.2000 1.00000 -> Vector3(-1.2, -0.2, 1)
...
71 f 21 23 22 -> Triangle(Vector1, Vector3, Vector2)
And this is what i tried, which obviously did not work since I am a Java newbie :P
public static ArrayList<Triangle> mesh = new ArrayList<>();
public static void loadObject(String fileName) {
try {
Scanner scan = new Scanner(fileName);
ArrayList<Vector> vectors = new ArrayList<>();
while (scan.hasNextLine()) {
if (scan.equals("v")) {
Vector v = new Vector();
int i = 0;
while (scan.hasNextDouble() && i < 3) {
if (i == 0) {
v.setX(scan.nextDouble());
}
if (i == 1) {
v.setY(scan.nextDouble());
}
if (i == 2) {
v.setZ(scan.nextDouble());
}
i++;
}
vectors.add(v);
}
if (scan.equals("f")) {
Triangle t = new Triangle();
int j = 0;
while (scan.hasNextInt() && j < 3) {
if (j == 0) {
t.setVec1(vectors.get(scan.nextInt() - 1));
}
if (j == 1) {
t.setVec2(vectors.get(scan.nextInt() - 1));
}
if (j == 2) {
t.setVec3(vectors.get(scan.nextInt() - 1));
}
j++;
}
mesh.add(t);
}
}
} catch (Exception e) {
}
}
Thanks for the help
You are describing the Wavefron OBJ file format for which many loaders already exist. You should consider using an existing loader instead of rolling your own.
Googling for it I find three Java .obj loaders on Github right away:
javagl/Obj
seanrowens/oObjLoader
Blunderchips/LWJGL-OBJ-Loader
I have not used any of these so you would need to try them out yourself and see if they provide the right API for you and solve your concrete problem.
How about something like this:
static List<Triangle> parse(String fileName)
{
List<Triangle> mesh = new ArrayList<>();
Map<String, Vector> vm = new HashMap<>();
try (Scanner scan = new Scanner(new FileReader(fileName)))
{
while(scan.hasNextLine())
{
String[] p = scan.nextLine().split("\\s");
if("v".equals(p[1]))
{
vm.put(p[0], new Vector(Double.parseDouble(p[2]), Double.parseDouble(p[3]), Double.parseDouble(p[4])));
}
else if("f".equals(p[1]))
{
mesh.add(new Triangle(vm.get(p[2]), vm.get(p[3]), vm.get(p[4])));
}
}
}
catch(IOException e)
{
e.printStackTrace();
}
return mesh;
}
I am new to inheritance in java and I have the folowing problem. My base class is Plane, its child class is PlaneComponent and PlaneComponent's child class is PassengerCompartment. My program consists of 11 classes, when I ignore the PassengerCompartment class everythig's right. But when I run the whole program I get this message: at Plane.<init>(Plane.java:14)
at PlaneComponent.<init>(PlaneComponent.java:1)
at PassengerCompartment.<init>(PassengerCompartment.java:11)
which is printed repeatedly for so many times that I cannot see the top line of the error. The lines includes in error messages are the bold lines (here printed between ** **).
Plane:
import java.util.*;
public class Plane
{
int cap;
int pl;
public Plane()
{
String desc = "Plane Description";
String title = "Boeing 747";
Random rand = new Random();
cap = rand.nextInt(100) + 51; //initialize cap with 50<value<100
**PassengerCompartment a8 = new PassengerCompartment();**
pl = cap / a8.cap2; // pl = sum of Passenger Compartments
if (cap % a8.cap2 != 0)
{
pl = pl - (cap % a8.cap2) + 1;
}
}
public boolean ready_check()
{
CargoBay a6 = new CargoBay();
a6.ready_check();
for (int i = 0 ; i < 3 ; i++)
{
EquipmentCompartment a7 = new EquipmentCompartment();
a7.ready_check();
}
for(int i = 0; i < pl; i++)
{
PassengerCompartment a8 = new PassengerCompartment();
a8.ready_check();
}
System.out.println("Plane OK!");
return true;
}
public void process(String e)
{
if(e == "SecurityEmployee")
{
SecurityEmployee a14 = new SecurityEmployee();
a14.workOn();
}
if(e == "MaintenanceEmployee")
{
MaintenanceEmployee a15 = new MaintenanceEmployee();
a15.workOn();
}
if(e == "CleaningEmployee")
{
CleaningEmployee a16 = new CleaningEmployee();
a16.workOn();
}
}
public void values()
{
System.out.println("Would you like more info about the plane's capacity? type Y or N");
Scanner input = new Scanner(System.in);
String inp = input.nextLine();
while (!(inp.equals("Y")) && !(inp.equals("N")))
{
System.out.println("Wrong input, please type again");
inp = input.nextLine();
}
if ( inp.equals("Y"))
{
PassengerCompartment a8 = new PassengerCompartment();
System.out.println("Plane's capacity: " + cap);
System.out.println("PassComp's capacity: " + a8.cap2);
System.out.println("Number ofPassenger Compartments " + pl);
}
}
}
PlaneComponent:
**public class PlaneComponent extends Plane**
{
public boolean ready_check()
{
return true;
}
public void process(String desc)
{
Employee.workOn(desc);
}
}
PassengerCompartment:
import java.util.*;
public class PassengerCompartment extends PlaneComponent
{
Random rand = new Random();
boolean inner = rand.nextBoolean();
int cap2;
String desc;
public PassengerCompartment()
**{**
desc = "Passenger Compartment";
cap2 = rand.nextInt(50) + 21; //initialize cap with 20<value<50
}
public boolean ready_check()
{
System.out.println(desc);
if (super.ready_check() == true)
{
System.out.println("Passenger Compartment OK!");
if (inner == true)
{
desc = "Inner Compartment";
System.out.println(desc);
if (super.ready_check() == true)
{
System.out.println("Inner Compartment OK!");
}
}
}
return true;
}
public void process(String desc)
{
super.process(desc);
}
}
You have a circular-inheritance-kind-of-situation (which probably creates a StackOverflow exception):
You instantiate PassengerCompartment a8 = new PassengerCompartment(); in the constructor of Plane.
PassengerCompartment extends PlaneComponent, so the constructor of PlaneComponent is called implicitly (super()), which inherit from Plane. In Plane's constructor you have the mentioned instantiation of PassengerCompartment and so on... So I would strongly advise to not instantiate classes that inherit from your class in the constructor of said class.
I would recommend reading my Q/A on this very topic here.
Your issue is with your inheritance structure.
In Planes constructor, you create an instance of PassengerCompartment.
PassengerCompartment extends PlaneCompartment. PlaneCompartment extends `Plane'. So, you are in a circle.
I don't think it makes sense for PlaneCompartment to extends Plane.
What you have is a 'has-a' relationship. So, Plane has a PlaneCompartment but neither extends each other.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 8 years ago.
I'm trying to code a battleship game and im getting a weird error when i run it: Exception in thread "main" java.lang.NullPointerException
at Caculations.findShip(Caculations.java:29)
at Board.main(Board.java:60)
Please help im stuck and i dont know how to continue! Here is my code: (Note, its in 2 class files in my eclipse work enviorment)
public class Board {
public static void main(String[] args) {
boolean continuePlay = true;
int[][] board = new int[10][10]; // creating 2d array 'board'
char[][] boardGraphical = new char[10][10]; // creating 2d array 'board
// this time the visual
for (int x = 0; x < 10; x++) { // for within for //initializing elements
// in both boards using double for
// method
for (int y = 0; y < 10; y++) {
board[x][y] = 0;
boardGraphical[x][y] = 'o';
System.out.println("Board element " + x + " " + y // printing
// initialized
// elements
// here
+ " initialized");
}
}
/*
* 1) Make user ships 1 and computer ships 2 (all numbers other than 0 =
* true)
*
* 2) Make it where if the computer gets a hit on a '1' than it sets
* that value to like a 3 or something so it knows when the ship is
* sunk. So in a if it does if([x][y] && [x][y])
* System.out.println("You sunk my ship!");
*
* 3) REMEMBER YOU CAN DO MULTIPLE IFS INSIDE IFS FOR MULTIPLE
* CONDITIONS. 4) declare ships here!
*
* 5) PUT STUFF IN A WHILE LOOP SO COMP CAN KEEP GOING
*/
board[3][3] = 1; // declaring a battleship. Very important.
board[3][4] = 1;
board[3][5] = 1;
boardGraphical[3][3] = 's';
boardGraphical[3][4] = 's';
boardGraphical[3][5] = 's';
while (continuePlay == true) { // while loop so that computer keeps
// guessing
// WITHIN THIS LOOP KEEP REPRINTING THE BOARD
double computerChoiceXd = Math.floor(Math.random() * 10); // using
// Math.random
// functions
// for
// computers
// first
// guess
// to be
// a
// random
// num
double computerChoiceYd = Math.floor(Math.random() * 10);
int computerChoiceX = (int) computerChoiceXd;
int computerChoiceY = (int) computerChoiceYd;
if (board[computerChoiceX][computerChoiceY] == 1) { // checking if
// math.random
// landed on a
// ship point
System.out.println("Computer got a hit at " + computerChoiceX
+ " " + computerChoiceY);
board[computerChoiceX][computerChoiceY] = 2; // setting the
// point as 2 or
// 'hit'
boardGraphical[computerChoiceX][computerChoiceY] = 'H';
for (int row = 0; row < 10; row++) { // printing out graphical
// board using the same
// method as when
// intializing
for (int col = 0; col < 10; col++) {
System.out.print(boardGraphical[row][col]);
}
System.out.println(" "); // spacer for printing
}
Caculations test = new Caculations(computerChoiceX, // Creating
// a new
// object of
// calculation
computerChoiceY, board);
test.findShip();
// break;
if (board[3][3] == 2) { // checking to see if ship is sunk using
// a triple if statement
if (board[3][4] == 2) {
if (board[3][5] == 2) {
System.out.println("Battleship sunk!");
continuePlay = false; // if so than it breaks out of
// loop to end the game
break;
}
}
}
} else if (board[computerChoiceX][computerChoiceY] == 0) { // otherwise
// if
// the
// area
// is a
// 0 or
// 'unmarked'
System.out.println("Computer missed at " + computerChoiceX
+ " " + computerChoiceY);
boardGraphical[computerChoiceX][computerChoiceY] = 'x'; // mark
// area
// as a
// miss
for (int row = 0; row < 10; row++) { // print out board
for (int col = 0; col < 10; col++) {
System.out.print(boardGraphical[row][col]);
}
System.out.println(" ");
}
}
}
}
}
public class Caculations {
int xValue;
int yValue;
int[][] myArray;
int[][] storage = new int[10][10];
boolean xAxisChangeP;
boolean yAxisChangeP;
boolean xAxisChangeN;
boolean yAxisChangeN;
boolean notSunk;
Caculations(int x, int y, int[][] myArray) {
xValue = x;
yValue = y;
xAxisChangeP = true;
yAxisChangeP = true;
xAxisChangeN = true;
yAxisChangeN = true;
notSunk = true;
}
void findShip() {
while (notSunk == true) {
// 1
while (xAxisChangeP == true) {
if (myArray[xValue + 1][yValue] == 1) {
myArray[xValue + 1][yValue] = 2;
if (myArray[3][3] == 2) {
if (myArray[3][4] == 2) {
if (myArray[3][5] == 2) {
System.out.println("Battleship sunk!");
notSunk = false;
}
}
}
continue;
}
else {
xAxisChangeP = false;
}
}
while (xAxisChangeN == true) {
if (myArray[xValue - 1][yValue] == 1) {
myArray[xValue - 1][yValue] = 2;
if (myArray[3][3] == 2) {
if (myArray[3][4] == 2) {
if (myArray[3][5] == 2) {
System.out.println("Battleship sunk!");
notSunk = false;
}
}
}
continue;
}
else {
xAxisChangeN = false;
}
}
// 1
while (yAxisChangeP == true) {
if (myArray[xValue][yValue + 1] == 1) {
myArray[xValue][yValue + 1] = 2;
if (myArray[3][3] == 2) {
if (myArray[3][4] == 2) {
if (myArray[3][5] == 2) {
System.out.println("Battleship sunk!");
notSunk = false;
}
}
}
continue;
}
else {
yAxisChangeP = false;
}
}
while (yAxisChangeN == true) {
if (myArray[xValue][yValue - 1] == 1) {
myArray[xValue][yValue - 1] = 2;
if (myArray[3][3] == 2) {
if (myArray[3][4] == 2) {
if (myArray[3][5] == 2) {
System.out.println("Battleship sunk!");
notSunk = false;
}
}
}
continue;
}
else {
yAxisChangeN = false;
}
}
}
}
}
Have you initialized myarray? Best is debug your code to see, which statement throws the exception. In eclipse you can add NullPointerExeption as your breakpoint and debug.
You use myArray, but you never initialize it.
public class Caculations {
int xValue;
int yValue;
int[][] myArray; // array declared but never initialized
// ....
void findShip() {
while (notSunk == true) {
// 1
while (xAxisChangeP == true) {
if (myArray[xValue + 1][yValue] == 1) // then you use it here
Solution: initialize variables before using.
More importantly, you need to learn the general concepts of how to debug a NPE (NullPointerException). You should critically read your exception's stacktrace to find the line of code at fault, the line that throws the exception, and then inspect that line carefully, find out which variable is null, and then trace back into your code to see why. You will run into these again and again, trust me.
In your constructor for Calculations, you never initialized myArray:
Caculations(int x, int y, int[][] myArray) {
xValue = x;
yValue = y;
xAxisChangeP = true;
yAxisChangeP = true;
xAxisChangeN = true;
yAxisChangeN = true;
notSunk = true;
this.myArray = myArray; //Add this line
}
This is a direct answer to your problem, but all in all, you should do some research regarding the meaning behind the exception that was thrown so you understand what it means.
Why this problem happened
In Java, all objects and primitives, if not initialized manually, are given a default value.
For default values of primitives, check this: Primitive Data Types
In case of non-primitive types - such as Object, String, Thread, etc, as well as any user-defined class (i.e. Calculations) and also arrays (i.e. myArray) - the default value is null.
With that in mind, inside your constructor, as exemplified above, you have not initialized myArray, which means that when this variable was accessed for the first time, the value returned was null.
So, what's the problem with null?
Well, by itself, it does no harm. It's there. It doesn't bother you. Until you decide to use a variable that doesn't have an object assigned to it, but somehow you forget that and treat it as if it held something like a String or an array.
That's when Java will tell you: "Hey! There's no object here. I can't work like this. Let's throw an exception!".
I'm trying to make a 2d array of an object in java. This object in java has several private variables and methods in it, but won't work. Can someone tell me why and is there a way I can fix this?
This is the exeception I keep getting for each line of code where I try to initialize and iterate through my 2d object.
"Exception in thread "main" java.lang.NullPointerException
at wumpusworld.WumpusWorldGame.main(WumpusWorldGame.java:50)
Java Result: 1"
Here is my main class:
public class WumpusWorldGame {
class Agent {
private boolean safe;
private boolean stench;
private boolean breeze;
public Agent() {
safe = false;
stench = false;
breeze = false;
}
}
/**
* #param args
* the command line arguments
* #throws java.lang.Exception
*/
public static void main(String [] args) {
// WumpusFrame blah =new WumpusFrame();
// blah.setVisible(true);
Scanner input = new Scanner(System.in);
int agentpts = 0;
System.out.println("Welcome to Wumpus World!\n ******************************************** \n");
//ArrayList<ArrayList<WumpusWorld>> woah = new ArrayList<ArrayList<WumpusWorld>>();
for (int i = 0 ; i < 5 ; i++) {
WumpusWorldObject [] [] woah = new WumpusWorldObject [5] [5];
System.out.println( "*********************************\n Please enter the exact coordinates of the wumpus (r and c).");
int wumpusR = input.nextInt();
int wumpusC = input.nextInt();
woah[wumpusR][wumpusC].setPoints(-3000);
woah[wumpusR][wumpusC].setWumpus();
if ((wumpusR <= 5 || wumpusC <= 5) && (wumpusR >= 0 || wumpusC >= 0)) {
woah[wumpusR][wumpusC].setStench();
}
if (wumpusC != 0) {
woah[wumpusR][wumpusC - 1].getStench();
}
if (wumpusR != 0) {
woah[wumpusR - 1][wumpusC].setStench();
}
if (wumpusC != 4) {
woah[wumpusR][wumpusC + 1].setStench();
}
if (wumpusR != 4) {
woah[wumpusR + 1][wumpusC].setStench();
}
System.out.println( "**************************************\n Please enter the exact coordinates of the Gold(r and c).");
int goldR = input.nextInt();
int goldC = input.nextInt();
woah[goldR][goldC].setGold();
System.out.println("***************************************\n How many pits would you like in your wumpus world?");
int numPits = input.nextInt();
for (int k = 0 ; k < numPits ; k++) {
System.out.println("Enter the row location of the pit");
int r = input.nextInt();
System.out.println("Enter the column location of the pit");
int c = input.nextInt();
woah[r][c].setPit();
if ((r <= 4 || c <= 4) && (r >= 0 || c >= 0)) {
woah[r][c].setBreeze();
}
if (c != 0) {
woah[r][c - 1].setBreeze();
}
if (r != 0) {
woah[r - 1][c].setBreeze();
}
if (c != 4) {
woah[r][c + 1].setBreeze();
}
if (r != 4) {
woah[r + 1][c].setBreeze();
}
}
for (int x = 0 ; x < 4 ; x++) {
int j = 0;
while (j < 4) {
agentpts = agentpts + woah[x][j].getPoints();
Agent [] [] k = new Agent [4] [4];
if (woah[x][j].getWumpus() == true) {
agentpts = agentpts + woah[x][j].getPoints();
System.out.println("You just got ate by the wumpus!!! THE HORROR!! Your score is " + agentpts);
}
if (woah[x][j].getStench() == true) {
k[x][j].stench = true;
System.out.println("You smell something funny... smells like old person.");
}
if (woah[x][j].getBreeze() == true) {
k[x][j].breeze = true;
System.out.println("You hear a breeze. yeah");
}
if (woah[x][j].getPit() == true) {
agentpts = agentpts + woah[x][j].getPoints();
System.out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH! you dumb bith, your dead now.");
}
// if breeze or stench, if breeze and stench, if nothing, etc then move.
k[x][j].safe = true;
// if(k[i][j].isSafe()!=true){
// } else { }
}
}
}
}
}
Here is my class object that I'm trying to implement:
package wumpusworld;
/**
*
* #author Jacob
*/
public class WumpusWorldObject {
private boolean stench;
private boolean breeze;
private boolean pit;
private boolean wumpus;
private boolean gold;
private int points;
private boolean safe;
public WumpusWorldObject(){
}
public boolean getPit() {
return pit;
}
public void setPit() {
this.pit = true;
}
public boolean getWumpus() {
return wumpus;
}
public void setWumpus() {
this.wumpus = true;
}
public int getPoints() {
return points;
}
public void setPoints(int points) {
this.points = points;
}
public boolean getStench() {
return stench;
}
public void setStench() {
this.stench = true;
}
public boolean getBreeze() {
return breeze;
}
public void setBreeze() {
this.breeze = true;
}
public boolean getSafe() {
return safe;
}
public void setSafe() {
this.safe = true;
}
public void setGold(){
this.gold=true;
}
}
Creating array doesn't mean it will be automatically filled with new instances of your class. There are many reasons for that, like
which constructor should be used
what data should be passed to this constructor.
This kind of decisions shouldn't be made by compiler, but by programmer, so you need to invoke constructor explicitly.
After creating array iterate over it and fill it with new instances of your class.
for (int i=0; i<yourArray.length; i++)
for (int j=0; j<yourArray[i].length; j++)
yourArray[i][j] = new ...//here you should use constructor
AClass[][] obj = new AClass[50][50];
is not enough, you have to create instances of them like
obj[i][j] = new AClass(...);
In your code the line
woah[wumpusR][wumpusC].setPoints(-3000);
must be after
woah[wumpusR][wumpusC] = new WumpusWorldObject();
.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
Is there a way that I can optimize this code as to not run out of memory?
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.Stack;
public class TilePuzzle {
private final static byte ROWS = 4;
private final static byte COLUMNS = 4;
private static String SOLUTION = "123456789ABCDEF0";
private static byte RADIX = 16;
private char[][] board = new char[ROWS][COLUMNS];
private byte x; // Row of the space ('0')
private byte y; // Column of the space ('0') private String representation;
private boolean change = false; // Has the board changed after the last call to toString?
private TilePuzzle() {
this(SOLUTION);
int times = 1000;
Random rnd = new Random();
while(times-- > 0) {
try {
move((byte)rnd.nextInt(4));
}
catch(RuntimeException e) {
}
}
this.representation = asString();
}
public TilePuzzle(String representation) {
this.representation = representation;
final byte SIZE = (byte)SOLUTION.length();
if (representation.length() != SIZE) {
throw new IllegalArgumentException("The board must have " + SIZE + "numbers.");
}
boolean[] used = new boolean[SIZE];
byte idx = 0;
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
char digit = representation.charAt(idx++);
byte number = (byte)Character.digit(digit, RADIX);
if (number < 0 || number >= SIZE) {
throw new IllegalArgumentException("The character " + digit + " is not valid.");
} else if(used[number]) {
throw new IllegalArgumentException("The character " + digit + " is repeated.");
}
used[number] = true;
board[i][j] = digit;
if (digit == '0') {
x = i;
y = j;
}
}
}
}
/**
* Swap position of the space ('0') with the number that's up to it.
*/
public void moveUp() {
try {
move((byte)(x - 1), y);
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
/**
* Swap position of the space ('0') with the number that's down to it.
*/
public void moveDown() {
try {
move((byte)(x + 1), y);
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
/**
* Swap position of the space ('0') with the number that's left to it.
*/
public void moveLeft() {
try {
move(x, (byte)(y - 1));
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
/**
* Swap position of the space ('0') with the number that's right to it.
*/
public void moveRight() {
try {
move(x, (byte)(y + 1));
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
private void move(byte movement) {
switch(movement) {
case 0: moveUp(); break;
case 1: moveRight(); break;
case 2: moveDown(); break;
case 3: moveLeft(); break;
}
}
private boolean areValidCoordinates(byte x, byte y) {
return (x >= 0 && x < ROWS && y >= 0 && y < COLUMNS);
}
private void move(byte nx, byte ny) {
if (!areValidCoordinates(nx, ny)) {
throw new IllegalArgumentException("(" + nx + ", " + ny + ")");
}
board[x][y] = board[nx][ny];
board[nx][ny] = '0';
x = nx;
y = ny;
change = true;
}
public String printableString() {
StringBuilder sb = new StringBuilder();
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
sb.append(board[i][j] + " ");
}
sb.append("\r\n");
}
return sb.toString();
}
private String asString() {
StringBuilder sb = new StringBuilder();
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
sb.append(board[i][j]);
}
}
return sb.toString();
}
public String toString() {
if (change) {
representation = asString();
}
return representation;
}
private static byte[] whereShouldItBe(char digit) {
byte idx = (byte)SOLUTION.indexOf(digit);
return new byte[] { (byte)(idx / ROWS), (byte)(idx % ROWS) };
}
private static byte manhattanDistance(byte x, byte y, byte x2, byte y2) {
byte dx = (byte)Math.abs(x - x2);
byte dy = (byte)Math.abs(y - y2);
return (byte)(dx + dy);
}
private byte heuristic() {
byte total = 0;
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
char digit = board[i][j];
byte[] coordenates = whereShouldItBe(digit);
byte distance = manhattanDistance(i, j, coordenates[0], coordenates[1]);
total += distance;
}
}
return total;
}
private class Node implements Comparable<Node> {
private String puzzle;
private byte moves; // Number of moves from original configuration
private byte value; // The value of the heuristic for this configuration.
public Node(String puzzle, byte moves, byte value) {
this.puzzle = puzzle;
this.moves = moves;
this.value = value;
}
#Override
public int compareTo(Node o) {
return (value + moves) - (o.value + o.moves);
}
}
private void print(Map<String, String> antecessor) {
Stack toPrint = new Stack();
toPrint.add(SOLUTION);
String before = antecessor.get(SOLUTION);
while (!before.equals("")) {
toPrint.add(before);
before = antecessor.get(before);
}
while (!toPrint.isEmpty()) {
System.out.println(new TilePuzzle(toPrint.pop()).printableString());
}
}
private byte solve() {
if(toString().equals(SOLUTION)) {
return 0;
}
PriorityQueue<Node> toProcess = new PriorityQueue();
Node initial = new Node(toString(), (byte)0, heuristic());
toProcess.add(initial);
Map<String, String> antecessor = new HashMap<String, String>();
antecessor.put(toString(), "");
while(!toProcess.isEmpty()) {
Node actual = toProcess.poll();
for (byte i = 0; i < 4; ++i) {
TilePuzzle t = new TilePuzzle(actual.puzzle);
try {
t.move(i);
} catch(RuntimeException e) {
continue;
}
if (t.toString().equals(SOLUTION)) {
antecessor.put(SOLUTION, actual.puzzle);
print(antecessor);
return (byte)(actual.moves + 1);
} else if (!antecessor.containsKey(t.toString())) {
byte v = t.heuristic();
Node neighbor = new Node(t.toString(), (byte)(actual.moves + 1), v);
toProcess.add(neighbor);
antecessor.put(t.toString(), actual.puzzle);
}
}
}
return -1;
}
public static void main(String... args) {
TilePuzzle puzzle = new TilePuzzle();
System.out.println(puzzle.solve());
}
}
The problem
The root cause is the tons of String objects you are creating and storing in the toProcess Queue and the antecessor Map. Why are you doing that?
Look at your algorithm. See if you really need to store >2 million nodes and 5 million strings in each.
The investigation
This was hard to spot because the program is complex. Actually, I didn't even try to understand all of the code. Instead, I used VisualVM – a Java profiler, sampler, and CPU/memory usage monitor.
I launched it:
And took a look at the memory usage. The first thing I noticed was the (obvious) fact that you're creating tons of objects.
This is an screenshot of the app:
As you can see, the amount of memory used is tremendous. In as few as 40 seconds, 2 GB were consumed and the entire heap was filled.
A dead end
I initially thought the problem had something to do with the Node class, because even though it implements Comparable, it doesn't implement equals. So I provided the method:
public boolean equals( Object o ) {
if( o instanceof Node ) {
Node other = ( Node ) o;
return this.value == other.value && this.moves == other.moves;
}
return false;
}
But that was not the problem.
The actual problem turned out to be the one stated at the top.
The workaround
As previously stated, the real solution is to rethink your algorithm. Whatever else can be done, in the meantime, will only delay the problem.
But workarounds can be useful. One is to reuse the strings you're generating. You're very intensively using the TilePuzzle.toString() method; this ends up creating duplicate strings quite often.
Since you're generating string permutations, you may create many 12345ABCD strings in matter of seconds. If they are the same string, there is no point in creating millions of instances with the same value.
The String.intern() method allows strings to be reused. The doc says:
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals() method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
For a regular application, using String.intern() could be a bad idea because it doesn't let instances be reclaimed by the GC. But in this case, since you're holding the references in your Map and Queue anyway, it makes sense.
So making this change:
public String toString() {
if (change) {
representation = asString();
}
return representation.intern(); // <-- Use intern
}
Pretty much solves the memory problem.
This is a screenshot after the change:
Now, the heap usage doesn't reach 100 MB even after a couple of minutes.
Extra remarks
Remark #1
You're using an exception to validate if the movement is valid or not, which is okay; but when you catch them, you're just ignoring them:
try {
t.move(i);
} catch(RuntimeException e) {
continue;
}
If you're not using them anyway, you can save a lot of computation by not creating the exceptions in the first place. Otherwise you're creating millions of unused exceptions.
Make this change:
if (!areValidCoordinates(nx, ny)) {
// REMOVE THIS LINE:
// throw new IllegalArgumentException("(" + nx + ", " + ny + ")");
// ADD THIS LINE:
return;
}
And use validation instead:
// REMOVE THESE LINES:
// try {
// t.move(i);
// } catch(RuntimeException e) {
// continue;
// }
// ADD THESE LINES:
if(t.isValidMovement(i)){
t.move(i);
} else {
continue;
}
Remark #2
You're creating a new Random object for every new TilePuzzle instance. It would be better if you used just one for the whole program. After all, you are only using a single thread.
Remark #3
The workaround solved the heap memory problem, but created another one involving PermGen. I simply increased the PermGen size, like this:
java -Xmx1g -Xms1g -XX:MaxPermSize=1g TilePuzzle
Remark #4
The output was sometimes 49 and sometimes 50. The matrices were printed like:
1 2 3 4
5 6 7 8
9 A B C
D E 0 F
1 2 3 4
5 6 7 8
9 A B C
D E F 0
... 50 times