if-statement <c:if - ¿Demasiadas declaraciones 'si'?




java varias (22)

El siguiente código funciona como lo necesito, pero es feo, excesivo o una serie de otras cosas. He mirado fórmulas y he intentado escribir algunas soluciones, pero acabo con una cantidad similar de declaraciones.

¿Hay algún tipo de fórmula matemática que me beneficiaría en este caso o son 16 si las declaraciones son aceptables?

Para explicar el código, es para un tipo de juego basado en turnos simultáneos ... dos jugadores tienen cuatro botones de acción cada uno y los resultados provienen de una matriz (0-3), pero las variables 'uno' y 'dos' pueden ser Asignado algo si esto ayuda. El resultado es, 0 = ninguno de los dos gana, 1 = p1 gana, 2 = p2 gana, 3 = ambos ganan.

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;
}

Answers

Puedes crear una matriz que contenga resultados.

int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};

Cuando quieras obtener valor usarás

public int fightMath(int one, int two) {
  return this.results[one][two]; 
}

No tengo experiencia con Java, por lo que puede haber algunos errores tipográficos. Por favor considere el código como pseudo-código.

Yo iría con un simple interruptor. Para eso, necesitarías una evaluación de un solo número. Sin embargo, para este caso, dado que 0 <= one < 4 <= 9 y 0 <= two < 4 <= 9 , podemos convertir ambos ints a un simple int multiplicando one por 10 y sumando two . Luego usa un interruptor en el número resultante como este:

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 10
    int evaluate = one * 10 + two;

    switch(evaluate) {
        // I'd consider a comment in each line here and in the original code
        // for clarity
        case 0: result = 0; break;
        case 1: result = 0; break;
        case 1: result = 0; break;
        case 2: result = 1; break;
        case 3: result = 2; break;
        case 10: result = 0; break;
        case 11: result = 0; break;
        case 12: result = 2; break;
        case 13: result = 1; break;
        case 20: result = 2; break;
        case 21: result = 1; break;
        case 22: result = 3; break;
        case 23: result = 3; break;
        case 30: result = 1; break;
        case 31: result = 2; break;
        case 32: result = 3; break;
        case 33: result = 3; break;
    }

    return result;
}

Hay otro método corto que solo quiero señalar como un código teórico. Sin embargo, no lo usaría porque tiene una complejidad adicional con la que normalmente no quieres lidiar. La complejidad adicional proviene de la base 4 , porque el conteo es 0, 1, 2, 3, 10, 11, 12, 13, 20, ...

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 4
    int evaluate = one * 4 + two;

    allresults = new int[] { 0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 1, 2, 3, 3 };

    return allresults[evaluate];
}

Realmente solo una nota adicional, en caso de que me esté perdiendo algo de Java. En PHP yo haría:

function fightMath($one, $two) {
    // Convert one and two to a single variable in base 4
    $evaluate = $one * 10 + $two;

    $allresults = array(
         0 => 0,  1 => 0,  2 => 1,  3 => 2,
        10 => 0, 11 => 0, 12 => 2, 13 => 1,
        20 => 2, 21 => 1, 22 => 3, 23 => 3,
        30 => 1, 31 => 2, 32 => 3, 33 => 3 );

    return $allresults[$evaluate];
}

Espero entender la lógica correctamente. ¿Qué tal algo como:

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;
}

El control de un golpe alto o bajo no está bloqueado y lo mismo para el jugador dos.

Edición: el algoritmo no se entendió completamente, se otorgó "golpe" cuando se bloqueó, lo cual no me di cuenta (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;
}

Personalmente me gusta conectar en cascada los operadores ternarios:

int result = condition1
    ? result1
    : condition2
    ? result2
    : condition3
    ? result3
    : resultElse;

Pero en tu caso, puedes usar:

final int[] result = new int[/*16*/] {
    0, 0, 1, 2,
    0, 0, 2, 1,
    2, 1, 3, 3,
    1, 2, 3, 3
};

public int fightMath(int one, int two) {
    return result[one*4 + two];
}

O bien, puede notar un patrón en bits:

one   two   result

section 1: higher bits are equals =>
both result bits are equals to that higher bits

00    00    00
00    01    00
01    00    00
01    01    00
10    10    11
10    11    11
11    10    11
11    11    11

section 2: higher bits are different =>
lower result bit is inverse of lower bit of 'two'
higher result bit is lower bit of 'two'

00    10    01
00    11    10
01    10    10
01    11    01
10    00    10
10    01    01
11    00    01
11    01    10

Así que puedes usar la magia:

int fightMath(int one, int two) {
    int b1 = one & 2, b2 = two & 2;
    if (b1 == b2)
        return b1 | (b1 >> 1);

    b1 = two & 1;

    return (b1 << 1) | (~b1);
}

Dado que su conjunto de datos es tan pequeño, puede comprimir todo en 1 entero largo y convertirlo en una fórmula

public int fightMath(int one,int two)
{
   return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}

Más variante bitwise:

Esto hace uso del hecho de que todo es un múltiplo de 2.

public int fightMath(int one,int two)
{
   return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}

El origen de la constante mágica

¿Qué puedo decir? El mundo necesita magia, a veces la posibilidad de algo requiere su creación.

La esencia de la función que resuelve el problema de OP es un mapa de 2 números (uno, dos), dominio {0,1,2,3} al rango {0,1,2,3}. Cada una de las respuestas ha abordado cómo implementar ese mapa.

Además, puede ver en una cantidad de las respuestas una reafirmación del problema como un mapa de 1 número de base de 2 dígitos 4 número N (uno, dos) donde uno es el dígito 1, dos es el dígito 2 y N = 4 * uno + dos; N = {0,1,2, ..., 15} - dieciséis valores diferentes, eso es importante. La salida de la función es un número 4 de base de 1 dígito {0,1,2,3} - 4 valores diferentes, también importantes.

Ahora, un número base de 1 dígito 4 se puede expresar como un número base de 2 dígitos 2; {0,1,2,3} = {00,01,10,11}, por lo que cada salida puede codificarse con solo 2 bits. Desde arriba, solo son posibles 16 salidas diferentes, por lo que 16 * 2 = 32 bits es todo lo que se necesita para codificar todo el mapa; todo esto puede caber en 1 entero.

La constante M es una codificación del mapa m donde m (0) está codificada en los bits M [0: 1], m (1) está codificada en los bits M [2: 3], y m (n) está codificada en bits M [n * 2: n * 2 + 1].

Todo lo que queda es indexar y devolver la parte derecha de la constante, en este caso, puede desplazar M a la derecha 2 * N veces y tomar los 2 bits menos significativos, es decir (M >> 2 * N) y 0x3. Las expresiones (uno << 3) y (dos << 1) simplemente están multiplicando las cosas al mismo tiempo que se observa que 2 * x = x << 1 y 8 * x = x << 3.


Lo primero que se me ocurrió fue esencialmente la misma respuesta dada por Francisco Presencia, pero algo optimizada:

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;
    }
}

Puede optimizarlo aún más haciendo que el último caso (para 3) sea el caso predeterminado:

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

La ventaja de este método es que es más fácil ver para qué valores oney twoqué valores devuelven que otros métodos sugeridos.


No me gustan ninguna de las soluciones presentadas, excepto las de JAB. Ninguno de los otros facilita la lectura del código y comprende lo que se está calculando .

Así es como escribiría este código: solo conozco C #, no Java, pero obtienes la imagen:

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);
}    

Ahora está mucho más claro lo que se está calculando aquí: esto enfatiza que estamos calculando quiénes son los atacados y qué resultados se obtienen.

Sin embargo, esto podría ser aún mejor; Esa matriz booleana es algo opaca. Me gusta el enfoque de búsqueda en la mesa, pero me inclino a escribirlo de tal manera que quede claro cuáles eran las semánticas de juego previstas. Es decir, en lugar de "un ataque de cero y una defensa de uno resulta sin impacto", en lugar de encontrar una manera de hacer que el código implique más claramente "un ataque de patada baja y un resultado de defensa de bloqueo bajo no tiene impacto". Hacer que el código refleje la lógica de negocios del juego.


Un buen punto sería definir las reglas como texto, entonces puede derivar más fácilmente la fórmula correcta. Esto se extrae de la representación de matriz agradable de laalto:

{ 0, 0, 1, 2 },
{ 0, 0, 2, 1 },
{ 2, 1, 3, 3 },
{ 1, 2, 3, 3 }

Y aquí vamos con algunos comentarios generales, pero debes describirlos en términos de reglas:

if(one<2) // left half
{
    if(two<2) // upper left half
    {
        result = 0; //neither hits
    }
    else // lower left half
    {
        result = 1+(one+two)%2; //p2 hits if sum is even
    }
}
else // right half
{
    if(two<2) // upper right half
    {
        result = 1+(one+two+1)%2; //p1 hits if sum is even
    }
    else // lower right half
    {
        return 3; //both hit
    }
}

Por supuesto, puede reducir esto a menos código, pero generalmente es una buena idea entender lo que usted codifica en lugar de encontrar una solución compacta.

if((one<2)&&(two<2)) result = 0; //top left
else if((one>1)&&(two>1)) result = 3; //bottom right
else result = 1+(one+two+((one>1)?1:0))%2; //no idea what that means

¡Alguna explicación sobre los complicados hits de p1 / p2 sería genial, parece interesante!


Aquí hay una versión bastante concisa, similar a la respuesta de JAB . Esto utiliza un mapa para almacenar que mueve el triunfo sobre otros.

public enum Result {
  P1Win, P2Win, BothWin, NeitherWin;
}

public enum Move {
  BLOCK_HIGH, BLOCK_LOW, ATTACK_HIGH, ATTACK_LOW;

  static final Map<Move, List<Move>> beats = new EnumMap<Move, List<Move>>(
      Move.class);

  static {
    beats.put(BLOCK_HIGH, new ArrayList<Move>());
    beats.put(BLOCK_LOW, new ArrayList<Move>());
    beats.put(ATTACK_HIGH, Arrays.asList(ATTACK_LOW, BLOCK_LOW));
    beats.put(ATTACK_LOW, Arrays.asList(ATTACK_HIGH, BLOCK_HIGH));
  }

  public static Result compare(Move p1Move, Move p2Move) {
    boolean p1Wins = beats.get(p1Move).contains(p2Move);
    boolean p2Wins = beats.get(p2Move).contains(p1Move);

    if (p1Wins) {
      return (p2Wins) ? Result.BothWin : Result.P1Win;
    }
    if (p2Wins) {
      return (p1Wins) ? Result.BothWin : Result.P2Win;
    }

    return Result.NeitherWin;
  }
} 

Ejemplo:

System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));

Huellas dactilares:

P1Win

  1. Use constantes o enumeraciones para hacer que el código sea más legible
  2. Intenta dividir el código en más funciones.
  3. Intenta utilizar la simetría del problema.

Aquí hay una sugerencia de cómo podría verse esto, pero usar un ints aquí todavía es un poco feo:

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);
}

Sería mejor utilizar un tipo estructurado para la entrada y la salida. La entrada en realidad tiene dos campos: la posición y el tipo (bloque o ataque). La salida también tiene dos campos: player1Wins y player2Wins. Codificar esto en un solo entero hace que sea más difícil leer el código.

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;
}

Desafortunadamente, Java no es muy bueno para expresar ese tipo de tipos de datos.


Otras personas ya han sugerido mi idea inicial, el método de la matriz, pero además de consolidar las declaraciones if, puede evitar algo de lo que tiene asegurándose de que los argumentos proporcionados se encuentren en el rango esperado y utilizando retornos en el lugar (algunos códigos). Las normas que he visto imponen un punto de salida para las funciones, pero he encontrado que los retornos múltiples son muy útiles para evitar la codificación de flechas y, con la prevalencia de excepciones en Java, no tiene mucho sentido aplicar de manera estricta tal regla. como cualquier excepción no capturada lanzada dentro del método es un posible punto de salida de todos modos). Las sentencias de conmutación anidadas son una posibilidad, pero para el pequeño rango de valores que está verificando aquí, encuentro que las sentencias son más compactas y probablemente no generarán una gran diferencia de rendimiento, especialmente si su programa está basado en turnos en lugar de ser real -hora.

public int fightMath(int one, int two) {
    if (one > 3 || one < 0 || two > 3 || two < 0) {
        throw new IllegalArgumentException("Result is undefined for arguments outside the range [0, 3]");
    }

    if (one <= 1) {
        if (two <= 1) return 0;
        if (two - one == 2) return 1;
        return 2; // two can only be 3 here, no need for an explicit conditional
    }

    // one >= 2
    if (two >= 2) return 3;
    if (two == 1) return 1;
    return 2; // two can only be 0 here
}

Esto termina siendo menos legible de lo que podría ser debido a la irregularidad de las partes de la entrada-> mapeo de resultados. Yo prefiero el estilo de matriz en lugar de su simplicidad y de cómo puedes configurar la matriz para que tenga sentido visualmente (aunque eso está en parte influenciado por mis recuerdos de los mapas de Karnaugh):

int[][] results = {{0, 0, 1, 2},
                   {0, 0, 2, 1},
                   {2, 1, 3, 3},
                   {2, 1, 3, 3}};

Actualización: dada su mención de bloqueo / golpeo, aquí hay un cambio más radical a la función que utiliza tipos enumerados con propiedad / atributos para las entradas y el resultado y también modifica el resultado un poco para tener en cuenta el bloqueo, lo que debería resultar en más función legible.

enum MoveType {
    ATTACK,
    BLOCK;
}

enum MoveHeight {
    HIGH,
    LOW;
}

enum Move {
    // Enum members can have properties/attributes/data members of their own
    ATTACK_HIGH(MoveType.ATTACK, MoveHeight.HIGH),
    ATTACK_LOW(MoveType.ATTACK, MoveHeight.LOW),
    BLOCK_HIGH(MoveType.BLOCK, MoveHeight.HIGH),
    BLOCK_LOW(MoveType.BLOCK, MoveHeight.LOW);

    public final MoveType type;
    public final MoveHeight height;

    private Move(MoveType type, MoveHeight height) {
        this.type = type;
        this.height = height;
    }

    /** Makes the attack checks later on simpler. */
    public boolean isAttack() {
        return this.type == MoveType.ATTACK;
    }
}

enum LandedHit {
    NEITHER,
    PLAYER_ONE,
    PLAYER_TWO,
    BOTH;
}

LandedHit fightMath(Move one, Move two) {
    // One is an attack, the other is a block
    if (one.type != two.type) {
        // attack at some height gets blocked by block at same height
        if (one.height == two.height) return LandedHit.NEITHER;

        // Either player 1 attacked or player 2 attacked; whoever did
        // lands a hit
        if (one.isAttack()) return LandedHit.PLAYER_ONE;
        return LandedHit.PLAYER_TWO;
    }

    // both attack
    if (one.isAttack()) return LandedHit.BOTH;

    // both block
    return LandedHit.NEITHER;
}

Ni siquiera tiene que cambiar la función en sí misma si desea agregar bloques / ataques de más alturas, solo las enumeraciones; Sin embargo, la adición de tipos adicionales de movimientos requerirá la modificación de la función. Además, EnumSets podría ser más extensible que usar enumeraciones adicionales como propiedades de la enumeración principal, por ejemplo, EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...); y luego attacks.contains(move) lugar de move.type == MoveType.ATTACK , aunque el uso de EnumSet s probablemente sea un poco más lento que los controles directos de igual a igual.

En el caso de que un bloqueo exitoso resulte en un contador, puede reemplazar if (one.height == two.height) return LandedHit.NEITHER; con

if (one.height == two.height) {
    // Successful block results in a counter against the attacker
    if (one.isAttack()) return LandedHit.PLAYER_TWO;
    return LandedHit.PLAYER_ONE;
}

Además, reemplazar algunas de las sentencias if con el uso del operador ternario ( boolean_expression ? result_if_true : result_if_false ) podría hacer que el código sea más compacto (por ejemplo, el código en el bloque anterior se return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE; ), pero eso puede conducir a oneliners más difíciles de leer, por lo que no lo recomendaría para una ramificación más compleja.


((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2

En lugar de hacer algo como esto

   public int fightMath(int one, int two) {
    return Calculate(one,two)

    }


    private int Calculate(int one,int two){

    if (one==0){
        if(two==0){
     //return value}
    }else if (one==1){
   // return value as per condtiion
    }

    }

Gracias a @Joe Harper ya que terminé usando una variación de su respuesta. Para adelgazar aún más, ya que 2 resultados por 4 eran los mismos, lo adelgacé aún más.

Es posible que vuelva a este punto en algún momento, pero si no hay una resistencia importante causada por múltiples ifdeclaraciones, lo guardaré por ahora. Voy a mirar en la matriz de la tabla y cambiar las soluciones de declaración más.

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;
}

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


¿Por qué no usar una matriz?

Empezaré desde el principio. Veo un patrón, los valores van de 0 a 3 y desea capturar todos los valores posibles. Esta es su mesa:

0 & 0 = 0
0 & 1 = 0
0 & 2 = 1
0 & 3 = 2
1 & 0 = 0
1 & 1 = 0
1 & 2 = 2
1 & 3 = 1
2 & 0 = 2
2 & 1 = 1
2 & 2 = 3
2 & 3 = 3
3 & 0 = 2
3 & 1 = 1
3 & 2 = 3
3 & 3 = 3

Cuando miramos este mismo binario de tabla vemos los siguientes resultados:

00 & 00 = 00
00 & 01 = 00
00 & 10 = 01
00 & 11 = 10
01 & 00 = 00
01 & 01 = 00
01 & 10 = 10
01 & 11 = 01
10 & 00 = 10
10 & 01 = 01
10 & 10 = 11
10 & 11 = 11
11 & 00 = 10
11 & 01 = 01
11 & 10 = 11
11 & 11 = 11

Ahora tal vez ya vea algún patrón, pero cuando combino el valor uno y dos veo que está utilizando todos los valores 0000, 0001, 0010, ... 1110 y 1111. Ahora combinemos el valor uno y dos para hacer una sola 4 bits entero.

0000 = 00
0001 = 00
0010 = 01
0011 = 10
0100 = 00
0101 = 00
0110 = 10
0111 = 01
1000 = 10
1001 = 01
1010 = 11
1011 = 11
1100 = 10
1101 = 01
1110 = 11
1111 = 11

Cuando volvemos a traducir esto a valores decimales, vemos una gran variedad de valores en los que uno y dos combinados podrían usarse como índice:

0 = 0
1 = 0
2 = 1
3 = 2
4 = 0
5 = 0
6 = 2
7 = 1
8 = 2
9 = 1
10 = 3
11 = 3
12 = 2
13 = 1
14 = 3
15 = 3

La matriz es entonces {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3} , donde su índice es simplemente uno y dos combinados.

No soy un programador de Java, pero puedes deshacerte de todas las declaraciones if y simplemente escribirlo como algo así:

int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two]; 

No sé si un desplazamiento de bits por 2 es más rápido que la multiplicación. Pero podría valer la pena intentarlo.


Si no puede encontrar una fórmula, puede usar una tabla para un número tan limitado de resultados:

final int[][] result = new int[][] {
  { 0, 0, 1, 2 },
  { 0, 0, 2, 1 },
  { 2, 1, 3, 3 },
  { 1, 2, 3, 3 }
};
return result[one][two];

Veamos lo que sabemos.

1: sus respuestas son simétricas para P1 (jugador uno) y P2 (jugador dos). Esto tiene sentido para un juego de lucha, pero también es algo que puedes aprovechar para mejorar tu lógica.

2: 3 tiempos 0 tiempos 2 tiempos 1 tiempos 3. Los únicos casos no cubiertos por estos casos son combinaciones de 0 vs 1 y 2 vs 3. Para decirlo de otra manera, la única tabla de victorias se ve así: 0 tiempos 2, 1 tiempos 3, 2 tiempos 1, 3 tiempos 0.

3: Si 0/1 se enfrenta uno contra el otro, entonces hay un empate sin hits, pero si 2/3 se enfrentan entre sí, ambos golpean

Primero, construyamos una función unidireccional que nos diga si ganamos:

// returns whether we beat our opponent
public boolean doesBeat(int attacker, int defender) {
  int[] beats = {2, 3, 1, 0};
  return defender == beats[attacker];
}

Podemos usar esta función para componer el resultado final:

// returns the overall fight result
// bit 0 = one hits
// bit 1 = two hits
public int fightMath(int one, int two)
{
  // Check to see whether either has an outright winning combo
  if (doesBeat(one, two))
    return 1;

  if (doesBeat(two, one))
    return 2;

  // If both have 0/1 then its hitless draw but if both have 2/3 then they both hit.
  // We can check this by seeing whether the second bit is set and we need only check
  // one's value as combinations where they don't both have 0/1 or 2/3 have already
  // been dealt with 
  return (one & 2) ? 3 : 0;
}

Si bien esto es posiblemente más complejo y probablemente más lento que la búsqueda en la tabla en muchas respuestas, creo que es un método superior porque en realidad encapsula la lógica de su código y lo describe a cualquiera que esté leyendo su código. Creo que esto hace que sea una mejor implementación.

(Ha pasado un tiempo desde que hice Java, así que pido disculpas si la sintaxis está desactivada, espero que todavía sea inteligible si la tengo un poco mal)

Por cierto, 0-3 claramente significa algo; No son valores arbitrarios por lo que ayudaría a nombrarlos.


La solución más corta y aún legible:

static public int fightMath(int one, int two)
{
    if (one < 2 && two < 2) return 0;
    if (one > 1 && two > 1) return 3;
    int n = (one + two) % 2;
    return one < two ? 1 + n : 2 - n;
}

o incluso más corto:

static public int fightMath(int one, int two)
{
    if (one / 2 == two / 2) return (one / 2) * 3;
    return 1 + (one + two + one / 2) % 2;
}

No contiene ningún número "mágico";) Espero que ayude.



Como prefieres anidado if condicionales, aquí hay otra forma.
Tenga en cuenta que no utiliza el miembro del result y no cambia ningún estado.

public int fightMath(int one, int two) {
    if (one == 0) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 1; }
      if (two == 3) { return 2; }
    }   
    if (one == 1) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 2; }
      if (two == 3) { return 1; }
    }
    if (one == 2) {
      if (two == 0) { return 2; }
      if (two == 1) { return 1; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    if (one == 3) {
      if (two == 0) { return 1; }
      if (two == 1) { return 2; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    return DEFAULT_RESULT;
}

No tengo acceso a una caja de Linux en este momento, pero [en realidad es un programa (y un bash builtin), así que creo que tienes que poner un espacio entre [y el primer parámetro.

También tenga en cuenta que el operador de igualdad de cadenas parece ser un solo =







if-statement math formula