The following code does work how I need it to, but it\'s ugly, excessive or a number of other things. I\'ve looked at formulas and attempted to write a few solutions, but I
You may use a switch case instead of mutiple if
Also to mention that since you have two variables then you have to merge the two variables to use them in switch
Check this Java switch statement to handle two variables?
The shortest and still readable solution:
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;
}
or even shorter:
static public int fightMath(int one, int two)
{
if (one / 2 == two / 2) return (one / 2) * 3;
return 1 + (one + two + one / 2) % 2;
}
Doesn't contain any "magic" numbers ;) Hope it helps.
This uses a little bit of bitmagic (you're already doing it by holding two bits of information (low/high & attack/block) in a single integer):
I haven't run it, only typed it here, please doublecheck. The idea surely works.
EDIT: It is now tested for every input, works fine.
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;
}
}
Or should I suggest to separate the two bits of information into separate variables? Code based mostly on bit operations like this above is usually really hard to maintain.
To be quite honest, everyone has their own style of code. I wouldn't have thought performance would be affected too much. If you understand this better than using a switch case version, then carry on using this.
You could nest the ifs , so potentially there would be a slight performance increase for your last if checks as it wouldn't have gone through as many if statements. But in your context of a basic java course it probably won't benefit.
else if(one == 3 && two == 3) { result = 3; }
So, instead of...
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; }
You'd do...
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; }
}
And just reformat it as you'd prefer.
This doesn't make the code look better, but potentially speeds it up a little I believe.
Here's a fairly concise version, similar to JAB's response. This utilises a map to store which moves triumph over others.
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;
}
}
Example:
System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));
Prints:
P1Win
Here is a suggestion how this could look like, but using an ints here is still kind of ugly:
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);
}
It would be nicer to use a structured type for the input and the output. The input actually has two fields: the position and the type (block or attack). The output also has two fields: player1Wins and player2Wins. Encoding this into a single integer makes it harder to read the 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;
}
Unfortunately, Java is not very good at expressing those kinds of data-types.