Too many 'if' statements?

2019-01-12 13:36发布

The following code does work how I need it to, but it's ugly, excessive or a number of other things. I've looked at formulas and attempted to write a few solutions, but I end up with a similar amount of statements.

Is there a type of math formula that would benefit me in this instance or are 16 if statements acceptable?

To explain the code, it's for a kind of simultaneous-turn-based game.. two players have four action buttons each and the results come from an array (0-3), but the variables 'one' & 'two' can be assigned anything if this helps. The result is, 0 = neither win, 1 = p1 wins, 2 = p2 wins, 3 = both win.

public int fightMath(int one, int two) {

    if(one == 0 && two == 0) { result = 0; }
    else if(one == 0 && two == 1) { result = 0; }
    else if(one == 0 && two == 2) { result = 1; }
    else if(one == 0 && two == 3) { result = 2; }
    else if(one == 1 && two == 0) { result = 0; }
    else if(one == 1 && two == 1) { result = 0; }
    else if(one == 1 && two == 2) { result = 2; }
    else if(one == 1 && two == 3) { result = 1; }
    else if(one == 2 && two == 0) { result = 2; }
    else if(one == 2 && two == 1) { result = 1; }
    else if(one == 2 && two == 2) { result = 3; }
    else if(one == 2 && two == 3) { result = 3; }
    else if(one == 3 && two == 0) { result = 1; }
    else if(one == 3 && two == 1) { result = 2; }
    else if(one == 3 && two == 2) { result = 3; }
    else if(one == 3 && two == 3) { result = 3; }

    return result;
}

26条回答
ゆ 、 Hurt°
2楼-- · 2019-01-12 14:06

Thanks to @Joe Harper as I ended up using a variation of his answer. To slim it down further as 2 results per 4 were the same I slimmed it down further.

I may come back to this at some point, but if there's no major resistance caused by multiple if-statements then I'll keep this for now. I will look into the table matrix and switch statement solutions further.

public int fightMath(int one, int two) {

    if       (one == 0) {

        if       (two == 2) {
            result = 1;
        } else if(two == 3) {
            result = 2;
        } else {
            result = 0; }

    } else if(one == 1) {
        if       (two == 2) {
            result = 2;
        } else if(two == 3) {
            result = 1;
        } else {
            result = 0; }

    } else if(one == 2) {

        if       (two == 0) {
            result = 2;
        } else if(two == 1) {
            result = 1;
        } else {
            result = 3; }

    } else if(one == 3) {

        if       (two == 0) {
            result = 1;
        } else if(two == 1) {
            result = 2;
        } else {
            result = 3; }
    }

    return result;
}
查看更多
霸刀☆藐视天下
3楼-- · 2019-01-12 14:07
  1. Use constants or enums to make the code more readable
  2. Try to split the code into more functions
  3. Try to use the symmetry of the problem

Here is a suggestion how this could look like, but using an ints here is still kind of ugly:

static final int BLOCK_HIGH = 0;
static final int BLOCK_LOW = 1;
static final int ATTACK_HIGH = 2;
static final int ATTACK_LOW = 3;

public static int fightMath(int one, int two) {
    boolean player1Wins = handleAttack(one, two);
    boolean player2Wins = handleAttack(two, one);
    return encodeResult(player1Wins, player2Wins); 
}



private static boolean handleAttack(int one, int two) {
     return one == ATTACK_HIGH && two != BLOCK_HIGH
        || one == ATTACK_LOW && two != BLOCK_LOW
        || one == BLOCK_HIGH && two == ATTACK_HIGH
        || one == BLOCK_LOW && two == ATTACK_LOW;

}

private static int encodeResult(boolean player1Wins, boolean player2Wins) {
    return (player1Wins ? 1 : 0) + (player2Wins ? 2 : 0);
}

It would be nicer to use a structured type for the input and the output. The input actually has two fields: the position and the type (block or attack). The output also has two fields: player1Wins and player2Wins. Encoding this into a single integer makes it harder to read the code.

class PlayerMove {
    PlayerMovePosition pos;
    PlayerMoveType type;
}

enum PlayerMovePosition {
    HIGH,LOW
}

enum PlayerMoveType {
    BLOCK,ATTACK
}

class AttackResult {
    boolean player1Wins;
    boolean player2Wins;

    public AttackResult(boolean player1Wins, boolean player2Wins) {
        this.player1Wins = player1Wins;
        this.player2Wins = player2Wins;
    }
}

AttackResult fightMath(PlayerMove a, PlayerMove b) {
    return new AttackResult(isWinningMove(a, b), isWinningMove(b, a));
}

boolean isWinningMove(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.ATTACK && !successfulBlock(b, a)
            || successfulBlock(a, b);
}

boolean successfulBlock(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.BLOCK 
            && b.type == PlayerMoveType.ATTACK 
            && a.pos == b.pos;
}

Unfortunately, Java is not very good at expressing those kinds of data-types.

查看更多
戒情不戒烟
4楼-- · 2019-01-12 14:08

I hope I understand the logic correctly. How about something like:

public int fightMath (int one, int two)
{
    int oneHit = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : 0;
    int twoHit = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : 0;

    return oneHit+twoHit;
}

Checking one hit high or one hit low is not blocked and the same for player two.

Edit: Algorithm was not fully understood, "hit" awarded when blocking which I did not realize (Thx elias):

public int fightMath (int one, int two)
{
    int oneAttack = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : (one >= 2) ? 2 : 0;
    int twoAttack = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : (two >= 2) ? 1 : 0;

    return oneAttack | twoAttack;
}
查看更多
在下西门庆
5楼-- · 2019-01-12 14:10

static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }

查看更多
小情绪 Triste *
6楼-- · 2019-01-12 14:12

I don't like any of the solutions presented except for JAB's. None of the others make it easy to read the code and understand what is being computed.

Here's how I would write this code -- I only know C#, not Java, but you get the picture:

const bool t = true;
const bool f = false;
static readonly bool[,] attackResult = {
    { f, f, t, f }, 
    { f, f, f, t },
    { f, t, t, t },
    { t, f, t, t }
};
[Flags] enum HitResult 
{ 
    Neither = 0,
    PlayerOne = 1,
    PlayerTwo = 2,
    Both = PlayerOne | PlayerTwo
}
static HitResult ResolveAttack(int one, int two)
{
    return 
        (attackResult[one, two] ? HitResult.PlayerOne : HitResult.Neither) | 
        (attackResult[two, one] ? HitResult.PlayerTwo : HitResult.Neither);
}    

Now it is much more clear what is being computed here: this emphasizes that we are computing who gets hit by what attack, and returning both results.

However this could be even better; that Boolean array is somewhat opaque. I like the table lookup approach but I would be inclined to write it in such a way that made it clear what the intended game semantics were. That is, rather than "an attack of zero and a defense of one results in no hit", instead find a way to make the code more clearly imply "a low kick attack and a low block defense results in no hit". Make the code reflect the business logic of the game.

查看更多
Viruses.
7楼-- · 2019-01-12 14:12

The first thing that occurred to me was essentially the same answer given by Francisco Presencia, but optimized somewhat:

public int fightMath(int one, int two)
{
    switch (one*10 + two)
    {
    case  0:
    case  1:
    case 10:
    case 11:
        return 0;
    case  2:
    case 13:
    case 21:
    case 30:
        return 1;
    case  3:
    case 12:
    case 20:
    case 31:
        return 2;
    case 22:
    case 23:
    case 32:
    case 33:
        return 3;
    }
}

You could further optimize it by making the last case (for 3) the default case:

    //case 22:
    //case 23:
    //case 32:
    //case 33:
    default:
        return 3;

The advantage of this method is that it is easier to see which values for one and two correspond to which return values than some of the other suggested methods.

查看更多
登录 后发表回答