0

Trying to code procedural generation for a game for the first time but I'm having trouble with implementing positive and negative coordinates on a 2D plane.

Right now I have 4 ArrayLists representing each of the 4 quadrants but this feels inefficient and is also annoying to work with. Is there a way to get an ArrayList that can have negative indices, or am I just trying to make this the wrong way?

  • I'd probably just put that list behind your own abstraction that works with negative indices. Then, your abstraction could translate between those and the zero-based ones of the `ArrayList` it contains. Maybe that would mean storing two lists - one for each direction, I don't know. I do think the key thing though is creating the abstraction that you want: how do _you_ want to work with these coordinates? – ndc85430 Apr 20 '23 at 06:12

2 Answers2

0

You could do index manipulation. Basically, you could make a list which alternates between a positive and negative element.

If i is an index in your application, you can calculate the actual index using i>=0 ? i*2 : i*2+1. However, this would require extra memory for possible unused coordinates in the other direction. Also, you would need to store the size of both the positive and negative part distinctively:

public class BidirectionalList<T>{
    private List<T> data=new ArrayList<>();
    private int posSize=0;
    private int negSize=0;
    public void insert(T value, boolean positive){
        if(positive){
            if(posSize>negSize){
                data.add(value);//add positive element 
                data.add(null);//add negative placeholder 
            }else{
                data.set((posSize+1)*2, value);//set position
            }
        } else {
            if(negSize>posSize){
                data.add(null);//add positive placeholder 
                data.add(value);//add negative element 
            }else{
                data.set((negSize+1)*2+1, value);//set position
            }
        }
    }
    public T get(int i){
        if(!containsIndex(i)){
            throw new IndexOutOfBoundsException();
        }
        return data.get(i>=0 ? i*2 : i*2+1);
    }
    public boolean containsIndex(int i){
        return i>=0 ? posSize>=i : negSize> -i;
    }

}

Note that this approach loses a bit of locality and uses unnecessary space in case the positive and negative size are different.

Alternatively, you could create your own data structure with an array growing in both directions. That would require you to save an offset. That approach would not waste as much space if there is a significant difference between the positive and negative size.

public class BidirectionalList<T>{
    private static final int INITIAL_SIZE = 32;//how big the array is supposed to be by default
    private static final int GROWTH_FACTOR = 1.25;//how much the array should be increased if it runs out of space
    private T[] data = (T[])new Object[INITIAL_SIZE];
    private int origin = data.length/2;//position of semantic index 0
    private int positiveLength = 0;//number of positive elements
    private int negativeLength = 0;//number of negative elements
    public void insert(T value, boolean positive){
        int i;
        if(positive){
            i = positiveLength + origin;
            if(i>=data.length){//grow positively
                data=Arrays.copyOf(data, (int)(data.length*GROWTH_FACTOR));//create copy with bigger size
            }
            positiveLength++;
        }else{
            i = origin - negativeLength - 1;
            if(i<0){//grow negatively
                T[] newData = (T[]) newObject[(int)(data.length*GROWTH_FACTOR)]
                int lengthDiff = newData.length-data.length;
                System.arraycopy(data, 0, newData, lengthDiff, data.length);//copy elements over
                origin+=lengthDiff;
                data = newData;
                i += lengthDiff;
            }
            negativeLength++;
        }
        data[i]=value;
    }
    public T get(int i){
        if(!containsIndex(i)){
            throw new IndexOutOfBoundsException();
        }
        return data[i+origin];
    }
    public boolean containsIndex(int i){
        return i>=0 ? i<positiveLength : -i<negativeLength;
    }

}

This preserves locality of the references as neighbouring elements are always next to each other.

Since you want to implement a coordinate plane, I did not include inserting at arbitrary positions but only at the positive and negative ends. Also note that storing your whole coordinate plane might be memory intensive so you might only want to store the part you are currently interested in.

dan1st
  • 12,568
  • 8
  • 34
  • 67
  • Do you know that you can [edit] your answer - rather than deleting it and posting another answer. You can even edit your answer **after** you have deleted it and then undelete it. – Abra Apr 20 '23 at 07:09
  • Yes. I accidentially posted my answer twice due to network issues (one of them containing the 'old' version) - When I realized that I deleted one of them. – dan1st Apr 20 '23 at 07:10
0

Maybe you can use Hashmap instead of ArrayList:

public class Quadrant {
    private HashMap<String, Object> map;

    public Quadrant () {
        map = new HashMap<String, Object>();
    }

    public void add(int x, int y, Object obj) {
        String key = x + "," + y;
        map.put(key, obj);
    }

    public Object get(int x, int y) {
        String key = x + "," + y;
        return map.get(key);
    }
}

and usage:

Quadrant q = new Quadrant();
q.add(3, -5, myObject);
Object obj = q.get(3, -5);
gurkan
  • 509
  • 1
  • 4
  • 18