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
CodePudding user response:
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.
CodePudding user response:
As others have written, Java is pass-by-value for non-reference types (such as int or char). The Java way of implementing this is to define your very own object (which will be passed by-reference) 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.