if-statement - test - que es if else en programacion
¿Demasiadas declaraciones ''si''? (26)
- Use constantes o enumeraciones para hacer que el código sea más legible
- Intenta dividir el código en más funciones.
- 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.
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;
}
¿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.
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;
}
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.
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;
}
Esto usa un poco de bitmagic (ya lo estás haciendo manteniendo dos bits de información (bajo / alto y ataque / bloque) en un solo entero):
No lo he ejecutado, solo lo escribí aquí, por favor haga doble clic. La idea seguramente funciona. EDIT: Ahora se prueba para cada entrada, funciona bien.
public int fightMath(int one, int two) {
if(one<2 && two<2){ //both players blocking
return 0; // nobody hits
}else if(one>1 && two>1){ //both players attacking
return 3; // both hit
}else{ // some of them attack, other one blocks
int different_height = (one ^ two) & 1; // is 0 if they are both going for the same height - i.e. blocker wins, and 1 if height is different, thus attacker wins
int attacker = one>1?1:0; // is 1 if one is the attacker, two is the blocker, and 0 if one is the blocker, two is the attacker
return (attacker ^ different_height) + 1;
}
}
¿O debería sugerir separar los dos bits de información en variables separadas? El código basado principalmente en operaciones de bit como esta arriba es generalmente muy difícil de mantener.
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.
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];
}
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.
Para ser honesto, cada uno tiene su propio estilo de código. No hubiera pensado que el rendimiento se vería afectado demasiado. Si entiende esto mejor que usar una versión de caja de conmutador, continúe usándolo.
Puede anidar los ifs, por lo que potencialmente podría haber un ligero aumento en el rendimiento de sus últimos if, ya que no habría pasado por tantas declaraciones if. Pero en su contexto de un curso básico de java probablemente no se beneficiará.
else if(one == 3 && two == 3) { result = 3; }
Entonces, en lugar de ...
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; }
Tu harias
if(one == 0)
{
if(two == 0) { result = 0; }
else if(two == 1) { result = 0; }
else if(two == 2) { result = 1; }
else if(two == 3) { result = 2; }
}
Y simplemente reformatéalo como prefieras.
Esto no hace que el código se vea mejor, pero creo que potencialmente lo acelera un poco.
Pruébalo con la caja del interruptor. ..
Echa un vistazo here o here para más información al respecto.
switch (expression)
{
case constant:
statements;
break;
[ case constant-2:
statements;
break; ] ...
[ default:
statements;
break; ] ...
}
Puede agregarle múltiples condiciones (no simultáneamente) e incluso tener una opción predeterminada donde no se hayan satisfecho otros casos.
PS: Solo si una condición debe ser satisfecha ..
Si surgen 2 condiciones simultáneamente ... No creo que se pueda usar el interruptor. Pero puedes reducir tu código aquí.
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];
}
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.
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
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.
Al dibujar una tabla entre uno / dos y el resultado, veo un patrón,
if(one<2 && two <2) result=0; return;
Lo anterior reduciría al menos 3 si las declaraciones. No veo un patrón establecido ni puedo extraer mucho del código dado, pero si se puede derivar tal lógica, se reduciría una serie de declaraciones if.
Espero que esto ayude.
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
}
}
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 one
y two
qué valores devuelven que otros métodos sugeridos.
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);
}
Usaría un mapa, ya sea un HashMap o un TreeMap
Especialmente si los parámetros no están en el formulario. 0 <= X < N
Como un conjunto de enteros positivos aleatorios ..
Código
public class MyMap
{
private TreeMap<String,Integer> map;
public MyMap ()
{
map = new TreeMap<String,Integer> ();
}
public void put (int key1, int key2, Integer value)
{
String key = (key1+":"+key2);
map.put(key, new Integer(value));
}
public Integer get (int key1, int key2)
{
String key = (key1+":"+key2);
return map.get(key);
}
}
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
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 if
declaraciones, 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;
}
Puede utilizar un here cambio en lugar de mutipleif
También para mencionar que dado que tiene dos variables, entonces tiene que combinar las dos variables para usarlas en el switch
¿Comprobar esta instrucción del interruptor de Java para manejar dos variables?
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!
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2