以下代码确实可以按我的需要工作,但它很丑陋、过度或其他一些事情。我查看了公式并尝试编写一些解决方案,但我最终得到了类似数量的语句。
在这种情况下,是否有一种数学公式对我有利,或者如果陈述可以接受,是否有 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;
}
f(a, b)
可以产生答案吗?您还没有解释计算的逻辑,因此所有答案都只是猪的口红。我将首先认真重新考虑您的程序逻辑,使用 int
标志进行操作非常过时。 enum
可以包含逻辑并且是描述性的,这将允许您以更现代的方式编写代码。
如果你不能想出一个公式,你可以使用一个表格来表示数量有限的结果:
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];
由于您的数据集很小,您可以将所有内容压缩为 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个数字(一,二),域{0,1,2,3}到范围{0,1,2,3}的映射。每个答案都涉及如何实现该地图。
此外,您可以在许多答案中看到问题的重述,作为 1 个 2 位基数 4 数字 N(one,two) 的映射,其中 1 是数字 1,2 是数字 2,N = 4*one +两个; 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。表达式 (one << 3) 和 (two << 1) 只是将事物相乘,同时注意到 2*x = x << 1 和 8*x = x << 3。
我不喜欢除 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);
}
现在更清楚了这里计算的是什么:这强调了我们正在计算谁被什么攻击击中,并返回两个结果。
然而,这可能会更好;该布尔数组有些不透明。我喜欢表格查找方法,但我更倾向于以明确游戏语义的方式编写它。即与其说“一攻零一防不命中”,而是想办法让代码更清楚地暗示“低踢攻击低格挡不命中”。让代码体现游戏的业务逻辑。
您可以创建包含结果的矩阵
int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};
当您想获得价值时,您将使用
public int fightMath(int one, int two) {
return this.results[one][two];
}
其他人已经提出了我最初的想法,矩阵方法,但是除了合并 if 语句之外,您还可以通过确保提供的参数在预期范围内并使用就地返回(一些编码我见过的标准对函数强制执行单点退出,但我发现多次返回对于避免箭头编码非常有用,并且在 Java 中普遍存在异常情况下,无论如何严格执行这样的规则并没有多大意义因为在方法内抛出的任何未捕获的异常都是可能的退出点)。嵌套 switch 语句是可能的,但是对于您在此处检查的小范围值,我发现 if 语句更紧凑并且不太可能导致很大的性能差异,特别是如果您的程序是基于回合的而不是真实的-时间。
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
}
由于输入->结果映射部分的不规则性,最终的可读性确实不如其他情况。我更喜欢矩阵样式,因为它很简单,而且你可以设置矩阵以在视觉上有意义(尽管这部分受我对卡诺图的记忆的影响):
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
更新:鉴于您提到了阻塞/命中,这里对函数进行了更彻底的更改,该函数利用属性/属性保持枚举类型进行输入和结果,并且还稍微修改了结果以考虑阻塞,这应该会导致更多可读功能。
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;
}
如果您想添加更多高度的块/攻击,您甚至不必更改函数本身,只需枚举;但是,添加其他类型的移动可能需要修改函数。此外,EnumSet
s 可能比使用额外枚举作为主枚举的属性更具可扩展性,例如 EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
然后是 attacks.contains(move)
而不是 move.type == MoveType.ATTACK
,尽管使用 EnumSet
可能会比直接相等检查稍慢.
对于成功阻止导致计数器的情况,您可以将 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;
}
此外,使用三元运算符 (boolean_expression ? result_if_true : result_if_false
) 替换某些 if
语句可以使代码更紧凑(例如,前面块中的代码将变为 return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
),但这可能会导致更难-to-read oneliners 所以我不推荐它用于更复杂的分支。
one
和 two
的 int 值作为我的 spritesheet 上的起点。尽管它不需要太多额外的代码来实现这一点。
EnumMap
类可用于将枚举映射到整数偏移量(您也可以直接使用枚举成员的序数值,例如 Move.ATTACK_HIGH.ordinal()
将是 0
,Move.ATTACK_LOW.ordinal()
将是 { 6} 等,但是这比显式地将每个成员与一个值关联起来更脆弱/更不灵活,因为在现有成员之间添加枚举值会抛出计数,而 EnumMap
不会出现这种情况。)
attack(against)
方法添加到 Move
枚举,当移动是成功攻击时返回 HIT,当移动是被阻止的攻击时返回 BACKFIRE,当它不是攻击时返回 NOTHING。通过这种方式,您可以在一般情况下实现它(public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }
),并在需要时在特定动作上覆盖它(任何块都可以阻挡的弱动作,永远不会适得其反的攻击等)
为什么不使用数组?
我将从头开始。我看到了一个模式,值从 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
当我们查看同一个表二进制文件时,我们会看到以下结果:
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
现在也许您已经看到了一些模式,但是当我将值 1 和 2 组合在一起时,我发现您正在使用所有值 0000、0001、0010、..... 1110 和 1111。现在让我们将值 1 和 2 组合成一个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
当我们将其转换回十进制值时,我们会看到一个非常可能的值数组,其中 1 和 2 的组合可以用作索引:
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
然后数组是 {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}
,它的索引只是 1 和 2 的组合。
我不是 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];
我不知道位移 2 是否比乘法快。但这可能值得一试。
这使用了一点位魔法(您已经通过在一个整数中保存两位信息(低/高和攻击/块)来做到这一点):
我没有运行它,只是在这里输入,请仔细检查。这个想法肯定行得通。编辑:现在对每个输入都进行了测试,工作正常。
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;
}
}
还是我应该建议将这两个信息分成单独的变量?像上面这样主要基于位操作的代码通常很难维护。
return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
老实说,每个人都有自己的代码风格。我不会认为性能会受到太大影响。如果您比使用 switch case 版本更了解这一点,请继续使用它。
您可以嵌套 ifs ,因此最后一次 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; }
}
只需按照您的喜好重新格式化即可。
这不会使代码看起来更好,但我相信它可能会加快一点。
让我们看看我们知道什么
1:你的答案对于 P1(玩家一)和 P2(玩家二)是对称的。这对于格斗游戏来说是有意义的,但也是你可以利用它来提高你的逻辑的东西。
2:3 比 0 比 2 比 1 比 3。这些案例中唯一没有包含的情况是 0 比 1 和 2 比 3 的组合。换句话说,独特的胜利表是这样的:0 比 2、1 比3、2 拍 1、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];
}
然后我们可以使用这个函数来组成最终的结果:
// 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;
}
虽然这可以说比许多答案中提供的表查找更复杂并且可能更慢,但我相信这是一种更好的方法,因为它实际上封装了代码的逻辑并将其描述给正在阅读代码的任何人。我认为这使它成为一个更好的实现。
(我已经有一段时间没有做任何 Java 了,如果语法不正确,希望它仍然可以理解,如果我有一点错误的话)
顺便说一句,0-3 显然意味着什么;它们不是任意值,因此命名它们会有所帮助。
我希望我能正确理解逻辑。怎么样:
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;
}
检查一击高或一击低不会被阻止,玩家二也是如此。
编辑:算法尚未完全理解,在我没有意识到的阻塞时授予“命中”(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;
}
我没有使用 Java 的经验,所以可能会有一些拼写错误。请将该代码视为伪代码。
我会用一个简单的开关。为此,您需要一个数字评估。但是,对于这种情况,由于 0 <= one < 4 <= 9
和 0 <= two < 4 <= 9
,我们可以通过将 one
乘以 10 并添加 two
将两个 int 转换为简单的 int。然后在结果数字中使用一个开关,如下所示:
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;
}
还有另一种简短的方法,我只想指出作为理论代码。但是我不会使用它,因为它有一些您通常不想处理的额外复杂性。额外的复杂性来自以 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];
}
真的只是附加说明,以防我遗漏了 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];
}
由于您更喜欢嵌套的 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;
}
else
链,但没有任何区别。
else if
语句,我们可以使用 switch
或查找表来加速代码。
one==0
它将运行代码,那么它必须检查 if one==1
然后 if one==2
最后 if one==3
- 如果那里有 else if,它不会执行最后三个检查,因为它将在第一场比赛后退出该语句。是的,您可以通过使用 switch 语句代替 if (one...
语句来进一步优化,然后在 one's
的情况下进一步使用另一个 switch。然而,这不是我的问题。
用开关外壳试试...
switch (expression)
{
case constant:
statements;
break;
[ case constant-2:
statements;
break; ] ...
[ default:
statements;
break; ] ...
}
您可以向其中添加多个条件(不是同时),甚至可以在不满足其他情况的情况下使用默认选项。
PS:只有满足一个条件..
如果同时出现2个条件..我认为不能使用switch。但是你可以在这里减少你的代码。
Java switch statement multiple cases
我首先想到的是与弗朗西斯科·普雷森西亚(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;
此方法的优点是与其他一些建议方法相比,更容易查看 one
和 two
的哪些值对应于哪些返回值。
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2
; --unicorns
您可以使用 switch case 而不是多个 if
还要提一下,既然你有两个变量,那么你必须合并这两个变量才能在 switch 中使用它们
检查此Java switch statement to handle two variables?
当我在 1/2 和结果之间绘制表格时,我看到了一种模式,
if(one<2 && two <2) result=0; return;
以上将减少至少 3 个 if 语句。我没有看到一个固定的模式,也无法从给定的代码中收集到太多信息——但如果可以推导出这样的逻辑,它将减少一些 if 语句。
希望这可以帮助。
一个好点是将规则定义为文本,这样您就可以更容易地推导出正确的公式。这是从 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 命中进行一些解释会很棒,看起来很有趣!
最短且仍然可读的解决方案:
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;
}
甚至更短:
static public int fightMath(int one, int two)
{
if (one / 2 == two / 2) return (one / 2) * 3;
return 1 + (one + two + one / 2) % 2;
}
不包含任何“神奇”数字;)希望它有所帮助。
我个人喜欢级联三元运算符:
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);
}
这是一个相当简洁的版本,类似于 JAB's response。这利用地图来存储哪些移动胜利超过了其他。
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
static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();
,它应该更有效。
我会使用 Map,HashMap 或 TreeMap
特别是如果参数不在 0 <= X < N
形式上
就像一组随机正整数..
代码
public class MyMap
{
private TreeMap<String,Integer> map;
public MyMap ()
{
map = new TreeMap<String,Integer> ();
}
public void put (int key1, int key2, Integer value)
{
String key = (key1+":"+key2);
map.put(key, new Integer(value));
}
public Integer get (int key1, int key2)
{
String key = (key1+":"+key2);
return map.get(key);
}
}
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
感谢@Joe Harper,因为我最终使用了他的答案的变体。为了进一步缩小它,因为每 4 个结果中的 2 个结果相同,我进一步缩小了它。
我可能会在某个时候回到这一点,但如果多个 if
语句没有造成重大阻力,那么我将暂时保留这一点。我将进一步研究表格矩阵和 switch 语句解决方案。
public int fightMath(int one, int two) {
if (one === 0) {
if (two === 2) { return 1; }
else if(two === 3) { return 2; }
else { return 0; }
} else if (one === 1) {
if (two === 2) { return 2; }
else if (two === 3) { return 1; }
else { return 0; }
} else if (one === 2) {
if (two === 0) { return 2; }
else if (two === 1) { return 1; }
else { return 3; }
} else if (one === 3) {
if (two === 0) { return 1; }
else if (two === 1) { return 2; }
else { return 3; }
}
}
使用常量或枚举使代码更具可读性 尝试将代码拆分为更多函数 尝试使用问题的对称性
这是一个建议,但在这里使用 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 并不擅长表达这些数据类型。
而是做这样的事情
public int fightMath(int one, int two) {
return Calculate(one,two)
}
private int Calculate(int one,int two){
if (one==0){
if(two==0){
//return value}
}else if (one==1){
// return value as per condtiion
}
}
不定期副业成功案例分享
IndexOutOfBoundsException
。i
/j
/k
约定除外)、命名常量、以可读方式排列我的代码等,但是当变量和函数名称开始占用超过 20 个字符,我发现它实际上会导致代码的可读性降低。我通常的方法是尝试使用易于理解但简洁的代码,并在此处和那里添加注释来解释为什么代码的结构方式(与如何)。将原因放在名称中只会使一切变得混乱。