0

My question is why variables for 'Life' won't work even if I think I did it right?
I have main method and in it:

        int destroyer1Life = 4;
        int destroyer2Life = 4;
        int battleShipLife = 5;
        int gameBoardLenght = 10;
        int shipsLife = destroyer1Life + destroyer2Life + battleShipLife;

        while(shipsLife > 0){  //ships life is warned as always true

            char locationViewUpdate = evaluateGuessAndGetTarget(guessLocation, gameBoard, water, hit, miss, destroyer1, destroyer2, battleShip,battleShipLife, destroyer1Life, destroyer2Life, ship );
            if (shipsLife == 0){  //ships life is warned as always false
                System.out.print("You won");
            }

In another method I have:

    private static char evaluateGuessAndGetTarget(int[] guessLocation, char[][] gameBoard, char water, char hit, char miss, char destroyer1, char destroyer2, char battleShip, int battleShipLife, int destroyer1Life, int destroyer2Life,char ship) {

        if (target == destroyer1){
            if (destroyer1Life > 0){
                target = hit;
                message = "Hit!";
                destroyer1Life--; //The value changed at 'destroyer1Life--' is never used 
            }
        }

        if (target == destroyer2){
            if (destroyer2Life > 0){
                target = hit;
                message = "Hit!";
                destroyer2Life--;  //The value changed at 'destroyer2Life--' is never used 
            }
        }

        if (target == battleShip){
            if (battleShipLife > 0){
                target = hit;
                message = "Hit!";
                battleShipLife--;  //The value changed at 'battleShipLife--' is never used 
            }
        }
}

So, even when I get an update on board that ship has been changed into hit, life wont go down. Full code: https://github.com/Mertyon/BattleShipsGame/blob/main/src/com/company/BattleShipsGame.java

Mertyon
  • 1
  • 4
  • [Java is pass-by-value, always](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value). – Turing85 Apr 11 '22 at 19:00

2 Answers2

1

For the context of primitives, like int, Java is pass-by-value. This means that when you pass - for example - destroyer1Life to a method, any change is isolated to that method.

e.g.

/**
 * loop until someValue isn't 1
 */
public void myThing() {
    int someValue = 1;

    while(someValue == 1)
    {
      decrement(someValue);
    }
}

/**
 * Because of pass-by-value, what happens in decrement, stays in decrement
 */
private void decrement(int someValue) {
    someValue--;
}

If you want to modify a value (keeping it simple), you need to reassign it in scope. For example, to fix the above:

/**
 * loop until someValue isn't 1
 */
public void myThing() {
    int someValue = 1;

    while(someValue == 1)
    {
      // modify the value in scope
      someValue = decrement(someValue);
    }
}

/**
 * Because of pass-by-value, what happens in decrement, stays in decrement
 * BUT, if we return it, it can be reassigned in scope.
 */
private int decrement(int someValue) {
    return --someValue;
}

So you can't act on all those values because they aren't being reassigned. It's likely to be simpler to make an object that contains those values, pass that, then modify them on that value. That is where 'pass-by-value' becomes a little less exact in Java (a little too complex to go into here). That object could be a map, a custom POJO, or something else.

Chris J
  • 1,441
  • 9
  • 19
  • "*For the context of primitives, like int, Java is pass-by-value.*" - [Java is **always** pass-by-value](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value). – Turing85 Apr 12 '22 at 14:25
  • 1
    @Turing85 while strictly correct, I think that Chris' explanation will help OP more than quibbles over terminology. Changes to a primitive vs those to a reference type *do* behave similarly to what one usually understands regarding by-value and by-reference. – tucuxi Apr 12 '22 at 15:59
  • @tucuxi - absolutely. The "always pass by value" shtick is useless and meaningless, because the "value" with Objects is purely a reference to a location in the heap, meaning that, in practical terms, it is passing a reference. I find it's more useful to explain it in a meaningful way, than to regurgitate the RTFM stuff that doesn't help someone to learn :) – Chris J Apr 12 '22 at 19:10
0

The Java way of implementing this is to define your very own object to represent the state of the game. So you would have something like:

// State.java
public class State {

    // constants, initialized only once
    public final char WATER = '~';
    public final char HIT = 'X';
    public final char MISS = '·';
    public final char D1 = '1';
    public final char D2 = '2';
    public final char BB = 'B';
    public final char SHIP = 'O';

    // attributes, can be different for different State objects
    private int destroyer1Life = 4;
    private int destroyer2Life = 4;
    private int battleShipLife = 5;
    private int gameBoardLength = 10;
    private char [][] board;

    // a method to calculate remaining life in this State object
    public int getTotalLife() {
        return destroyer1Life + destroyer2Life + battleShipLife;
    }
     
    // a method to update this State object after a guess
    public char update(int[] guessLocation) {
        char target = board[guessLocation[0]][guessLocation[1]];
        char result = '?';
        if (target == 'WATER') {
           System.out.println("Sploof, miss!");
           result = MISS;   
        } else if (target == D1) {
           System.out.println("Hit!");
           result = HIT;
           destroyer1Life --;
        } // add more else ifs here for other boat types
        // ...

        // write new result to displayed board
        board[guessLocation[0]][guessLocation[1]] = result;
        return result;
    }

    // a method to show the state
    public void show() {
        for (int row=0; row<board.length(); row++) {
          for (int col=0; col<board[0].length(); col++) {
             char c = board[row][col];
             // show '?' for all unknown tiles
             System.out.print(c == HIT || c == MISS ? c : '?');
          }
          System.out.println("");
        }
    }
}

// in Main.java
State state = new State();
while (state.getTotalLife() > 0) {
        int[] guessLocation = askForGuess();
        state.update(guessLocation);
        state.show();
        if (shipsLife == 0){ 
            System.out.print("You won");
        }
}

Instead of passing around lots of integers, and having no way to get their changed values back, you group those values into objects, which have their own methods that can freely change the object's values. Much cleaner.

tucuxi
  • 17,561
  • 2
  • 43
  • 74
  • "*As others have written, Java is pass-by-value for non-reference types (such as int or char).*" - [Java is **always** pass-by-value](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value). – Turing85 Apr 12 '22 at 14:26
  • @Turing85 - removed any reference to pass-by-x, because I feel it only confuses the issues at OP's level – tucuxi Apr 12 '22 at 15:54