El siguiente código funciona como lo necesito, pero es feo, excesivo o varias otras cosas. Miré las fórmulas e intenté escribir algunas soluciones, pero termino 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 una especie 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 cualquier cosa si esto ayuda. El resultado es, 0 = ninguno 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;
}
if-statement
math
formula
TomFirth
fuente
fuente
f(a, b)
que produce la respuesta en el caso general? No ha explicado la lógica del cálculo, por lo tanto, todas las respuestas son solo lápiz labial en un cerdo. Comenzaría por repensar seriamente la lógica de su programa, el uso deint
indicadores para las acciones está muy desactualizado.enum
Los s pueden contener lógica y son descriptivos, esto le permitiría escribir su código de una manera más moderna.Respuestas:
Si no puede encontrar una fórmula, puede usar una tabla para un número tan limitado de resultados:
fuente
IndexOutOfBoundsException
todos modos si uno o más índices están fuera de límites.i
/j
/k
para variables de bucle), constantes nombradas, organizar mi código de manera legible, etc., pero cuando los nombres de variables y funciones comienzan a ocupar más de 20 caracteres cada vez que lo encuentro en realidad conduce a un código menos legible. Mi enfoque habitual es tratar de obtener un código comprensible pero sucinto con comentarios aquí y allá para explicar por qué el código está estructurado de la manera en que está (frente a cómo). Poner el por qué en los nombres simplemente abarrota todo.Como su conjunto de datos es tan pequeño, puede comprimir todo en 1 entero largo y convertirlo en una fórmula
Variante más bit a bit:
Esto hace uso del hecho de que todo es un múltiplo de 2
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 serie de respuestas una reexpresión del problema como un mapa de 1 número de base 4 de 2 dígitos 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 base 4 de 1 dígito {0,1,2,3} - 4 valores diferentes, también importantes.
Ahora, un número de base 4 de 1 dígito se puede expresar como un número de base 2 de 2 dígitos; {0,1,2,3} = {00,01,10,11}, por lo que cada salida puede codificarse con solo 2 bits. Desde arriba, solo hay 16 salidas diferentes posibles, 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) se codifica en bits M [0: 1], m (1) se codifica en bits M [2: 3] y m (n) se codifica 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 cambiar M a la derecha 2 * N veces y tomar los 2 bits menos significativos, es decir (M >> 2 * N) y 0x3. Las expresiones (una << 3) y (dos << 1) simplemente multiplican las cosas mientras observan que 2 * x = x << 1 y 8 * x = x << 3.
fuente
No me gusta ninguna de las soluciones presentadas, excepto las de JAB. Ninguno de los otros facilita la lectura del código y la comprensión de lo que se está calculando .
Así es como escribiría este código: solo conozco C #, no Java, pero entiendes la imagen:
Ahora está mucho más claro lo que se está calculando aquí: esto enfatiza que estamos calculando quién es golpeado por qué ataque y devolviendo ambos resultados.
Sin embargo, esto podría ser aún mejor; esa matriz booleana es algo opaca. Me gusta el enfoque de búsqueda en la tabla, pero me inclinaría a escribirlo de tal manera que dejara en claro cuáles eran las semánticas del juego. Es decir, en lugar de "un ataque de cero y una defensa de uno da como resultado ningún golpe", en su lugar, encuentra una manera de hacer que el código implique más claramente "un ataque de patada baja y una defensa de bloque bajo no da ningún golpe". Haz que el código refleje la lógica de negocios del juego.
fuente
Puedes crear una matriz que contenga resultados
Cuando quieras obtener valor, usarás
fuente
Otras personas ya han sugerido mi idea inicial, el método de matriz, pero además de consolidar las declaraciones if, puede evitar algo de lo que tiene asegurándose de que los argumentos proporcionados estén en el rango esperado y utilizando retornos en el lugar (algunos códigos Los estándares 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 hacer cumplir estrictamente dicha regla de todos modos ya que cualquier excepción no detectada lanzada dentro del método es un posible punto de salida de todos modos). La posibilidad de anidar declaraciones de cambio es una posibilidad, pero para el pequeño rango de valores que está comprobando aquí, encuentro si las declaraciones son más compactas y no es probable que generen una gran diferencia de rendimiento,
Esto termina siendo menos legible de lo que podría ser debido a la irregularidad de partes del mapeo input-> result. En su lugar, prefiero el estilo de matriz debido a su simplicidad y a cómo puede configurar la matriz para que tenga sentido visualmente (aunque eso está en parte influenciado por mis recuerdos de los mapas de Karnaugh):
Actualización: dada su mención de bloqueo / golpe, aquí hay un cambio más radical en la función que utiliza tipos enumerados de propiedad / retención de atributos para las entradas y el resultado y también modifica un poco el resultado para dar cuenta del bloqueo, lo que debería dar como resultado Función legible.
Ni siquiera tiene que cambiar la función en sí si desea agregar bloques / ataques de más alturas, solo las enumeraciones; Sin embargo, agregar tipos adicionales de movimientos probablemente requerirá la modificación de la función. Además,
EnumSet
s 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 enattacks.contains(move)
lugar de hacerlomove.type == MoveType.ATTACK
, aunque el uso deEnumSet
s probablemente será un poco más lento que las comprobaciones de igualdad directa.Para el caso donde un bloque exitoso resulta en un contador, puede reemplazarlo
if (one.height == two.height) return LandedHit.NEITHER;
conAdemás, reemplazar algunas de las
if
declaraciones 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 volveríareturn one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), pero eso puede dar lugar a líneas más difíciles de leer, por lo que no No lo recomiendo para ramificaciones más complejas.fuente
one
ytwo
ser reutilizado como puntos de inicio en mi hoja de sprites. Aunque no requiere mucho código adicional para permitir eso.EnumMap
clase que se puede utilizar para mapear las enumeraciones de sus desplazamientos enteros (también se puede usar los valores ordinales de los miembros de enumeración directamente, por ejemplo,Move.ATTACK_HIGH.ordinal()
sería0
,Move.ATTACK_LOW.ordinal()
sería1
, etc., pero eso es más frágil / menos flexible que forma explícita asociar cada miembro con un valor como agregar valores de enumeración entre los existentes eliminaría el conteo, lo que no sería el caso con unEnumMap
.)attack(against)
método a laMove
enumeración, devolviendo HIT cuando el movimiento es un ataque exitoso, BACKFIRE cuando el movimiento es un ataque bloqueado y NADA cuando no es un ataque. De esta forma, puede implementarlo en general (public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }
) y anularlo en movimientos específicos cuando sea necesario (movimientos débiles que cualquier bloque puede bloquear, ataques que nunca son contraproducentes, etc.)¿Por qué no usar una matriz?
Comenzaré 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:
Cuando miramos esta misma tabla binaria, vemos los siguientes resultados:
Ahora tal vez ya ve algún patrón, pero cuando combino el valor uno y dos, veo que está usando todos los valores 0000, 0001, 0010, ..... 1110 y 1111. Ahora combinemos el valor uno y dos para hacer un solo Entero de 4 bits.
Cuando traducimos esto nuevamente a valores decimales, vemos una matriz muy posible de valores donde el uno y dos combinados podrían usarse como índice:
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 de esta manera:
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.
fuente
Esto usa un poco de bitmagic (ya lo estás haciendo manteniendo dos bits de información (bajo / alto y ataque / bloqueo) en un solo entero):
No lo he ejecutado, solo lo escribí aquí, por favor verifique dos veces.La idea seguramente funciona. EDITAR: ahora se ha probado para cada entrada, funciona bien.¿O debería sugerir separar los dos bits de información en variables separadas? El código basado principalmente en operaciones de bits como este es generalmente muy difícil de mantener.
fuente
return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
Para ser sincero, todos tienen su propio estilo de código. No hubiera pensado que el rendimiento se vería demasiado afectado. Si entiende esto mejor que usar una versión de caja de interruptor, continúe usándolo.
Podría anidar los ifs, por lo que potencialmente habría un ligero aumento en el rendimiento de sus últimas comprobaciones 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á.
Entonces, en lugar de ...
Tu harías ...
Y simplemente vuelva a formatearlo como prefiera.
Esto no hace que el código se vea mejor, pero creo que potencialmente lo acelera un poco.
fuente
Veamos 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 latidos 0 latidos 2 latidos 1 latidos 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 tabla de victoria única se ve así: 0 latidos 2, 1 latidos 3, 2 latidos 1, 3 latidos 0.
3: Si 0/1 se enfrentan entre sí, entonces hay un empate sin golpes, pero si 2/3 se enfrentan entre sí, ambos golpean
Primero, construyamos una función unidireccional que nos diga si ganamos:
Entonces podemos usar esta función para componer el resultado final:
Si bien esto es posiblemente más complejo y probablemente más lento que la búsqueda en la tabla ofrecida en muchas respuestas, creo que es un método superior porque realmente encapsula la lógica de su código y lo describe a cualquiera que esté leyendo su código. Creo que esto lo hace una mejor implementación.
(Ha pasado un tiempo desde que hice cualquier Java, así que me disculpo si la sintaxis está desactivada, espero que todavía sea inteligible si me equivoco un poco)
Por cierto, 0-3 claramente significa algo; no son valores arbitrarios, por lo que sería útil nombrarlos.
fuente
Espero entender la lógica correctamente. ¿Qué tal algo como:
Comprobar un golpe alto o un golpe bajo no está bloqueado y lo mismo para el jugador dos.
Editar: Algoritmo no se entendió completamente, "golpe" otorgado al bloquear que no me di cuenta (Thx elias):
fuente
No tengo experiencia con Java, por lo que puede haber algunos errores tipográficos. Considere el código como pseudocódigo.
Yo iría con un simple interruptor. Para eso, necesitaría una evaluación de un solo número. Sin embargo, para este caso, desde
0 <= one < 4 <= 9
y0 <= two < 4 <= 9
, podemos convertir ambas entradas a un int simple multiplicandoone
por 10 y sumandotwo
. Luego use un interruptor en el número resultante como este: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, ...
Realmente solo una nota adicional, en caso de que me falte algo de Java. En PHP haría:
fuente
Dado que prefiere
if
condicionales anidados , aquí hay otra forma.Tenga en cuenta que no usa el
result
miembro y no cambia ningún estado.fuente
else
cadenas pero no había hecho ninguna diferencia.else if
declaraciones, podemos acelerar el código conswitch
tablas de búsqueda.one==0
ejecutará el código, entonces tendrá que verificar sione==1
entonces sione==2
y finalmente sione==3
- Si hubiera otros if dentro, no haría las últimas tres comprobaciones porque saldría de la declaración después de la primera coincidencia. Y sí, podría optimizar aún más mediante el uso de una declaración de cambio en lugar de lasif (one...
declaraciones y luego usando otro interruptor dentro del caso de laone's
. Sin embargo, esa no es mi pregunta.Pruébelo con la carcasa del interruptor. ..
Echa un vistazo aquí o aquí para obtener más información al respecto.
Puede agregarle varias condiciones (no simultáneamente) e incluso tener una opción predeterminada donde no se hayan satisfecho otros casos.
PD: solo si se debe cumplir una condición ...
Si surgen 2 condiciones simultáneamente ... no creo que se pueda usar el interruptor. Pero puedes reducir tu código aquí.
Declaración de cambio de Java múltiples casos
fuente
Lo primero que se me ocurrió fue esencialmente la misma respuesta dada por Francisco Presencia, pero optimizada de alguna manera:
Puede optimizarlo aún más haciendo que el último caso (para 3) sea el caso predeterminado:
La ventaja de este método es que es más fácil ver qué valores
one
ytwo
qué valores de retorno corresponden a algunos de los otros métodos sugeridos.fuente
fuente
; --unicorns
Puedes usar una caja de interruptor cambio en lugar de múltiples
if
También mencionar que, dado que tiene dos variables, debe fusionar las dos variables para usarlas en el interruptor
¿Verifica esta declaración de cambio de Java para manejar dos variables?
fuente
Cuando dibujo una tabla entre uno / dos y el resultado, veo un patrón,
Lo anterior reduciría al menos 3 si las declaraciones. No veo un patrón establecido ni soy capaz de deducir mucho del código dado, pero si se puede derivar esa lógica, se reducirían varias declaraciones if.
Espero que esto ayude.
fuente
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 bonita representación de matriz de laalto:
Y aquí vamos con algunos comentarios generales, pero debe describirlos en términos de reglas:
Por supuesto, podría reducir esto a menos código, pero generalmente es una buena idea entender lo que codifica en lugar de encontrar una solución compacta.
Alguna explicación sobre los complicados éxitos p1 / p2 sería genial, ¡parece interesante!
fuente
La solución más corta y aún legible:
o incluso más corto:
No contiene ningún número "mágico";) Espero que ayude.
fuente
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
fuente
Personalmente, me gusta en cascada los operadores ternarios:
Pero en su caso, puede usar:
O puede notar un patrón en bits:
Entonces puedes usar magia:
fuente
Aquí hay una versión bastante concisa, similar a la respuesta de JAB . Esto utiliza un mapa para almacenar que mueve triunfo sobre los demás.
Ejemplo:
Huellas dactilares:
fuente
static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();
cambio, lo recomendaría , debería ser un poco más eficiente.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 al azar.
Código
fuente
Gracias a @ Joe Harper ya que terminé usando una variación de su respuesta. Para reducirlo aún más, ya que 2 resultados por 4 fueron los mismos, lo reduje aún más.
Puedo volver a esto en algún momento, pero si no hay una gran resistencia causada por múltiples
if
declaraciones, entonces me quedaré con esto por ahora. Examinaré la matriz de la tabla y cambiaré las soluciones de los enunciados.fuente
Aquí hay una sugerencia de cómo se vería esto, pero el uso de entradas aquí todavía es algo feo:
Sería mejor usar un tipo estructurado para la entrada y la salida. La entrada en realidad tiene dos campos: la posición y el tipo (bloqueo 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.
Desafortunadamente, Java no es muy bueno para expresar ese tipo de tipos de datos.
fuente
En cambio, haz algo como esto
fuente