web-dev-qa-db-fra.com

Trop de déclarations 'if'?

Le code suivant fonctionne comme je le souhaite, mais c'est moche, excessif ou un certain nombre d'autres choses. J'ai examiné des formules et essayé d'écrire quelques solutions, mais je finis par avoir un nombre similaire d'énoncés.

Existe-t-il un type de formule mathématique qui me serait utile dans ce cas ou 16 déclarations sont-elles acceptables?

Pour expliquer le code, il s’agit d’une sorte de jeu basé tour par tour. Deux joueurs ont chacun quatre boutons d’action et les résultats proviennent d’un tableau (0-3), mais les variables 'un' et 'deux' peuvent être assigné quoi que ce soit si cela aide. Le résultat est le suivant: 0 = ni victoire, 1 = p1 gagne, 2 = p2 gagne, 3 = les deux gagnent.

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

Si vous ne pouvez pas trouver de formule, vous pouvez utiliser un tableau pour un nombre de résultats aussi limité:

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];
598
laalto

Puisque votre ensemble de données est si petit, vous pouvez tout compresser en 1 entier long et le transformer en une formule

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

Variante plus binaire:

Cela rend utile le fait que tout est un multiple de 2

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

L'origine de la constante magique

Que puis-je dire? Le monde a besoin de magie, parfois la possibilité de quelque chose appelle sa création.

L'essence de la fonction qui résout le problème de OP est une carte de 2 nombres (un, deux), domaine {0,1,2,3} à la plage {0,1,2,3}. Chacune des réponses a abordé la manière de mettre en œuvre cette carte.

En outre, vous pouvez voir dans un certain nombre de réponses une reformulation du problème sous la forme d’une carte à 1 chiffre 2 de base 4 composé de N (un, deux) où l’un est le chiffre 1, deux le chiffre 2 et N = 4 * un + deux; N = {0,1,2, ..., 15} - seize valeurs différentes, c'est important. Le résultat de la fonction est un nombre à une base de 4 chiffres {0,1,2,3} - 4 valeurs différentes, également importantes.

Maintenant, un numéro de base 4 à 1 chiffre peut être exprimé sous forme de numéro de base 2 à 2 chiffres; {0,1,2,3} = {00,01,10,11}, ainsi chaque sortie peut être codée avec seulement 2 bits. De là-haut, il n'y a que 16 sorties différentes possibles, donc 16 * 2 = 32 bits suffisent pour coder la carte entière; tout cela peut tenir dans 1 entier.

La constante M est un codage de la carte m où m(0) est codé en bits M [0: 1], m(1) est codé en bits M [2: 3] et m(n) est codé en bits M [n * 2: n * 2 + 1].

Il ne reste plus qu'à indexer et à renvoyer la partie droite de la constante. Dans ce cas, vous pouvez décaler M deux fois N à droite et prendre les deux bits les moins significatifs, soit (M >> 2 * N) & 0x3. Les expressions (une << 3) et (deux << 1) ne font que multiplier les choses tout en notant que 2 * x = x << 1 et 8 * x = x << 3.

201
waTeim

Je n'aime aucune des solutions présentées, à l'exception de JAB. Aucun des autres ne facilite la lecture du code et la compréhension de ce qui est calculé.

Voici comment j'écrirais ce code - je ne connais que le C #, pas Java, mais vous obtenez l'image:

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

Maintenant, ce qui est calculé ici est beaucoup plus clair: cela montre que nous calculons qui est touché par quelle attaque et renvoyons les deux résultats.

Cependant, cela pourrait être encore mieux; Ce tableau booléen est quelque peu opaque. J'aime l'approche de la recherche de table, mais je serais enclin à l'écrire de manière à rendre claire la sémantique du jeu prévu. C’est-à-dire qu’au lieu de "une attaque de zéro et une défense d’un ne donne aucun coup", trouve plutôt un moyen de rendre le code plus clairement "une attaque à coup de pied bas et une défense par bloc bas ne donne aucun coup". Faites que le code reflète la logique commerciale du jeu.

98
Eric Lippert

Vous pouvez créer une matrice contenant des résultats

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

Quand vous voulez obtenir de la valeur, vous utiliserez

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

D’autres personnes ont déjà suggéré mon idée de départ, la méthode de la matrice, mais en plus de consolider les déclarations if, vous pouvez éviter ce que vous avez en vous assurant que les arguments fournis sont dans la plage attendue et en utilisant des retours en place (certains codes). Les normes que j'ai observées imposent un point de sortie pour les fonctions, mais j’ai constaté que les retours multiples sont très utiles pour éviter le codage par flèche et que les exceptions dans Java sont relativement rares. l’application stricte d’une telle règle de toute façon, car toute exception non interceptée levée à l’intérieur de la méthode constitue de toute façon un point de sortie possible). L'imbrication d'instructions de commutateur est une possibilité, mais pour la petite gamme de valeurs que vous cochez ici, je trouve si les instructions sont plus compactes et ne risquent pas de générer une grande différence de performances, en particulier si votre programme est basé sur une rotation plutôt que sur de vraies performances. -temps.

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 que ce ne serait autrement dû à l'irrégularité de certaines parties du mappage input-> result. Je préfère le style de matrice en raison de sa simplicité et de la manière dont vous pouvez configurer la matrice pour qu'elle ait un sens visuel (même si cela est en partie influencé par mes souvenirs de cartes Karnaugh):

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

Mise à jour: Compte tenu de votre mention du blocage/frappe, voici une modification plus radicale de la fonction qui utilise des types énumérés propertied/holding-attribute pour les entrées et le résultat et modifie également un peu le résultat pour tenir compte du blocage, ce qui devrait entraîner une perte plus importante. fonction 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;
}

Vous n'avez même pas besoin de changer la fonction elle-même si vous voulez ajouter des blocs/attaques de plus hauteurs, juste les enums; l'ajout de types de déplacements nécessitera probablement une modification de la fonction. De même, EnumSets pourrait être plus extensible que d’utiliser des énumérations supplémentaires en tant que propriétés de l’énumération principale, par ex. EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...); puis attacks.contains(move) plutôt que move.type == MoveType.ATTACK, bien que l'utilisation de EnumSets soit probablement un peu plus lente que les vérifications directes.


Pour le cas où un bloc réussi aboutit à un compteur, vous pouvez remplacer if (one.height == two.height) return LandedHit.NEITHER; par

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 certaines des instructions if par 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 liseuses plus difficiles à lire, donc je ne le recommanderais pas pour les ramifications plus complexes.

69
JAB

Pourquoi ne pas utiliser un tableau?

Je vais commencer depuis le début. Je vois un motif, les valeurs vont de 0 à 3 et vous voulez attraper toutes les valeurs possibles. Voici votre table:

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

quand on regarde ce même binaire de table on voit les résultats suivants:

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

Maintenant, vous voyez peut-être déjà un motif, mais quand je combine les valeurs un et deux, je vois que vous utilisez toutes les valeurs 0000, 0001, 0010, ..... 1110 et 1111. Maintenant, combinons les valeurs un et deux pour en faire une seule Entier de 4 bits.

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

Lorsque nous traduisons cela en valeurs décimales, nous voyons un tableau très possible de valeurs où les valeurs un et deux combinées pourraient être utilisées comme index:

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

Le tableau est alors {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}, où son index est simplement un et deux combinés.

Je ne suis pas un programmeur Java mais vous pouvez vous débarrasser de toutes les instructions if et les écrire simplement comme ceci:

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

Je ne sais pas si un décalage en bits par 2 est plus rapide que la multiplication. Mais cela pourrait valoir la peine d'essayer.

50
dj bazzie wazzie

Cela utilise un peu de bitmagic (vous le faites déjà en tenant deux bits d'information (low/high & attack/block) dans un seul entier):

Je ne l'ai pas lancé, je l'ai seulement tapé ici, s'il vous plaît, double-cocher. L'idée fonctionne sûrement. MODIFIER: Il est maintenant testé pour chaque entrée, fonctionne 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;
    }
}

Ou devrais-je suggérer de séparer les deux bits d'information en variables distinctes? Le code basé principalement sur des opérations de bits comme celles ci-dessus est généralement très difficile à maintenir.

24
elias

Pour être tout à fait honnête, chacun a son propre style de code. Je n'aurais pas pensé que la performance serait trop affectée. Si vous comprenez mieux que d’utiliser une version boîtier commutateur, continuez à l’utiliser.

Vous pouvez imbriquer les if, ce qui pourrait entraîner une légère augmentation des performances de vos dernières vérifications if, car elles n'auraient pas subi autant d'instructions if. Mais dans le contexte d'un cours de base Java, il n'en tirera probablement aucun avantage.

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

Alors, au lieu 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 ferais ...

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

Et reformatez-le comme vous préférez.

Cela ne donne pas une meilleure apparence au code, mais l'accélère potentiellement un peu, je crois.

20
Joe Harper

Voyons ce que nous savons

1: vos réponses sont symétriques pour P1 (joueur un) et P2 (joueur deux). Cela a du sens pour un jeu de combat mais vous pouvez également en tirer parti pour améliorer votre logique.

2: 3 battements 0 battements 2 battements 1 battements 3. Les seuls cas non couverts par ces cas sont des combinaisons de 0 contre 1 et 2 contre 3. Pour le dire autrement, le tableau des victoires unique ressemble à ceci: 0 battements 2, 1 battements 3, 2 temps 1, 3 temps 0.

3: Si 0/1 s’affrontent, il y a un match nul sans coup sûr, mais si les 2/3 s’affrontent, les deux coups

Tout d’abord, construisons une fonction à sens unique nous disant si nous avons gagné:

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

Nous pouvons ensuite utiliser cette fonction pour composer le résultat 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;
}

Bien que cela soit sans doute plus complexe et probablement plus lent que la consultation de table proposée dans de nombreuses réponses, je pense que c'est une méthode supérieure, car elle encapsule la logique de votre code et la décrit à toute personne qui lit votre code. Je pense que cela en fait une meilleure mise en œuvre.

(Cela fait un moment que je n'ai pas fait de Java alors excuses si la syntaxe est désactivée, j'espère qu'elle est toujours intelligible si je l'ai un peu mal)

En passant, 0-3 clairement moyenne quelque chose; ce ne sont pas des valeurs arbitraires, il serait donc utile de les nommer.

12
Jack Aidley

J'espère que j'ai bien compris la logique. Que diriez-vous de quelque chose comme:

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

La vérification d'un coup haut ou d'un coup bas n'est pas bloquée et la même chose pour le joueur deux.

Edit: L'algorithme n'a pas été complètement compris, "hit" attribué lors du blocage que je n'avais pas réalisé (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;
}
11
Chris

Je n'ai pas d'expérience avec Java et il pourrait donc y avoir des fautes de frappe. Veuillez considérer le code comme pseudo-code.

J'irais avec un simple commutateur. Pour cela, vous auriez besoin d'une évaluation de numéro unique. Cependant, dans ce cas, puisque 0 <= one < 4 <= 9 et 0 <= two < 4 <= 9, nous pouvons convertir les deux ints en un simple int en multipliant one par 10 et en ajoutant two. Ensuite, utilisez un commutateur dans le nombre résultant comme ceci:

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

Il y a une autre méthode courte que je veux juste souligner en tant que code théorique. Cependant, je ne l'utiliserais pas car il comporte une complexité supplémentaire à laquelle vous ne voulez normalement pas faire face. La complexité supplémentaire provient de base 4, car le comptage est 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];
}

Vraiment juste une note supplémentaire, au cas où il me manque quelque chose de Java. Dans PHP je ferais:

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

Puisque vous préférez imbriquer les conditions if imbriquées, voici une autre méthode.
Notez qu'il n'utilise pas le membre result et qu'il ne modifie aucun état.

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

Essayez-le avec le boîtier de l'interrupteur. ..

Jetez un oeil ici ou ici pour plus d'informations à ce sujet

switch (expression)
{ 
  case constant:
        statements;
        break;
  [ case constant-2:
        statements;
        break;  ] ...
  [ default:
        statements;
        break;  ] ...
}

Vous pouvez ajouter plusieurs conditions (pas simultanément) et même avoir une option par défaut si aucun autre cas n'a été résolu.

PS: Seulement si une condition doit être remplie ..

Si 2 conditions se présentent simultanément .. Je ne pense pas que l'interrupteur peut être utilisé. Mais vous pouvez réduire votre code ici.

instruction de commutateur Java plusieurs cas

6
Nevin Madhukar K
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2
6
Dawood ibn Kareem

La première chose qui me vint à l’esprit fut essentiellement la même réponse donnée par Francisco Presencia, mais quelque peu optimisée:

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

Vous pouvez l’optimiser davantage en faisant du dernier cas (pour 3) le cas par défaut:

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

L'avantage de cette méthode est qu'il est plus facile de voir quelles valeurs pour one et two correspondent à quelles valeurs renvoyées par rapport à certaines des autres méthodes suggérées.

6
David R Tribble

Vous pouvez utiliser un switch case au lieu de mutiple if

De plus, étant donné que vous avez deux variables, vous devez les fusionner pour les utiliser dans switch.

Cochez cette instruction de commutateur Java pour gérer deux variables?

4
Rahul Tripathi

Un bon point serait de définir les règles sous forme de texte, vous pouvez alors dériver plus facilement de la formule correcte. Ceci est extrait de la représentation du tableau de Nice de laalto:

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

Et voici quelques commentaires généraux, mais vous devriez les décrire en termes de règles:

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

Vous pourriez bien sûr réduire ce code en moins de code, mais il est généralement préférable de comprendre ce que vous codez plutôt que de trouver une solution compacte.

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

Quelques explications sur les hits compliqués p1/p2 seraient géniales, a l’air intéressant!

3
Marcellus

La solution la plus courte et toujours lisible:

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

ou même plus court:

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

Ne contient pas de chiffres "magiques";) J'espère que ça aide.

3
P.W.

En dessinant une table entre un/deux et le résultat, je vois un motif,

if(one<2 && two <2) result=0; return;

Ce qui précède réduirait au moins 3 si déclarations. Je ne vois pas de modèle prédéfini et je ne suis pas en mesure de puiser beaucoup dans le code donné - mais si une telle logique peut être dérivée, elle réduirait un certain nombre d'instructions if.

J'espère que cela t'aides.

3
AnonNihcas

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

2
user1837841

Grâce à @ Joe Harper, j'ai utilisé une variante de sa réponse. Pour le réduire davantage car 2 résultats sur 4 étaient les mêmes, je l'ai encore réduit.

J'y reviendrai peut-être à un moment donné, mais s'il n'y a pas de résistance majeure causée par plusieurs déclarations if-, je la garderai pour le moment. Je vais examiner la matrice de la table et passer aux solutions d’énoncé.

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

J'utiliserais une carte, une HashMap ou une TreeMap

Surtout si les paramètres ne sont pas sous la forme 0 <= X < N

Comme un ensemble d'entiers positifs aléatoires ..

Code

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);
    }
}
1
Khaled.K

Voici une version assez concise, similaire à réponse de JAB . Cela utilise une carte à stocker qui se déplace triomphant sur les autres.

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

Exemple:

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

Impressions:

P1Win
1
Duncan Jones

Personnellement, j'aime bien mettre en cascade les opérateurs ternaires:

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

Mais dans votre cas, vous pouvez utiliser:

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

Ou, vous pouvez remarquer un motif 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

Pour que vous puissiez utiliser la magie:

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);
}
1
Kirill Gamazkov
  1. Utilisez des constantes ou des enums pour rendre le code plus lisible
  2. Essayez de scinder le code en plusieurs fonctions
  3. Essayez d'utiliser la symétrie du problème

Voici une suggestion à quoi cela pourrait ressembler, mais l’utilisation d’un contenu ici est toujours un peu moche:

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

Il serait préférable d'utiliser un type structuré pour l'entrée et la sortie. L'entrée comporte en réalité deux champs: la position et le type (bloc ou attaque). La sortie comporte également deux champs: player1Wins et player2Wins. Encoder cela dans un seul entier rend plus difficile la lecture du 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;
}

Malheureusement, Java n'est pas très efficace pour exprimer ce type de types de données.

0
peq