Getting unexpected return value when appending to array

Hello programmers,

I am trying to write a program for a school excercise and it’s actually coming along quite nicely.
It’s supposed to be a game called subset (a lighter version of a game called “set”).
The game consists of cards, and every card has 3 properties. It has a shape, a color and a number of shapes, this results in a total of 27 (333) unique cards. The cards are shuffled and 9 of them are then printed to a playing field. The goal is to then make sets of three in which either all properties are the same or different in the 3 selected cards. The code I made does all these things but there are some bonus features I’d like to implement.

I’m trying to make an array of an array of Strings (the properties of cards are represented in Strings). These arrays contain 3 strings of cards which form a valid set.
The function below tests for this and i want to append all results and return all these valid sets.

So far so good.

The problem is, the sets i find in the for loop, after checking if all conditions are met with checkSet, are fine. These are the values i want to find if i check them with println(sets[0]).
The value of sets that is returned after the for-loops, when actually returning the set, it always returns the properties of the 7th, 8th and 9th card on the board. And does this as many times as there are valid sets in the game. I have no idea what I’m doing wrong here.

I hope you understand what I’m going for, I’m just baffled that the return value is different from the value in the loop.

String[][] setsOnBoard(String[] cards, int numOfCards, String blank) {
  String[][] sets = new String[0][3];
  String[] threeCards = new String[3];
  
  for (int i = 0; i < numOfCards - 2; i++) {
    if (cards[i] == blank) {
      continue;
    }
    for (int j = i + 1; j < numOfCards - 1; j++) {
      if (cards[j] == blank) {
        continue;
      }
      for (int k = j + 1; k < numOfCards; k++) {
        if (cards[k] == blank) {
          continue;
        }
        threeCards[0] = cards[i];
        threeCards[1] = cards[j];
        threeCards[2] = cards[k];

        if (checkSet(threeCards)) {
          sets = (String[][])append(sets, threeCards);
          println(sets[0]);
        }
      }
    }
  }
  println(sets[0]);
  return sets;
}

example results are:
for the println in the for loop:
B1R R1R G1R

B2R R1R G3R

G1T G3T G2T

for the println near return:
B3T G2T G1R --> these are the 7th 8th and 9th cards on screen

1 Like

Ah, could it be that Processing treats the threeCards object as a pointer, and thus keeps updating its values instead of storing a copy?

It may be part of Java’s shady black magic when managing memory for you. Try creating a new set object inside the for loop like so?

  String[][] setsOnBoard(String[] cards, int numOfCards, String blank) {
  String[][] sets = new String[0][3];
  
  
  for (int i = 0; i < numOfCards - 2; i++) {
    if (cards[i] == blank) {
      continue;
    }
    for (int j = i + 1; j < numOfCards - 1; j++) {
      if (cards[j] == blank) {
        continue;
      }
      for (int k = j + 1; k < numOfCards; k++) {
        if (cards[k] == blank) {
          continue;
        }

        String[] threeCards = new String[3];
        threeCards[0] = cards[i];
        threeCards[1] = cards[j];
        threeCards[2] = cards[k];

        if (checkSet(threeCards)) {
          sets = (String[][])append(sets, threeCards);
          println(sets[0]);
        }
      }
    }
  }
  println(sets[0]);
  return sets;
}
1 Like

Thank you, this worked like a charm. Do you have any idea besides shady black magic, why Java might do this?

I recently wrote this little program, maybe it can help explain what’s going on:

class Test {
  // private might be misleading, as
  // we could change the content of p
  // by calling getPos()
  private PVector p;
  Test(float x, float y) {
    p = new PVector(x, y);
  }
  // other languages have ways to specify that 
  // the returned value can not be modified,
  // but that doesn't exist in Java (afaik), therefore
  // the returned object CAN be changed.
  public PVector getPos() {
    return p;
  }
  // in Java we can return a copy. Which is less
  // efficient, specially if the returned object is
  // heavy. But at least by doing this we make sure
  // the variable (p in this case) is not modified
  // from outside, which might lead to unpredictable results
  public PVector getPosCopy() {
    return p.copy();
  }
  public void print() {
    println(p);
  }
}

void setup() {
  Test t = new Test(4, 6);
  t.print(); // [ 4.0, 6.0, 0.0 ]
  // here we change p even if it's private
  t.getPos().x = 6;
  t.print(); // [ 6.0, 6.0, 0.0 ]
  // here we only get a copy, so we don't
  // change the original
  t.getPosCopy().x = 666;
  t.print(); // [ 6.0, 6.0, 0.0 ]
}

// The program above includes both getPos() and getPosCopy()
// to show their difference.
// In a proper program you may want just one of the two.

It’s not black magic :slight_smile: It’s also not pointers (there’s no pointer arithmetic in Java), but references. I agree it can be surprising when you encounter it for the first time.

1 Like

Yes, if you’re interested it’s more clear when you look at this behaviour in the context of c++. What Processing is doing in the background is basically how pointers/references are used in c++. Instead of passing the actual object, or making a copy of the object, processing uses “shortcuts” to the variable as it were.
When you declare a new variable Processing allocates a little bit of memory. However, when you then start passing it around, different things can happen. For simple datatypes, eg. floats, ints, char — anything that isn’t capitalised Processing will copy the actual thing, but for anything else (including a String) when assigned or passed into a function, it will send a reference to the object. So when you appended threeCards in the example above, you were actually appending a shortcut to the same single object, linking the same threeCards object to each slot in the array. When you moved the declaration and initialisation of the object inside the for loop, you actually made a new instance for each slot in the array.

Luckily, and unlike c++, you don’t have to worry about these objects being created and lingering around. Processing/Java will detect when objects aren’t used anymore and free up their memory. Ie. an object declared/created inside a for loop will persist if you assign it / refer to it from a place in an array like we did above. Had you not appended it to the array, Java would’ve killed the unused threeCard instances silently in the background. Quite murderous.

Lastly, the behaviour as described below you can totally use to your advantage. Share a single object to multiple instances through functions and/or constructor to other objects and you can adjust their behaviour from a single place afterwards.

1 Like