他の人々はすでに私の最初のアイデアであるマトリックス法を提案していますが、ifステートメントを統合することに加えて、提供された引数が期待される範囲内にあることを確認し、インプレースリターン(一部のコーディング私が見た標準は関数に1つの出口を強制しますが、複数の戻り値は矢印コーディングを回避するのに非常に役立ち、Javaで例外が蔓延しているため、とにかくそのようなルールを厳密に適用することにはあまり意味がないことがわかりましたメソッド内でスローされたキャッチされない例外は、とにかく出口の可能性があるためです)。switchステートメントをネストすることは可能ですが、ここで確認している値の範囲が狭い場合は、ステートメントがよりコンパクトで、パフォーマンスに大きな違いが出ない可能性があるかどうかを確認します。
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
}
これは、input-> resultマッピングの一部の不規則性が原因である可能性があるため、最終的には読みにくくなります。私は代わりに、マトリックススタイルを好んでいます。その単純さと、マトリックスを視覚的に設定して視覚的に理解できるようにする方法です(ただし、これはKarnaughマップの私の記憶の影響を受けています)。
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
Sは、おそらく直接対等チェックよりもわずかに遅くなります。
カウンターで成功したブロックの結果の場合には、あなたは置き換えることができ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;
}
また、一部のif
ステートメントを三項演算子(boolean_expression ? result_if_true : result_if_false
)を使用して置き換えると、コードがよりコンパクトになります(たとえば、前のブロックのコードはreturn one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
)が、読みにくいonelinersにつながる可能性があるため、より複雑な分岐にはこれをお勧めします。