Tom*_*rth 260 math if-statement formula
以下代码确实可以满足我的需求,但它很难看,过多或者其他很多东西.我已经看过公式并尝试编写一些解决方案,但我最终得到了类似数量的陈述.
在这种情况下是否有一种数学公式可以使我受益,或者如果语句可以接受,那么它是16吗?
为了解释代码,这是一种基于同步回合的游戏.两个玩家每个都有四个动作按钮,结果来自一个数组(0-3),但变量'一'和'两个'可以是如果有帮助就分配任何东西 结果是,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;
}
Run Code Online (Sandbox Code Playgroud)
laa*_*lto 599
如果你不能提出一个公式,你可以使用一个表来获得如此有限的结果:
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];
Run Code Online (Sandbox Code Playgroud)
waT*_*eim 201
由于您的数据集非常小,您可以将所有内容压缩为1个长整数并将其转换为公式
public int fightMath(int one,int two)
{
return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}
Run Code Online (Sandbox Code Playgroud)
这使得事实是一切都是2的倍数
public int fightMath(int one,int two)
{
return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}
Run Code Online (Sandbox Code Playgroud)
我能说什么?世界需要魔法,有时候需要某种东西来创造它.
解决OP问题的函数的本质是从2个数字(一个,两个),域{0,1,2,3}到范围{0,1,2,3}的映射.每个答案都涉及如何实现该地图.
此外,您可以在一些答案中看到问题的重述,作为1个2位数字4的数字N(一,二)的映射,其中一个是数字1,两个是数字2,N = 4*1 +两个; N = {0,1,2,...,15} - 十六个不同的值,这很重要.该函数的输出是一个1位数的基数4数{0,1,2,3} - 4个不同的值,也很重要.
现在,1位数的基数4可以表示为2位数的基数2; {0,1,2,3} = {00,01,10,11},因此每个输出只能用2位编码.从上面可以看出,只有16种不同的输出,所以16*2 = 32位是编码整个地图所必需的.这可以全部适合1个整数.
常数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.表达式(一个<< 3)和(两个<< 1)只是将事物相乘,同时注意到2*x = x << 1和8*x = x << 3.
Eri*_*ert 98
除了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);
}
Run Code Online (Sandbox Code Playgroud)
现在更清楚的是这里计算的内容:这强调我们计算的是受到攻击的人,并返回两个结果.
然而,这可能会更好; 布尔数组有些不透明.我喜欢表查找方法,但我倾向于以这样的方式编写它,以便明确预期的游戏语义.也就是说,而不是"零攻击和一个防御导致没有击中",而是找到一种方法使代码更清楚地暗示"低踢攻击和低防御导致没有击中". 使代码反映游戏的业务逻辑.
djm*_*.im 87
您可以创建包含结果的矩阵
int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};
Run Code Online (Sandbox Code Playgroud)
当你想获得价值时,你会使用
public int fightMath(int one, int two) {
return this.results[one][two];
}
Run Code Online (Sandbox Code Playgroud)
JAB*_*JAB 69
其他人已经提出了我的初步想法,矩阵方法,但除了合并if语句之外,你可以通过确保提供的参数在预期范围内并使用就地返回(一些编码)来避免你所拥有的一些内容.标准我已经看到强制执行单点退出功能,但我发现多次返回对于避免箭头编码非常有用,并且随着Java中异常的普遍存在,无论如何严格执行这样的规则并不重要因为在方法内部抛出的任何未捕获的异常都是可能的退出点.嵌套切换语句是可能的,但对于您在此处检查的小范围值,我发现语句是否更紧凑并且不太可能导致性能差异,特别是如果您的程序是基于回合而不是真实的-时间.
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
}
Run Code Online (Sandbox Code Playgroud)
由于输入 - >结果映射的部分不规则,这最终会导致可读性降低.我喜欢矩阵样式,因为它的简单性以及如何设置矩阵以便在视觉上有意义(尽管这部分受到我对卡诺图的记忆的影响):
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
Run Code Online (Sandbox Code Playgroud)
更新:鉴于你提到阻塞/命中,这里是对函数的一个更彻底的改变,它利用了属性/属性保持枚举类型的输入和结果,并且稍微修改了结果以解决阻塞,这应该导致更多可读功能.
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;
}
Run Code Online (Sandbox Code Playgroud)
如果你想增加更多高度的块/攻击,你甚至不需要改变功能本身; 但是,添加其他类型的移动可能需要修改该功能.此外,EnumSets可能比使用额外的枚举作为主枚举的属性更具可扩展性,例如EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);然后attacks.contains(move)而不是move.type == MoveType.ATTACK,尽管使用EnumSets可能比直接等于检查稍慢.
对于成功的块导致计数器的情况,您可以替换if (one.height == two.height) return LandedHit.NEITHER;为
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;
}
Run Code Online (Sandbox Code Playgroud)
此外,if使用三元运算符(boolean_expression ? result_if_true : result_if_false)替换一些语句可以使代码更紧凑(例如,前一个块中的代码将变为return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;),但这可能导致难以读取的oneliner,所以我不会'推荐它用于更复杂的分支.
dj *_*zie 50
为什么不使用阵列?
我将从头开始.我看到一个模式,值从0到3,你想要捕获所有可能的值.这是你的桌子:
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
Run Code Online (Sandbox Code Playgroud)
当我们查看同一个表二进制文件时,我们会看到以下结果:
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
Run Code Online (Sandbox Code Playgroud)
现在也许你已经看到了一些模式,但是当我合并第一和第二的价值时,我看到你正在使用所有值0000,0001,0010,...... 1110和1111.现在让我们结合第一和第二的值来制作一个4位整数.
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
Run Code Online (Sandbox Code Playgroud)
当我们将其转换回十进制值时,我们会看到一个非常可能的值数组,其中一个和两个组合可以用作索引:
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
Run Code Online (Sandbox Code Playgroud)
然后是数组{0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3},它的索引只是一个和两个组合.
我不是Java程序员,但你可以摆脱所有if语句,只需将其写成如下:
int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two];
Run Code Online (Sandbox Code Playgroud)
我不知道2比特的移位是否比乘法更快.但它值得一试.
eli*_*ias 24
这使用了一点bitmagic(你已经通过在一个整数中保存两位信息(低/高和攻击/块)来做到这一点):
我没有运行它,只在这里打字,请仔细检查.这个想法肯定有效.
编辑:它现在测试每个输入,工作正常.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
或者我应该建议将两位信息分成单独的变量?主要基于上述位操作的代码通常很难维护.
Joe*_*per 20
说实话,每个人都有自己的代码风格.我不会想到性能会受到太大影响.如果你比使用switch case版本更好地理解这一点,那么继续使用它.
你可以嵌套ifs,所以你的上一次if检查可能会有轻微的性能提升,因为它不会经历尽可能多的if语句.但是在基本的java课程中,它可能不会受益.
else if(one == 3 && two == 3) { result = 3; }
Run Code Online (Sandbox Code Playgroud)
所以,而不是......
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; }
Run Code Online (Sandbox Code Playgroud)
你做...
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; }
}
Run Code Online (Sandbox Code Playgroud)
然后根据您的喜好重新格式化.
这并没有使代码看起来更好,但我相信可能会加速它.
Jac*_*ley 12
让我们看看我们所知道的
1:你的答案是P1(玩家一)和P2(玩家二)的对称.这对于格斗游戏来说是有意义的,但也可以利用它来改善你的逻辑.
2:3节拍0节拍2节拍1节拍3.这些案例未涵盖的唯一案例是0对1和2对3的组合.换句话说,唯一的胜利表看起来像这样:0节拍2,1节拍3节,2节拍3节拍0.
3:如果0/1相互对抗那么就会有一次无可匹敌的抽签,但是如果2/3上升,那么每次击中
首先,让我们构建一个单向函数,告诉我们是否赢了:
// returns whether we beat our opponent
public boolean doesBeat(int attacker, int defender) {
int[] beats = {2, 3, 1, 0};
return defender == beats[attacker];
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用这个函数来组成最终结果:
// 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;
}
Run Code Online (Sandbox Code Playgroud)
虽然这可能更复杂,并且可能比许多答案中提供的表查找更慢,但我认为它是一种优越的方法,因为它实际上封装了代码的逻辑并将其描述给正在阅读代码的任何人.我认为这使它成为更好的实现.
(自从我做了任何Java以来已经有一段时间了,如果语法关闭了,那么道歉,希望如果我有点错误它仍然是可理解的)
顺便说一句,0-3显然意味着什么; 它们不是任意值,因此有助于命名它们.
Chr*_*ris 11
我希望我能正确理解逻辑.怎么样的:
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;
}
Run Code Online (Sandbox Code Playgroud)
检查一个击中高点或一个击中低点不会被阻止,对于玩家二点也是如此.
编辑:算法没有完全理解,阻止"击中"时我没有意识到(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;
}
Run Code Online (Sandbox Code Playgroud)
Fra*_*cia 10
我没有Java经验,所以可能会有一些拼写错误.请将代码视为伪代码.
我会选择一个简单的开关.为此,您需要进行单一数字评估.但是,对于这种情况,因为0 <= one < 4 <= 9和0 <= two < 4 <= 9,我们可以通过乘以one10并添加将两个int转换为简单的int two.然后在结果数字中使用开关,如下所示:
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;
}
Run Code Online (Sandbox Code Playgroud)
还有一个简短的方法,我只想指出它是一个理论代码.但是我不会使用它,因为它有一些你通常不想处理的额外复杂性.额外的复杂性来自基数4,因为计数是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];
}
Run Code Online (Sandbox Code Playgroud)
真的只是额外的注释,以防我遗漏了Java的东西.在PHP中我会这样做:
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];
}
Run Code Online (Sandbox Code Playgroud)
既然您更喜欢嵌套if条件,那么这是另一种方式.
请注意,它不使用该result成员,并且它不会更改任何状态.
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;
}
Run Code Online (Sandbox Code Playgroud)
尝试使用开关外壳...
switch (expression)
{
case constant:
statements;
break;
[ case constant-2:
statements;
break; ] ...
[ default:
statements;
break; ] ...
}
Run Code Online (Sandbox Code Playgroud)
您可以向其添加多个条件(不同时),甚至可以使用默认选项,其中不满足其他任何情况.
PS:只有满足一个条件..
如果两个条件同时出现..我不认为可以使用开关.但是你可以在这里减少你的代码.
我遇到的第一件事基本上与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;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以通过将最后一个案例(对于3)作为默认情况进一步优化它:
//case 22:
//case 23:
//case 32:
//case 33:
default:
return 3;
Run Code Online (Sandbox Code Playgroud)
此方法的优点是,与其他一些建议的方法相比,更容易查看哪些值one和two哪些返回值对应.
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
28771 次 |
| 最近记录: |