下面的代码确实按照我需要的方式工作,但它很丑,过多或其他一些事情。我已经看了公式,并试图写一些解决方案,但我最终得到了类似数量的语句。

在这种情况下,是否有一种数学公式对我有益,或者是否可以接受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;
}

当前回答

我个人喜欢级联三元运算符:

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

其他回答

使用常量或枚举使代码更具可读性 尝试将代码拆分为更多的函数 试着利用问题的对称性

这里是一个建议,但在这里使用int型仍然有点难看:

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

使用结构化类型作为输入和输出会更好。输入实际上有两个字段:位置和类型(阻挡或攻击)。输出也有两个字段:player1Wins和player2Wins。将其编码为单个整数会使代码更难阅读。

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

不幸的是,Java并不擅长表达这类数据类型。

由于您的数据集非常小,您可以将所有内容压缩为1个长整数并将其转换为公式

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

更多的位变体:

这利用了所有东西都是2的倍数这一事实

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

神奇常数的起源

我能说什么呢?世界需要魔法,有时某些事情的可能性需要它的创造。

解决OP问题的函数的本质是从2个数字(1,2),域{0,1,2,3}到范围{0,1,2,3}的映射。每个答案都涉及了如何实现该映射。

此外,您可以在许多答案中看到问题的重述,即12个以4为基数的2位数字N(1,2)的映射,其中1是数字1,2是数字2,N = 4* 1 + 2;N ={0,1,2,…,15}——16个不同的值,这很重要。函数的输出是一个以4为基数的1位数字{0,1,2,3}——4个不同的值,这也很重要。

现在,1位以4为底的数可以表示为2位以2为底的数;{0,1,2,3} ={00,01,10,11},因此每个输出只能用2位编码。从上面来看,只有16种不同的输出可能,所以16*2 = 32位是编码整个地图所必需的;这些都可以装进一个整数。

常数M是映射M的编码,其中M(0)以位M[0:1]编码,M(1)以位M[2:3]编码,M (n)以位M[n*2:n*2+1]编码。

剩下的就是索引和返回常量的右边部分,在这种情况下,你可以将M右移2*N次,并取2个最低有效位,即(M >> 2*N) & 0x3。表达式(one << 3)和(two << 1)只是相乘,同时注意到2*x = x << 1和8*x = x << 3。

说实话,每个人都有自己的代码风格。我没想到性能会受到太大影响。如果您比使用开关箱版本更能理解这一点,那么请继续使用此版本。

您可以嵌套if语句,因此最后的if检查可能会略微提高性能,因为它不会经过那么多if语句。但是在你的java基础课程中,它可能不会有什么好处。

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

所以,与其…

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

你会做……

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

按照你的喜好重新格式化。

这并没有使代码看起来更好,但我相信它可能会加快一点速度。

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

现在,这里计算的内容更加清楚了:这强调了我们正在计算谁受到了什么攻击,并返回两个结果。

然而,这可能会更好;布尔数组有点不透明。我喜欢表格查找方法,但我更倾向于以一种能够明确游戏语义的方式来编写它。也就是说,与其“攻击为零,防御为一,结果是没有命中”,不如找到一种方法,让代码更清楚地暗示“低踢攻击和低阻挡防御,结果是没有命中”。让代码反映游戏的业务逻辑。

一个好的观点是将规则定义为文本,这样你就可以更容易地推导出正确的公式。这是从laalto漂亮的数组表示中提取出来的:

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

这里我们有一些一般性的评论,但你应该用规则来描述它们:

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

当然,您可以将其压缩为更少的代码,但理解您编写的代码而不是寻找紧凑的解决方案通常是一个好主意。

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

对复杂的p1/p2点击的一些解释会很棒,看起来很有趣!