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;
}
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];
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;
}
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;
}
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.
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.
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];
}
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, EnumSet
s 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 EnumSet
s 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.
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.
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.
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.
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.
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;
}
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];
}
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;
}
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.
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2
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.
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?
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!
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.
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.
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
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; }
}
}
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);
}
}
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
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);
}
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.