Reputation: 340
One part of my app has 6 different inputs, where each input has 5 different possible values. This works out to 15,625 permutations. I'd like to believe that I don't have to code out every single one, but I'm really unsure. I've started coding it as if I have to code out every single one, but it got me thinking that there must be a better way. So I've come here to see if any more experienced coders know of a better way to do this.
Is there any other way? Other than having to code out every single permutation?
Equation Explanation: There are 6 limbs (inputs). Each input depends on its percentage of leftover health. These dependencies (expressed as a percentage) are less than or equal to 0, greater than 0 and less than or equal to 25, greater than 25 and less than or equal to 50, greater than 50 and less than or equal to 75, and greater than 75. Each permutation will more often than not result in a different output. This output is displayed within the app for the user to use.
[EDITS] This is not a damage calculation. These are penalties applied to player stats based upon how much damage they've taken. The expected results either don't affect their stats or reduce their stats. Occasionally, the number subtracted is the same for different variables. If you look at the code below, you'll see that the number is the same for head > 0.0 && head <= 0.25
and chest > 0.0 && chest <= 0.25
, as well as chest > 0.25 && chest <= 0.50
and leftarm > 0.25 && leftarm <= 0.50
and rgitharm > 0.25 && rightarm <= 0.50
[EDITS] I'll reply to the comments here:
@Frank N. Stein: Oops. Thanks for the correction :)
@aioobe: I've edited the equation explanation
to hopefully clarify the expected outcomes.
@ian: Those values cannot be worked out as they're percentages. I would never be able to work them out.
@Marco13: It seems that way simply because I haven't finished coding every permutation out yet. On second thought, I don't think I need to display dead. I can just leave those permutations out of the calculations as it's taken care of in a different activity.
@aioobe: Is this better than enumerating the outcomes as suggested by @Ian and @Durandal.
@Danil Gaponov: Is this better or worse than what was suggested by @Durandal in his/her answer?
@Durandal: I've updated the equation explanation
to hopefully be more clear about what I'm expecting for each of these outcomes.
Thanks for your patience :)
Would I be able to do something similar to this:
// HEALTH
// N > 0.75 == Full
// N > 0.50 && <= 0.75 == HighHealth
// N > 0.25 && <= 0.50 == MedHealth
// N > 0.00 && <= 0.25 == LowHealth
// N <= 0.00 == Dead
IE I could then write h == Full
or h == Health.Full
. This would make my life a lot easier would make the code much easier to read. Although, I'd still have to work out every single permutation, it'd be a little more manageable.
Another idea I have is to put these calculations in a different class and run it within the activity. Something like this:
// SETTING DATA
// WEAPON 1
// MELEE
weapon1attack.setText(PenaltiesMeleeHelper);
weapon1accuracy.setText(PenaltiesMeleeHelper);
// RANGED
weapon1thrownattack.setText(PenaltiesThrownHelper);
weapon1thrownaccuracy.setText(PenaltiesThrownHelper);
// WEAPON 2
// MELEE
weapon2attack.setText(PenaltiesMeleeHelper);
weapon2accuracy.setText(PenaltiesMeleeHelper);
// RANGED
weapon2thrownattack.setText(PenaltiesThrownHelper);
weapon2thrownaccuracy.setText(PenaltiesThrownHelper);
Would any of these be possible?
[EDIT] Would an mathematical algorithm work? Let's say I have Checker that checks for the penalties every time the activity starts. If it finds a penalty/penalties, then it adds them all together for me and subtracts them from the appropriate stats.
For example:
// There are 3 main outcomes that are suffered for being damaged too much
// The reduction of parry (PRY), dodge (DOD), and damage & accuracy (AM)
// The deductions range from -2 to -5.
// I could declare Enums for each type
PRY2 = (- 2)
DOD2 = (- 2)
AM2 = (- 2)
PRY2 would be put into the equation if la > 0.50 && la <= 0.75 || ra > 0.50 && ra <= 0.75
DOD2 would be put into the equation if la > 0.50 && la <= 0.75 || ra > 0.50 && ra <= 0.75 || ll > 0.50 && ll <= 0.75 || rl > 0.50 && rl <= 0.75
AM2 would be put into the equation if la > 0.50 && la <= 0.75 || ra > 0.50 && ra <= 0.75 || c > 0.50 && c <= 0.75
PRY3 = (- 3)
DOD3 = (- 3)
AM3 = (- 3)
PRY3 would be put into the equation if c > 0.25 && c <= 0.50 || la > 0.25 && la <= 0.50 || ra > 0.25 && ra <= 0.50 ||
DOD3 would be put into the equation if c > 0.25 && c <= 0.50 || la > 0.25 && la <= 0.50 || ra > 0.25 && ra <= 0.50 || ll > 0.25 && ll <= 0.50 || rl > 0.25 && rl <= 0.50
AM3 would be put into the equation if c > 0.25 && c <= 0.50 || la > 0.25 && la <= 0.50 || ra > 0.25 && ra <= 0.50 ||
PRY4 = (- 4)
DOD4 = (- 4)
AM4 = (- 4)
PRY4 would be put into the equation if la > 0.0 && la <= 0.25 || ra > 0.0 && ra <= 0.25 || ll > 0.0 && ll <= 0.25 || rl > 0.0 && rl <= 0.25
DOD4 would be put into the equation if la > 0.0 && la <= 0.25 || ra > 0.0 && ra <= 0.25 || ll > 0.0 && ll <= 0.25 || rl > 0.0 && rl <= 0.25
AM4 would be put into the equation if la > 0.0 && la <= 0.25 || ra > 0.0 && ra <= 0.25 || ll > 0.0 && ll <= 0.25 || rl > 0.0 && rl <= 0.25
PRY5 = (- 5)
DOD5 = (- 5)
AM5 = (- 5)
PRY5 would be put into the equation if h > 0.0 && h <= 0.25 || c > 0.0 && c <= 0.25 || la <= 0.0 || ra <= 0.0 || ll <= 0.0 || rl <= 0.0
DOD5 would be put into the equation if h > 0.0 && h <= 0.25 || c > 0.0 && c <= 0.25 || la <= 0.0 || ra <= 0.0 || ll <= 0.0 || rl <= 0.0
AM5 would be put into the equation if h > 0.0 && h <= 0.25 || c > 0.0 && c <= 0.25 || la <= 0.0 || ra <= 0.0 || ll <= 0.0 || rl <= 0.0
// I'm not sure how, but somehow it would know, since it's PRY, then it knows to take the Enum and deduct it from the PRY slot and not the DOD or AM slots.
// Is this a step in the right direction?
Here's the code that I have currently (coding out every single permutation):
// SETTING DATA
// WEAPON 1
// MELEE
try {
float STRF = NumberUtils.toFloat(pref.getString("strength", ""), 0.0f);
float MCF = NumberUtils.toFloat(pref.getString("melee", ""), 0.0f);
float W1AttF = NumberUtils.toFloat(pref.getString("w1attack", ""), 0.0f);
// ALL LIMBS > 0.00 && h <= 0.25
if (MCF != 0.0 && W1AttF != 0.0 && h > 0.0 && h <= 0.25 && c > 0.0 && c <= 0.25
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 26));
}
// HEAD h > 0.25 && h <= 0.50
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.25 && h <= 0.50 && c > 0.0 && c <= 0.25
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 21));
}
// HEAD h > 0.50 && h <= 0.75
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.50 && h <= 0.75 && c > 0.0 && c <= 0.25
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 21));
}
// HEAD h > 0.75
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.75 && c > 0.0 && c <= 0.25
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 21));
}
// CHEST c > 0.25 && c <= 0.50
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.0 && h <= 0.25 && c > 0.25 && c <= 0.50
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 26));
}
// CHEST c > 0.50 && c <= 0.75
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.0 && h <= 0.25 && c > 0.50 && c <= 0.75
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 26));
}
// CHEST c > 0.75
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.0 && h <= 0.25 && c > 0.75
&& la > 0.0 && la <= 0.25 && ra > 0.0 && ra <= 0.25 && ll > 0.0 && ll <= 0.25
&& rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 26));
}
// HEAD ONLY
else if (MCF != 0.0 && W1AttF != 0.0 && h > 0.0 && h <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 5));
} else if (MCF != 0.0 && W1AttF != 0.0 && h <= 0.0) {
weapon1attack.setText("Dead");
}
// CHEST ONLY
else if (MCF != 0.0 && W1AttF != 0.0 && c > 0.0 && c <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 5));
} else if (MCF != 0.0 && W1AttF != 0.0 && c > 0.25 && c <= 0.50) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 3));
} else if (MCF != 0.0 && W1AttF != 0.0 && c > 0.50 && c <= 0.75) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 2));
} else if (MCF != 0.0 && W1AttF != 0.0 && c <= 0.0) {
weapon1attack.setText("Dead");
}
// LEFT ARM ONLY
else if (MCF != 0.0 && W1AttF != 0.0 && la > 0.0 && la <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 4));
} else if (MCF != 0.0 && W1AttF != 0.0 && la > 0.25 && la <= 0.50) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 3));
} else if (MCF != 0.0 && W1AttF != 0.0 && la > 0.50 && la <= 0.75) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 2));
} else if (MCF != 0.0 && W1AttF != 0.0 && la <= 0.0) {
weapon1attack.setText("Dead");
}
// RIGHT ARM ONLY
else if (MCF != 0.0 && W1AttF != 0.0 && ra > 0.0 && ra <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 4));
} else if (MCF != 0.0 && W1AttF != 0.0 && ra > 0.25 && ra <= 0.50) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 3));
} else if (MCF != 0.0 && W1AttF != 0.0 && ra > 0.50 && ra <= 0.75) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 2));
} else if (MCF != 0.0 && W1AttF != 0.0 && ra <= 0.0) {
weapon1attack.setText("Dead");
}
// LEFT LEG ONLY
else if (MCF != 0.0 && W1AttF != 0.0 && ll > 0.0 && ll <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 4));
} else if (MCF != 0.0 && W1AttF != 0.0 && ll <= 0.0) {
weapon1attack.setText("Dead");
}
// RIGHT LEG ONLY
else if (MCF != 0.0 && W1AttF != 0.0 && rl > 0.0 && rl <= 0.25) {
weapon1attack.setText(Float.toString((STRF + MCF + W1AttF) - 4));
} else if (MCF != 0.0 && W1AttF != 0.0 && rl <= 0.0) {
weapon1attack.setText("Dead");
}
else if (W1AttF == 0.0 || MCF == 0.0) {
weapon1attack.setText("");
} else if (MCF != 0.0 && W1AttF != 0.0) {
weapon1attack.setText(Float.toString(STRF + MCF + W1AttF));
}
}
catch (NumberFormatException ignore) {}
Upvotes: 3
Views: 212
Reputation: 1441
You can create a method that converts your parameters to unique integer from 0 to 15625 (5^6) corresponding to your permutation, and then branch according to it.
Edit: Another option may be to move this rules to a config file and process it in Java code.
Upvotes: 0
Reputation: 20059
There's too little to go on what the desired result for each case looks like to go on. From the comments in the code I gather its some kind of RPG damage calculation.
It might be best to describe the problem in natural language first, or rather not the problem, but the rules governing the outcome of each combination of parameters. Looking at the patterns that emerge from the rules, its usually possible the define a reasonably small set of cases that can be expressed as mathematical formulas.
If you really want to have absolutely free control of the outcome of each case, you would need to enumerate the cases, and build a data structure containing the outcome for each possible case (e.g. a data file listing the outcome for each combination that is read into memory in some form).
Speculating on what I see in your code, I presume you are trying to figure out damage depending on location, weapon, defense etc.
This lends itself well to using simple mathematical formulas, e.g. first determine the location, and depending on that assign a dmanage modifier (pseudocode):
modifier = 1.0; // defaults to "normal" damager
switch (location) {
case HEAD:
modfier *= 2.00; // double damage
case CHEST:
modifier *= 0.75; // 75% damage
default:
// elsewhere: no change
break;
}
// ... further modify based on other parameters like attack
// type, defense type whatever you have
// now figure out a base damage, purely fictional
baseDmg = attack - defense;
// we don't want negative damage in case def > att,
// so if negative set to 1.0
baseDmg = Math.max(baseDmg, 1.0);
// caluclate final dmg using modifier
realDmg = baseDmg * modifier;
if (realDmg > life)
// dead!
else
// ouch!
Try to break your rules down so you can write it like the example above.
Upvotes: 2