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

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

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

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

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. Выражения (один


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

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

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 будет вероятно, будет немного медленнее, чем прямые проверки equals.


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

Я не знаю, если bitshift на 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;
    }
}

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


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

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


надеюсь, я правильно понимаю логику. Как насчет чего-то вроде:

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 conditionals, вот еще один способ.
Обратите внимание, что он не использует 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

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

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

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


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

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

некоторые объяснение на сложные Р1/Р2 просмотров было бы здорово, выглядит интересно!


самое короткое и все еще читаемое решение:

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
    }

    }