下面的代码确实按照我需要的方式工作,但它很丑,过多或其他一些事情。我已经看了公式,并试图写一些解决方案,但我最终得到了类似数量的语句。
在这种情况下,是否有一种数学公式对我有益,或者是否可以接受16个if语句?
为了解释代码,这是一款基于同时回合制的游戏。两名玩家各有四个操作按钮,结果来自一个数组(0-3),但变量“1”和“2”可以赋值任何东西,如果这有帮助的话。结果是,0 =双方都不赢,1 = p1赢,2 = p2赢,3 =双方都赢。
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;
}
这使用了一点bitmagic(你已经通过在一个整数中保存两个比特信息(low/high & attack/block)来做到这一点):
我没有运行,只是在这里输入,请仔细检查。这个想法肯定有效。
编辑:现在对每个输入进行测试,工作正常。
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;
}
}
或者我应该建议把这两个信息分离成单独的变量?
主要基于上面这样的位操作的代码通常很难维护。
我个人喜欢级联三元运算符:
int result = condition1
? result1
: condition2
? result2
: condition3
? result3
: resultElse;
但在你的情况下,你可以使用:
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];
}
或者,你可以注意到比特的模式:
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
所以你可以使用魔法:
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);
}
如果你无法想出一个公式,你可以使用一个表格来描述有限的结果:
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];
我想到的第一件事基本上与Francisco Presencia给出的答案相同,但有所优化:
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;
}
}
你可以进一步优化它,使最后的情况(3)为默认情况:
//case 22:
//case 23:
//case 32:
//case 33:
default:
return 3;
此方法的优点是,与其他一些建议的方法相比,更容易看到1和2的哪个值对应于哪个返回值。
下面是一个相当简洁的版本,类似于JAB的回应。这使用地图来存储哪个移动战胜其他移动。
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;
}
}
例子:
System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));
打印:
P1Win
除了JAB之外,我不喜欢任何提出的解决方案。其他方法都不容易阅读代码和理解正在计算的内容。
以下是我如何编写这段代码——我只会c#,不懂Java,但你可以想象:
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);
}
现在,这里计算的内容更加清楚了:这强调了我们正在计算谁受到了什么攻击,并返回两个结果。
然而,这可能会更好;布尔数组有点不透明。我喜欢表格查找方法,但我更倾向于以一种能够明确游戏语义的方式来编写它。也就是说,与其“攻击为零,防御为一,结果是没有命中”,不如找到一种方法,让代码更清楚地暗示“低踢攻击和低阻挡防御,结果是没有命中”。让代码反映游戏的业务逻辑。