Problem with adding and reaching elements to ArrayList in Java - java

I am working on 2D graphical optimal fitting project. I was coding in C++ and changed to Java, therefore I know the algorithm works. But I am having a problem with reaching elements of ArrayList outside of the scope of loop or more likely adding to an ArrayList.
I can reach to an element I want in this scope, but outside of this scope some elements are lost. I know this is irrelevant and probably something occurs beyond my attention.
Triangle Class:
public class Triangle implements Shape, Cloneable
{
private double length; // size of equaliteral triangle's each edge.
private double x,y;
private boolean rotate; // Flag for rotate by 90 degress around pos(x,y)
private boolean fill; // Flag for fill
private static double total_area = 0;
private static double total_perim = 0;
private int[] xPoints;
private int[] yPoints;
.
...
}
Definitions:
Triangle t2 = (Triangle)small;
Triangle t = (Triangle)t2.clone();
List<Shape> shape = new ArrayList<Shape>();
In the code below, I draw it as soon as I added it into the List. Method draw() does not matter in this case, it only uses fields such as x and y.
Code 1:
// (a,width-a*sqrt(3)) init for fill inside without rotating
for(y = r1.getHeight()-tri_height;y>=0;y-=tri_height)
{
x=t.getLength()/2.0;
while(x+t.getLength()/2.0<=r1.getWidth())
{
t.setPosition(x+_x,y+_y);
shape.add((Triangle)t.clone());
shape.get(size).draw(g); // check this line.
++size;
x+=t.getLength();
}
}
In this same piece of code, I only draw/print them after insertion is done.
Code 2:
// (a,width-a*sqrt(3)) init for fill inside without rotating
for(y = r1.getHeight()-tri_height;y>=0;y-=tri_height)
{
x=t.getLength()/2.0;
while(x+t.getLength()/2.0<=r1.getWidth())
{
t.setPosition(x+_x,y+_y);
shape.add((Triangle)t.clone());
x+=t.getLength();
}
}
for(Shape s:shape)
s.draw(g);
clone() method:
#Override
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
Output 1(Wrong)
Output 2(Expected)
I am only using draw() method to show the difference better. The problem is the elements are gone after the scope. Or I didn't achieve to add them properly. It shows me that last element I added instead of every element I appended. What am I missing in this case?

It seems that your problem is in Triangle.clone() method. You have references in Triangle, like int[] xPoints or int[] yPoints.
Defualt implementation of Object.clone() works only for simple types, but not for references.
Triangle t = (Triangle)t2.clone();
t.xPoints == t2.xPoints; // this is same array (not a copy)
All your rectangles are drawn on the same place.
How to solve
Do not use clone() method. In general it is outdated. You have to create like C++ copy constructor and manually create copy of your object.
public class Triangle implements Shape {
private static double total_area = 0;
private static double total_perim = 0;
private double length;
private double x,y;
private boolean rotate;
private boolean fill;
private int[] xPoints;
private int[] yPoints;
public Triangle(Triangle triangle) {
this.length = triangle.length;
this.x = triangle.x;
this.y = triangle.y;
this.rotate = triangle.rotate;
this.fill = triangle.fill;
this.xPoints = xPoints != null ? Arrays.copyOf(xPoints, xPoints.length) : null;
this.yPoints = yPoints != null ? Arrays.copyOf(yPoints, yPoints.length) : null;
}
}
P.S.
int[] xPoints: xPoints - is not an array, this is reference to the array of int.
int[] xPointsCopy = xPoints: xPointsCopy - this is another reference to the same array of int.

Related

List copies final element to all other elements when converting from image to GameObject

I am working on a small project using Java and Swing. I'm making a very, very small and simple RPG that is in the style of Earthbound for mainly it's combat style. Alongside this project, I'm working on a framework to make it easier to create games of a similar genre and feel (i.e. screen, game loop, state manager, audio manager, etc). The issue I'm running into is that the "image to game object" converter class I made is overwriting the list with the final game object that is created. I am converting from a PNG to a GameObject (class created within the framework).
I diagnosed this by printing out the position and type of all the elements in the list to receive the same X, Y, and types for each element. I checked to make sure the HashMap I passed through was setup correctly and that each tile type was added correctly. Below is the relevant section of code to the top along with it's output:
private void init() {
tileMap.put(new Color(0,255,0), new Tile(16,16, TileType.GRASS));
tileMap.put(new Color(250,255,0), new Tile(16,16, TileType.SAND));
tileMap.put(new Color(135,135,135), new Tile(16,16, TileType.ROCK));
tileMap.put(new Color(255,0,233), new Tile(16,16, TileType.DIRT_PATH_SIDE));
for(Color t : tileMap.keySet()) {
System.out.println(((Tile) tileMap.get(t)).getTileType());
}
....
}
----- OUTPUT -----
GRASS
ROCK
DIRT_PATH_SIDE
SAND
The image loads correctly and the HashMap's values are added correctly so I decided to see where in the framework the issue was. I checked the position when it was being set and it was fine (ie (0,0), (0,16), (0,32)...) until I looped through the finished list where the game objects in the list are all the same.
I know that static variables cause an issue but the only "static" variable I am using is the enums to denote what the tile type is. I used an integer just to see what would happened and yielded the same result. I took the final list, made it a variable the class could read as opposed to just the function, and tried to see if that would work but still was just the final game object created after reading.
I've checked over SO and other websites but nothing is working. I fear that I looked at this problem the wrong way as I was trying to implement a solution I used in Unity/C# here but probably had a disconnect somewhere along the way.
Here is the full function from earlier:
private void init() {
tileMap.put(new Color(0,255,0), new Tile(16,16, TileType.GRASS));
tileMap.put(new Color(250,255,0), new Tile(16,16, TileType.SAND));
tileMap.put(new Color(135,135,135), new Tile(16,16, TileType.ROCK));
tileMap.put(new Color(255,0,233), new Tile(16,16, TileType.DIRT_PATH_SIDE));
for(Color t : tileMap.keySet()) {
System.out.println(((Tile) tileMap.get(t)).getTileType());
}
try {
im.loadImage();
} catch (IOException e) {
e.printStackTrace();
}
im.readImage(tileMap);
objects = im.getMap(); // objects is a list within the class that contains all Tile game objects.
}
And the converter class's relevant code:
public void readImage(HashMap<Color, GameObject> tileDict) {
for(int x=0; x<img.getWidth(); x++) {
for(int y=0; y<img.getHeight(); y++) {
GameObject g = determineObject(tileDict, x, y);
if(g != null) {
map.add(g);
}
}
}
}
private GameObject determineObject(HashMap<Color, GameObject> tileDict,
int x, int y) {
GameObject g = null;
for(Color c : tileDict.keySet()) {
if(c.getRGB() == img.getRGB(x, y)) {
g = tileDict.get(c);
g.setXPos(x*g.getWidth());
g.setYPos(y*g.getHeight());
}
}
return g;
}
--- EDIT: 8/10/2017 ---
I am sorry for not clarifying what the structure looks like. GameObject is an abstract class within the framework I've made that is extended by Tile in the game. As well, TileType is an enum type that I created within the game and not the framework. The framework is in a separate jar/project so it is unaware of what extends GameObject and what TileType is.
--- EDIT 2: 8/10/2017 ---
I tried implementing a solution using the Serializableclass but that didn't work. I followed the example from this question. The copy() function I created in the abstract GameObject looks like this:
public final GameObject copy() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ObjectOutputStream(baos).writeObject(this);
return (GameObject) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject();
} catch (Exception e) {
throw new AssertionError();
}
}
I have also tried this as an abstract method and wrote the function in Tile but there is no current result. I'll keep mucking around here to see if anything comes up.
Resources
Java ArrayList.add() replaces all elements in array with the final element
List overriding objects java [closed]
Add elements to Arraylist and it replaces all previous elements in Java [duplicate]
Why does my ArrayList contain N copies of the last item added to the list?
I think, the problem here is, that you get the GameObject from the map and then modify it. So if you get the same GameObject twice, it gets modified two times and also gets added to the list twice.
To have different GameObject's, you have to create a new one in determineObject().
BTW: it's very confusing to name a list map.
If I got it correctly tileMap is a static member of your class and you pass it to the readImage and thus to the determineObject method?!
In determineObject you change the xPos and yPos attributes of your objects in your static tile dictionary:
g = tileDict.get(c);
g.setXPos(x*g.getWidth());
g.setYPos(y*g.getHeight());
That is you modify the attributes of an entry in the global map.
You will need to create a copy of the GameObject instance you get from the map and continue with the copy instead of modifying the original object. Your code should do something like this:
GameObject globalGameObject = tileDict.get(c);
g = globalGameObject.createCopy();
g.setXPos(x*g.getWidth());
g.setYPos(y*g.getHeight());
Where createCopy() is a new method that creates a copy of the current instance. Each implementation of GameObject has to implement this method:
public abstract class GameObject {
...
public abstract GameObject createCopy();
...
}
You could use the Cloneable feature of Java to create the copies for you as well, but Cloneable is easy to use wrong and I've actually not seen anyone using it for real life code till now.
Alright so I found out the issue. While doing research on dpr's and Claus Radloff's answers, I came across the solution here. Seeing as in my case I was using an abstract class, I could not do what both Claus and dpr recommended per-say as you can't instantiate an abstract class. In order to solve this, I made the abstract class implement the Serializable class as I said in my question. I created the abstract method GameObject copy(); which the objects extending from this can implement or ignore (return null). From here, only one line of code is required inside said method:
return new Tile(this.width, this.height, this.type);
A big thank you to those that guided me in the right direction! I apologize again for not clarifying my structure in my initial post.
I think your map should containe TileTypes instead of Tiles:
tileMap.put(new Color(0,255,0), TileType.GRASS);
tileMap.put(new Color(250,255,0), TileType.SAND);
tileMap.put(new Color(135,135,135), TileType.ROCK);
tileMap.put(new Color(255,0,233), TileType.DIRT_PATH_SIDE);
then method determineObject would become:
private GameObject determineObject(HashMap<Color, GameObject> tileDict,
int x, int y) {
GameObject g = null;
for(Color c : tileDict.keySet()) {
if(c.getRGB() == img.getRGB(x, y)) {
TileType type = tileDict.get(c);
g = new Tile(x, y, type);
g.setXPos(x*g.getWidth());
g.setYPos(y*g.getHeight());
// BTW, how about a break here?
}
}
return g;
}
Or even:
private GameObject determineObject(HashMap<Color, GameObject> tileDict,
int x, int y) {
GameObject g = null;
Color c = new Color(img.getRGB(x, y));
TileType type = tileDict.get(c);
if (type != null) {
g = new Tile(x, y, type);
g.setXPos(x*g.getWidth());
g.setYPos(y*g.getHeight());
}
return g;
}
UPDATE
I forgot to multiply x and y by the tile width. With my approach, you cannot use g.getWidth() and g.getHeight(), but I think in your case their values are both 16. Ill will add constants:
private static final int TILE_WIDTH = 16;
private static final int TILE_HEIGHT = 16;
and the method determineObject becomes:
private GameObject determineObject(HashMap<Color, GameObject> tileDict,
int x, int y) {
GameObject g = null;
Color c = new Color(img.getRGB(x, y));
TileType type = tileDict.get(c);
if (type != null) {
g = new Tile(TILE_WIDTH, TILE_HEIGHT, type);
g.setXPos(x*TILE_WIDTH);
g.setYPos(y*TILE_HEIGHT);
}
return g;
}

How to get the same instance of a class in Java?

Good morning,
I have one little problem.
I created one class named Map(). In this class there in one method that generate an Array. Then i created other two classes (Top and Bottom) that extend Map. Then I created 2 objects. One of Top and one of Bottom. I want to get the same array for the Top's object and for the Bottom's object. Here is the code Source:
public class Map{
public Map(){}
public int [] yTopValues()
{
int [] arrayTopY = new int[100];
for(int i=0;i<100;i++)
arrayTopY[i]=randomInt(-50,50);//it puts in i-th position an int between 50 and -50
return arrayTopY;
}
public int [] yBottomValues()
{
int [] arrayBottomY = yTopValues;
for(int i=0;i<100;i++)
arrayBottomY[i]=arrayBottomY[i]-250;
return arrayBottomY;
}
public int [] xValues()
{
int [] arrayX = new int[100];
for(int i=0;i<100;i++)
arrayX[i]=randomInt(0,50);//it puts in i-th position an int between 0 and 50
return arrayX;
}
//other stuff
}
public class TopMap extends Map{
public TopMap(){
this.area=new Area(new Polygon(
this.xValues,
this.yTopValues,
200)
);
}
public class BottomMap extends Map{
public BottomMap(){
this.area=new Area(new Polygon(
this.xValues,
this.yBottomValues,
200)
);
}
In the View class I created two objects one of TopMap and one of BottomMap then I drew the areas with g2.draw(topMap.area) and g2.draw(bottomMap.area)
I need the 2 polygons to be similar, but both of them are different because the method is executed twice. What should I do?
Thank you very much!!
The class Map does not hold the array. It is local only to the xValues() method. If you want the other classes to get that exact array do this:
/*
* Since this is a variable in the CLASS field, this object will
* be accessible to the child class.
*/
private int[] arrayX = new int[100];
public Map(){
createArrayX(); // This makes sure that the array is created, or else
// every value inside of it will be 0.
}
public void createArrayX(){ // Exact same thing as xValues(), but without the return type
for(int i=0;i<100;i++){
arrayX[i]=randomInt(0,50);
}
}
public int[] getArrayX(){ // The method that gets the array.
return arrayX;
}

Java - Saving values for use in a different function

So this is a stupid dbeginner's question.
I wrote a function that checks if a specific game move is legal (Reversi). The function must only return a boolean true/false value.
Later, in a different function, I actually make the move (makeMove function). In this function, before making the move I call the isLegal function to make sure the move is legal.
Now, when the isLegal function decides the move is legal, it would help me to save the specific info that lead to the decision, and use it in the makeMove function. I have no ideah ow to do that. I tried writing a function that will store the relevant data, and then send it back, but there's an obvious provlem with scopes here.
So here's the relevant code from isLegal:
else if(board[k][l]==player){relevantDirection=false; isLegal=true; ReversiPlay.saveLegalMove(direction, k, l);}
Then the problematic saving function:
public static int[] saveLegalMove(int direction, int row, int column){
if(direction==0){ //get info from function
return legalMoveData;
}
else{ //save legal move data
int[] legalMoveData = new int[3];
legalMoveData[0]= direction;
legalMoveData[1]= row;
legalMoveData[2]= column;
return null;
}
}
And lastly, I try calling the stored data:
int[] getSavedInfo = ReversiPlay.saveLegalMove(0, 0, 0);
I'm sure there's a very simple way of pulling the variables direction+k+l... anyone?
Thanks!
Edit: Here's a clearer example:
public static boolean A(int a){
...calculations...
int x = [value]
int y = [value]
return false;}
public static void B(int a){
...calculations...
boolean h = A(3);
[here I'd like to know what x,y were]
}
else { //save legal move data
int[] legalMoveData = new int[3];
legalMoveData[0]= direction;
legalMoveData[1]= row;
legalMoveData[2]= column;
return null;
}
This part doesn't save anything. It stores the values to a local variable and returns null.
An approach would be to make a Move object that contains the data you need:
public class Move {
private int direction;
private int row;
private int column;
...
}
Your isLegal method would only tell you if the move is legal. Then you can use that same Move instance to make the move.
if(ReversiPlay.isLegalMove(move)) {
ReversiPlay.makeMove(move);
}
There is no need to explicitly save the move; you already have that information inside the Move object instance.
UPDATE
Based on your edit, perhaps it is better to return an object instead of a boolean from A:
public static boolean A(int a) {
...calculations...
int x = [value]
int y = [value]
return new MyObject(x, y, false);
}
...
MyObject myObject = A(someValue);
Then you can query myObject to see what the value of the flag is.
UPDATE
You mentioned you aren't allowed to use objects. If so, instead of making the same calculations however, you can extract that logic into its own method and then call that. That way you won't have to duplicate logic.

Dealing with ArrayList and pass by reference

I'm using a arraylist to add states(the board state for the 8 puzzle). My problem is when I get the children of the state it changes the values stored in my array list. I'm assuming this is because ArrayList just stores pointers to the objects and not the values themselves. In order to fix this I create a new object every time before I store it into the ArrayList but I'm still having the same problem.
I will also try to follow naming conventions more often thanks for the tip.
private ArrayList<int[][]>VisitedBoard;
if(RuleNumber ==2){
//Here is my problem. This will change what is stored in VistedBoards
NextState = new State(FireRule.Rule2(WM.get_Board()));//Fire Rule
for(int j=0;j<VisitedBoards.size();j++){
//Meaning this will always be true
if(Arrays.equals(VisitedBoards.get(j), NextState.get_Board())){
Loop =true; //Loop to previous state
}
if(j==VisitedBoards.size()-1 && Loop ==false){ //If the next state is not any previously visited
NotALoop =true;
VisitedBoards.add(NextState.get_Board());
WM.set_Board(NextState.get_Board());
}
}
}
public int[][] Rule2(int [][] Board){//The FireRule Class
Find_BlankLocation(Board);
int temp;
State NewState;
temp = Board[BlankLocation[0]-1][BlankLocation[1]];
Board[BlankLocation[0]-1][BlankLocation[1]] = 0;
Board[BlankLocation[0]][BlankLocation[1]] = temp;
NewState = new State(Board);
return Board;
}
public class State { //State class
private int[][] Board;
private int[][] Goal;
private Boolean GoalFound;
public State(int[][] Start, int[][] goal){
Board = Start;
Goal = goal;
GoalFound=false;
}
public State(int[][] NewState){
Board=NewState;
}
public int[][] get_Goal(){
return Goal;
}
public int[][] get_Board(){
return Board;
}
public void set_Board(int[][] board){
Board = board;
}
public Boolean get_GoalFound(){
return GoalFound;
}
}
Containers like ArrayList work the same in all languages: they are called data structures because they organize storage/retrieval of objects. Obviously they don't store the fields of the objects themselves.
Trying to interpret your problem, maybe you don't want to share the boards between the list of visitedBoards and WM (whatever it means...). Then simply implement get_Board() to return a copy of the array instead of the Board object itself:
public int[][] get_Board(int[][] src) {
int[][] dst = new int[src.length][src[0].length];
for (int i = 0; i < src.length; i++) {
System.arraycopy(src[i], 0, dst[i], 0, src[i].length);
}
return dst;return dst;
}
Beside this, as others already told you, you'd really better to adopt the standard Java naming conventions, use meaningful names, and encapsulate your x, y and int[][] in real application classes.
Presumably, the new State object contains a pointer to the same arrayList as before. You'll want to manually copy the array out to a new one (a "deep clone" or "deep copy" as it is called). You might find this useful: Deep cloning multidimensional arrays in Java...?
Every time you create a new instance of State, you pass it the same array (whatever is returned by WM.get_Board()).
You then add that same array to VisitedBoards when you call VisitedBoards.add().
The fact that you're creating new State objects is irrelevant, because only the return value of NextState.get_Board() gets added to the list.
As a result, the list VisitedBoards always contains several references to the exact same array.
As Raffaele has suggested, you'll be fine if you make sure get_Board() returns a copy of the array in stead of a reference to the original (assuming that doesn't mess up logic that exists elsewhere).
The main thing I learned from this question is how important it is to follow naming conventions.
Your unconventional capitalization has made me dizzy!
Following these rules will make it much easier for others to understand your Java code:
class names should be capitalized (ie PascalCase)
variable names should be lowercase (ie camelCase)
do not use underscores in method names, class names, or variable names (they should only be used for constants)
always use meaningful names when possible
My advice is to create your own container object for their 2D array and implement deep copying.
For example:
package netbeans;
import java.util.Arrays;
public class Container
implements Cloneable
{
private int [] _data;
private int _sx;
private int _sy;
public int get(int x, int y)
{
try { return this._data[y*this._sx+x]; }
catch (Exception e) { throw new ArrayIndexOutOfBoundsException(); }
}
public void set(int x, int y, int value)
{
try { this._data[y*this._sx+x] = value; }
catch (Exception e) { throw new ArrayIndexOutOfBoundsException(); }
}
public Object Clone() { return new Container(this); }
public Container(int sizeX, int sizeY, int [] data)
{
this._sx = sizeX;
this._sy = sizeY;
this._data = data;
}
public Container(Container cont)
{
this._data = Arrays.copyOf(cont._data, cont._data.length);
}
}

Java Awt Paint Method's Variable Inconsistency

I'm not sure what is wrong but there's some weird happening in my Paint() concerning some variables.
this code works just fine:
public void paint(Graphics g)
{
Point[] positions = {new Point(20,50),new Point(60,30),new Point(80,20),new Point(80,30)};
}
but this one don't, i wanted this one, because im changing position formations on user's selection:
// declared somewhere
Point[] selectedFormation = {new Point(20,50),new Point(60,30),new Point(80,20),new Point(80,30)};
public void paint(Graphics g)
{
Point[] positions = selectedFormation;
}
when you do positions = selectedFormation you are not creating a copy of selectedFormation, you are just storing a reference of it into position. Both point to the same object (the same array). If the array is changed by using position, it is the same array as selectedFormation.
Use clone() to create a copy of the array:
public void paint(Graphics g)
{
Point[] positions = selectedFormation.clone();
}
but consider that clone does not copy the elements of the array, both list will contain the same instances. If you only change the coordinates of any point, it will affect both lists. In that case you need to make a deep copy of the list:
public Point[] deepCopy(Point[] array) {
Point[] copy = new Point[array.length];
for (int i = 0; i < array.length; i++) {
copy[i] = new Point(array[i]);
}
return copy;
}
could use generalization for this... but lets keep it simple

Categories

Resources