Слишком много утверждений "если"?


следующий код работает так, как мне нужно, но это уродливо, чрезмерно или ряд других вещей. Я посмотрел на формулы и попытался написать несколько решений, но в итоге я получил аналогичное количество утверждений.

есть ли тип математической формулы, которая принесла бы мне пользу в этом случае или 16, если утверждения приемлемы?

чтобы объяснить код, это для своего рода одновременной пошаговой игры.. два игрока имеют четыре кнопки действий каждый и результаты исходят из массива (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;
}
26 257

26 ответов:

Если вы не можете придумать формулу, вы можете использовать таблицу для такого ограниченного числа результатов:

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(один, два), где один-цифра 1, два-цифра 2 и N = 4*один + два; 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. Выражения (один

мне не нравится ни одно из решений представлено, кроме удар. ни один из других не позволяет легко читать код и понять, что вычисляется.

вот как я бы написал этот код, я знаю только 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-это возможность, но для небольшого диапазона значений, которые вы проверяете здесь, я нахожу, что операторы будут более компактными и вряд ли приведут к значительной разнице в производительности, особенно если ваша программа пошаговая, а не в режиме реального времени.

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

Update: учитывая ваше упоминание о блокировке / ударе, вот более радикальное изменение функции, которая использует пропертированные / атрибутивные перечислимые типы для входных данных и результата, а также немного изменяет результат для учета блокировка, которая должна привести к более читаемой функции.

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

вам даже не нужно менять саму функцию, если вы хотите добавить блоки/атаки большей высоты, просто перечисления; добавление дополнительных типов ходов, вероятно, потребует модификации функции. Кроме того,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;
}

кроме того, заменив некоторые из 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

теперь, возможно, вы уже видите какой-то шаблон, но когда я объединяю значение один и два, я вижу, что вы используете все значения 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

когда мы переводим это обратно в десятичные значения, мы видим очень возможный массив значений, где один и два вместе могут быть использованы в качестве индекса:

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}, где это индекс просто один и два в сочетании.

Я не программист 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 быстрее, чем умножение. Но попробовать стоит.

Это использует немного bitmagic (вы уже делаете это, удерживая два бита информации (low / high & attack / block) В одном целом числе):

Я не запускал его, только набрал его здесь, пожалуйста, дважды проверьте. идея, безусловно, работает. EDIT: сейчас тестируется для каждого входа, отлично работает.

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

или я должен предложить разделить два бита информации на отдельные переменные? Код, основанный в основном на битовых операциях, как это выше обычно очень трудно поддерживать.

честно говоря, у каждого свой стиль кода. Я бы не подумал, что производительность будет затронута слишком сильно. Если вы понимаете это лучше, чем использование версии 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;
}

проверка одного хита высокого или одного хита низкого не блокируется и то же самое для игрока два.

Edit: алгоритм не был полностью понят," хит " присуждается при блокировке, которую я не понял (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, мы можем преобразовать оба ints в простой int путем умножения one 10 и добавить 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;
}

есть еще один короткий метод, который я просто хочу указать как теоретический код. Однако я бы не использовал его, потому что он имеет некоторую дополнительную сложность, с которой вы обычно не хотите иметь дело. Дополнительная сложность исходит от база 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;
}

попробуйте переключатель корпус...

посмотреть здесь или здесь подробнее о

switch (expression)
{ 
  case constant:
        statements;
        break;
  [ case constant-2:
        statements;
        break;  ] ...
  [ default:
        statements;
        break;  ] ...
}

вы можете добавить несколько условий(не одновременно) и даже есть опция по умолчанию где никакие другие случаи не были удовлетворены.

PS: только если одно условие должно быть выполнено..

если одновременно возникают 2 условия.. Я не думаю, что переключатель можете использовать. Но вы можете уменьшить свой код здесь.

оператор Java switch несколько случаев

первое, что пришло мне в голову, был по существу тот же ответ, данный Франсиско 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

можно использовать переключатель case вместо mutiple if

также отметить, что поскольку у вас есть две переменные, то вы должны объединить две переменные, чтобы использовать их в switch

проверить это оператор Java switch для обработки двух переменных?

когда я рисую таблицу между одним / двумя и результатом, я вижу один шаблон,

if(one<2 && two <2) result=0; return;

выше было бы сократить по крайней мере 3, Если заявления. Я не вижу заданного шаблона, и я не могу многое почерпнуть из данного кода, но если такая логика может быть получена, она сократит количество операторов 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;
}

не содержит никаких "магических" чисел ;) Надеюсь, это поможет.

static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }

мне лично нравится каскадировать тернарные операторы:

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

вот довольно краткий вариант, похожий на удар!--5-->. Это использует карту для хранения, которая движется триумф над другими.

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

Я бы использовал карту, либо 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);
    }
}

спасибо @Joe Harper, поскольку я в конечном итоге использовал вариацию его ответа. Чтобы уменьшить его дальше, поскольку 2 результата на 4 были одинаковыми, я уменьшил его дальше.

Я могу вернуться к этому в какой-то момент, но если нет серьезного сопротивления, вызванного несколькими if-заявления тогда я оставлю это пока. Я буду смотреть в матрицу таблицы и переключать решения операторов дальше.

public int fightMath(int one, int two) {

    if       (one == 0) {

        if       (two == 2) {
            result = 1;
        } else if(two == 3) {
            result = 2;
        } else {
            result = 0; }

    } else if(one == 1) {
        if       (two == 2) {
            result = 2;
        } else if(two == 3) {
            result = 1;
        } else {
            result = 0; }

    } else if(one == 2) {

        if       (two == 0) {
            result = 2;
        } else if(two == 1) {
            result = 1;
        } else {
            result = 3; }

    } else if(one == 3) {

        if       (two == 0) {
            result = 1;
        } else if(two == 1) {
            result = 2;
        } else {
            result = 3; }
    }

    return result;
}
  1. использовать константы или перечисления, чтобы сделать код более читабельным
  2. попробуйте разделить код на несколько функций
  3. попробуйте использовать симметрию задачи

вот предложение, как это может выглядеть, но использование ints здесь все еще выглядит уродливо:

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
    }

    }