D'autres personnes ont déjà suggéré mon idée initiale, la méthode matricielle, mais en plus de consolider les instructions if, vous pouvez éviter une partie de ce que vous avez en vous assurant que les arguments fournis sont dans la plage attendue et en utilisant des retours en place (certaines normes de codage que j'ai vues imposent un point de sortie pour les fonctions, mais j'ai trouvé que les retours multiples sont très utiles pour éviter le codage en flèche et avec la prévalence des exceptions en Java, il n'y a pas beaucoup d'intérêt à appliquer strictement une telle règle de toute façon, car toute exception non attrapée lancée à l'intérieur de la méthode est un point de sortie possible). L'imbrication d'instructions switch est une possibilité, mais pour la petite gamme de valeurs que vous vérifiez ici, je trouve que les instructions if sont plus compactes et ne sont pas susceptibles d'entraîner une grande différence de performances, en particulier si votre programme est basé sur des tours plutôt que sur du temps réel.
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
}
Cela finit par être moins lisible qu'il ne le serait autrement en raison de l'irrégularité de certaines parties de la correspondance entrée->résultat. Je préfère le style matriciel en raison de sa simplicité et de la façon dont vous pouvez configurer la matrice pour qu'elle ait un sens visuellement (bien que cela soit en partie influencé par mes souvenirs des cartes de Karnaugh) :
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
Mise à jour : étant donné que vous avez mentionné le blocage/le frappement, voici une modification plus radicale de la fonction qui utilise des types énumérés propriétaires/attributs pour les entrées et le résultat et qui modifie aussi un peu le résultat pour tenir compte du blocage, ce qui devrait donner une fonction plus lisible.
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;
}
Il n'est même pas nécessaire de modifier la fonction elle-même si vous voulez ajouter des blocs/attaques de plus grande hauteur, juste les enums ; l'ajout de types de mouvements supplémentaires nécessitera probablement une modification de la fonction, cependant. Aussi, EnumSet
s pourrait être plus extensible que d'utiliser des enums supplémentaires comme propriétés de l'enum principal, par exemple EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
et ensuite attacks.contains(move)
plutôt que move.type == MoveType.ATTACK
mais en utilisant EnumSet
seront probablement légèrement plus lentes que les chèques d'égalité directs.
Pour le cas où un bloc réussi donne lieu à un compteur, vous pouvez remplacer 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;
}
En outre, le remplacement de certains des if
avec l'utilisation de l'opérateur ternaire ( boolean_expression ? result_if_true : result_if_false
) pourrait rendre le code plus compact (par exemple, le code du bloc précédent deviendrait return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), mais cela peut conduire à des oneliners plus difficiles à lire et je ne le recommande pas pour des branchements plus complexes.
1 votes
@waqaslam:- Cela peut aider. Une instruction de commutation Java pour gérer deux variables ?
9 votes
Il y a sûrement une certaine logique ici qui peut être généralisée plutôt que forcée brutalement ? Il y a sûrement une fonction
f(a, b)
qui donne la réponse dans le cas général ? Vous n'avez pas expliqué la logique du calcul, donc toutes les réponses ne sont que du rouge à lèvres sur un cochon. Je commencerais par repenser sérieusement la logique de votre programme, en utilisantint
Les drapeaux pour les actions sont très dépassés.enum
peuvent contenir de la logique et sont descriptives, ce qui vous permettrait d'écrire votre code d'une manière plus moderne.0 votes
Après avoir lu les réponses fournies par @Steve Benett dans sa question alternative liée ci-dessus, je peux supposer qu'il n'y a pas de réponse directe à cette question, car il s'agit essentiellement de la même chose qu'une base de données. J'ai essayé d'expliquer dans la question originale que je créais un jeu simple (un combattant) et que les utilisateurs avaient une sélection de 4 boutons : blockHigh(0), blockLow(1), attackHigh(2) et attackLow(3). Ces chiffres sont conservés dans un tableau jusqu'à ce qu'ils soient nécessaires. Plus tard, ils sont utilisés par la fonction 'fightMath()' qui compare les sélections de playerOne à celles de playerTwos pour obtenir le résultat. Pas de détection de collision à proprement parler.
9 votes
Si vous avez une réponse, veuillez l'afficher comme telle. La discussion prolongée dans les commentaires est difficile à suivre, surtout lorsqu'il s'agit de code. Si vous voulez discuter de la question de savoir si cette question aurait dû être migrée vers la revue de code, il y a un forum de discussion. Méta discussion à ce sujet.
1 votes
Que voulez-vous dire par "identique à une base de données" ? Si ces valeurs sont dans la base de données, tirez-les de là. Sinon, si c'est vraiment aussi complexe, je le laisserais tel quel et ajouterais des commentaires de logique commerciale après chaque ligne pour que les gens comprennent ce qui se passe. Il est préférable (pour moi) d'être long et explicite - quelqu'un dans le futur pourra comprendre ce qui se passe. Si vous le mettez dans une carte ou essayez d'économiser 8 lignes de code, l'avantage est vraiment petit, et le désavantage est plus grand : vous rendez le tout de plus en plus confus pour quelqu'un qui devra lire votre code un jour.
0 votes
@skaz Il y a eu un commentaire précédent disant qu'une formule pour résoudre mon problème était difficile car les nombres sont entrés par l'utilisateur plutôt que par les mathématiques. Je pense cependant que la réponse de laalto, maintenant que je la comprends, serait facile à lire tant que les variables 1 et 2 sont expliquées.