GregT
GregT

Reputation: 1099

Can I make a method do return for the caller method?

I'd like to DRY up this code:

YourConsultant.GameState gameState() {

    for (int i = 0; i < 3; i++) {
        // an 'xxx' column returns 'x', an 'ooo' returns 'o', a mixed row returns '0'
        char c = areTheSame(board[i][0], board[i][1], board[i][2]);
        if (c == 'x') {return YourConsultant.GameState.WON_BY_X;}
        else if (c == 'o') {return YourConsultant.GameState.WON_BY_O;}
    }

    for (int i = 0; i < 3; i++) {
        char c = areTheSame(board[0][i], board[1][i], board[2][i]);
        if (c == 'x') {return YourConsultant.GameState.WON_BY_X;}
        else if (c == 'o') {return YourConsultant.GameState.WON_BY_O;}
    }

    {
        char c = areTheSame(board[0][0], board[1][1], board[2][2]);
        if (c == 'x') {return YourConsultant.GameState.WON_BY_X;}
        else if (c == 'o') {return YourConsultant.GameState.WON_BY_O;}
    }

    {
        char c = areTheSame(board[0][2], board[1][1], board[2][0]);
        if (c == 'x') {return YourConsultant.GameState.WON_BY_X;}
        else if (c == 'o') {return YourConsultant.GameState.WON_BY_O;}
    }

    ...
}

For that end, I want to write a short method which does this:

        if (c == 'x') {return YourConsultant.GameState.WON_BY_X;}
        else if (c == 'o') {return YourConsultant.GameState.WON_BY_O;}

But this would make the new method return. I suppose I cannot do something like super.return? I could check the return value again, but it wouldn't make my code DRY. What do you suggest? (Sorry if it's been asked before, I found this difficult to search for)

UPDATE: I cannot simply pass the values, because if areTheSame == 0 then I should not return (yet).

UPDATE 2: I modified the code, replacing each two lines with this:

        if (c == 'x' || c == 'o')  return declareWinner(c);

It works fine, and does the same. Still have some repetition, but much better IMO.

Upvotes: 3

Views: 2375

Answers (3)

Bohemian
Bohemian

Reputation: 425003

You can't make super return, but you could collect all the chars first, then return the first hit, null otherwise. And without any new methods:

private YourConsultant.GameState gameState() {
    List<Character> chars = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        chars.add(areTheSame(board[i][0], board[i][1], board[i][2]));
    }

    for (int i = 0; i < 3; i++) {
        chars.add(areTheSame(board[0][i], board[1][i], board[2][i]));
    }

    chars.add(areTheSame(board[0][0], board[1][1], board[2][2]));
    chars.add(areTheSame(board[0][2], board[1][1], board[2][0]));

    return chars.stream()
            .filter(c -> c == 'x' || c == 'o')
            .map(c -> c == 'x' ? YourConsultant.GameState.WON_BY_X : YourConsultant.GameState.WON_BY_O)
            .findFirst()
            .orElse(null); // or whatever "non winning" value you want
}

The "performance" impact of examining all of a tic tac toe board, instead of stopping at the first returnable state, would be measured in microseconds.

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 180181

No, a method cannot perform a return for its caller, but the caller can directly return the value returned by a called method. It does not appear that that would serve your purpose, however, for you want to return only conditionally.

I'd approach the problem with a deeper change. Note how similar your four stanzas are: it's not just the conditional returns that are a little wet. The tests you want to perform are few enough to be enumerable, so you could consider something along these lines:

private final static int[][][] TRIPLES = new int[][][] {
    { {0, 0}, {0, 1}, {0, 2} },
    { {1, 0}, {1, 1}, {1, 2} },
    { {2, 0}, {2, 1}, {2, 2} },
    { {0, 0}, {1, 0}, {2, 0} },
    { {0, 1}, {1, 1}, {2, 1} },
    { {0, 2}, {1, 2}, {2, 2} },
    { {0, 0}, {1, 1}, {2, 2} },
    { {0, 2}, {1, 1}, {2, 0} },
};

YourConsultant.GameState gameState() {

    for (int i = 0; i < TRIPLES.length; i++) {
        char c = areTheSame(
            board[TRIPLES[i][0][0]][TRIPLES[i][0][1]],
            board[TRIPLES[i][1][0]][TRIPLES[i][1][1]],
            board[TRIPLES[i][2][0]][TRIPLES[i][2][1]]
        );
        if (c == 'x') {
            return YourConsultant.GameState.WON_BY_X;
        } else if (c == 'o') {
            return YourConsultant.GameState.WON_BY_O;
        }
    }

    return YourConsultant.GameState.NO_WINNER;
}

Upvotes: 2

stevegal
stevegal

Reputation: 66

you just need a findWinner method

private YourConsultant.GameState findWinner(YourConsultant.GameState previousWinner, char boardResult) {
  if (previousWinner!=null) {
      return previousWinner;
  }
  YourConsultant.GameState winner=null;
  switch(boardResult) {
     case 'x': 
         winner = YourConsultant.GameState.WON_BY_X; 
         break;
     case 'o':
         winner = YourConsultant.GameState.WON_BY_O;
         break;
     default:
         winner = null;
  }       
  return winner;
}

then your board methods...

YourConsultant.GameState currentWinner=null;

for (int i = 0; i < 3; i++) {
    currentWinner = findWinner(currentWinner,areTheSame(board[i][0], board[i][1], board[i][2]));      
}
for (int i = 0; i < 3; i++) {
   currentWinner = findWinner(currentWinner,areTheSame(board[0][i], board[1][i], board[2][i]));
}
...

of course this isn't the most efficient way...

Upvotes: 1

Related Questions