次のコードは、私がそれを必要とする方法で動作しますが、,い、過剰または他の多くのものです。数式を見て、いくつかの解決策を記述しようとしましたが、同じような量のステートメントになります。
この場合に私にとって有益な数式の種類はありますか、またはステートメントが受け入れられる場合は16ですか?
コードを説明すると、一種の同時ターンベースのゲームです。2人のプレイヤーはそれぞれ4つのアクションボタンを持ち、結果は配列(0〜3)から取得されますが、変数「one」と「two」はこれが役立つ場合は何でも割り当てました。結果は、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;
}
数式が思いつかない場合は、限られた数の結果に対してテーブルを使用できます。
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つの数字(1、2)、ドメイン{0,1,2,3}から範囲{0,1,2,3}へのマップです。それぞれの答えは、そのマップを実装する方法に近づいています。
また、1つの数字が1、2が数字の2、N = 4 * 1の2桁のベース4の数字N(one、two)のマップとして問題の修正を多くの回答で見ることができます。 + 2; N = {0,1,2、...、15}-16の異なる値、これは重要です。関数の出力は、1つの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を取ることができます。式(1 << 3)および(two << 1)は、2 * x = x << 1および8 * x = x << 3。
JAB以外の提示されたソリューションはどれも好きではありません。 他のどれもコードを読みやすくし、計算されているものを理解することを容易にしません。
このコードの記述方法は次のとおりです。JavaではなくC#のみを知っていますが、画像はわかります。
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);
}
ここで何が計算されているかがより明確になりました。これは、誰がどの攻撃に見舞われ、両方の結果を返すかを計算していることを強調しています。
ただし、これはさらに改善される可能性があります。そのブール配列はやや不透明です。私はテーブルルックアップアプローチが好きですが、意図したゲームセマンティクスが何であるかを明確にするような方法で記述したいと思います。つまり、「ゼロの攻撃と1つの防御でヒットしない」よりも、コードをより明確に「ローキック攻撃と低いブロック防御でヒットしない」ことを暗示する方法を見つけます。 ゲームのビジネスロジックをコードに反映させます。
結果を含むマトリックスを作成できます
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ステートメントを統合することに加えて、提供された引数が期待される範囲にあることを確認し、インプレースリターン(一部のコーディング私が見た標準は、関数に対して1つの出口を強制しますが、複数のリターンは、矢印コーディングを回避し、Javaで例外がof延するのに非常に役立つことがわかりました。とにかくメソッド内でスローされたキャッチされていない例外は、とにかく終了の可能性があるポイントなので、そのようなルールを厳密に強制します。 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マッピングの一部の不規則性に起因する可能性がある場合よりも読みにくくなります。そのシンプルさと、視覚的に意味を成すようにマトリックスを設定する方法のために、代わりにマトリックススタイルを好みます(ただし、一部はカルノーマップの私の記憶の影響を受けます)。
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
sを使用すると、おそらく直接同等チェックよりもわずかに遅くなりますが、move.type == MoveType.ATTACK
ではなくEnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
に続いてattacks.contains(move)
になります。
ブロックが成功した場合にカウンターになる場合、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につながる可能性があるため、より複雑な分岐にはお勧めしません。
配列を使用しないのはなぜですか?
最初から始めます。パターンが表示され、値は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を組み合わせて1つにします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
これを10進数値に戻すと、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のビットシフトが乗算より速いかどうかはわかりません。しかし、試してみる価値はあります。
これは、少しのビットマジックを使用します(単一の整数に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;
}
}
または、2ビットの情報を別々の変数に分けることを提案すべきですか?上記のようなビット操作に主に基づくコードは、通常、維持するのが非常に困難です。
正直に言うと、誰もが独自のスタイルのコードを持っています。パフォーマンスがあまり影響を受けるとは思いませんでした。スイッチケースバージョンを使用するよりもこのことをよく理解している場合は、これを引き続き使用してください。
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; }
}
必要に応じてフォーマットし直してください。
これはコードの見栄えを良くするものではありませんが、潜在的には少しスピードアップすると思います。
私たちが知っていることを見てみましょう
1:P1(プレーヤー1)とP2(プレーヤー2)の答えは対称的です。これは格闘ゲームには理にかなっていますが、ロジックを改善するために利用できるものでもあります。
2:3ビート0ビート2ビート1ビート3.これらのケースでカバーされない唯一のケースは、0対1および2対3の組み合わせです。 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;
}
1ヒットの高または1ヒットの低をチェックすることはブロックされず、プレイヤー2でも同じです。
編集:アルゴリズムは完全に理解されておらず、ブロックされたときに「ヒット」が与えられましたが、私は気づいていませんでした(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;
}
理論的なコードとして指摘したい別の短い方法があります。ただし、通常は扱いたくない余分な複雑さがあるため、使用しません。カウントは0、1、2、3、10、11、12、13、20、...であるため、余分な複雑さはbase 4に由来します。
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;
}
スイッチケーシングで試してみてください。..
switch (expression)
{
case constant:
statements;
break;
[ case constant-2:
statements;
break; ] ...
[ default:
statements;
break; ] ...
}
複数の条件を追加することができます(同時にではなく)、さらにデフォルトオプションがあります他のケースが満たされていない場合。
PS:1つの条件が満たされる場合のみ..
2つの条件が同時に発生した場合..スイッチを使用できるとは思わない。ただし、ここでコードを減らすことができます。
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2
私が最初に思いついたのは、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
のどの値がどの戻り値に対応するかを、他の推奨されるメソッドよりも簡単に確認できることです。
複数のif
の代わりに switch case を使用できます
また、2つの変数があるため、2つの変数をマージしてスイッチで使用する必要があることにも言及してください
これを確認してください 2つの変数を処理するJava switchステートメント?
良い点は、ルールをテキストとして定義することです。そうすれば、正しい式を簡単に導き出すことができます。これは、laaltoのNice配列表現から抽出されます。
{ 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;
}
「魔法の」数字は含まれていません;)それが役に立てば幸いです。
1/2と結果の間に表を描くと、1つのパターンが見えます。
if(one<2 && two <2) result=0; return;
上記は、少なくとも3つのifステートメントを削減します。設定されたパターンが表示されず、指定されたコードから多くを収集することもできませんが、そのようなロジックを導出できれば、多数のifステートメントが削減されます。
お役に立てれば。
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
- statementsによる大きな抵抗がない場合は、今のところこれを保持します。テーブルマトリックスを調べ、ステートメントソリューションをさらに切り替えます。
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; }
}
}
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);
}
}
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
私は個人的に三項演算子をカスケードするのが好きです:
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);
}
入力と出力に構造化タイプを使用する方が良いでしょう。入力には実際には2つのフィールドがあります。位置とタイプ(ブロックまたは攻撃)です。出力には、player1Winsとplayer2Winsの2つのフィールドもあります。これを単一の整数にエンコードすると、コードが読みにくくなります。
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はこれらの種類のデータ型の表現があまり得意ではありません。