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

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

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

Ответ 1

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

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

Ответ 2

Поскольку ваш набор данных настолько мал, вы можете сжать все в 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;
}

Происхождение Магической константы

Что я могу сказать? Мир нуждается в магии, иногда возможность чего-то требует его создания.

Сущность функции, решающей проблему ОП, - это карта из двух чисел (одна, две), области {0,1,2,3} в диапазон {0,1,2,3}. Каждый из ответов приблизился к тому, как реализовать эту карту.

Кроме того, в ряде ответов вы можете увидеть повторение проблемы как карту 1 двузначной базы 4 числа N (один, два), где одна цифра 1, две цифры 2, а N = 4 * один + два; N = {0,1,2,..., 15} - шестнадцать различных значений, что важно. Выходной сигнал функции - это 1-значный базовый номер 4 {0,1,2,3} - 4 разных значения, также важный.

Теперь однозначное базовое число 4 может быть выражено в виде 2-значного базового номера 2; {0,1,2,3} = {00,01,10,11}, и поэтому каждый выход может быть закодирован только с двумя битами. Сверху доступно только 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. Выражения (один < 3) и (два < 1) просто умножают данные, отмечая, что 2 * x = x < 1 и 8 * x = x < 3.

Ответ 3

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

Вот как бы я написал этот код - я знаю только С#, а не 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);
}    

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

Однако это может быть даже лучше; что Boolean массив несколько непрозрачен. Мне нравится подход к поиску в таблице, но я был бы склонен писать его таким образом, чтобы он дал понять, что подразумевалось в семантике игры. То есть, вместо того, чтобы "атака нуля и защита одного результата не попала в цель", вместо этого найдите способ сделать код более четко подразумевающим "атаку с низким ударом, а защита с низким блоком - без удара". Сделайте код, отражающий бизнес-логику игры.

Ответ 4

Вы можете создать матрицу, содержащую результаты

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

Ответ 5

Другие люди уже предложили мою первоначальную идею - матричный метод, но помимо консолидации операторов if вы можете избежать некоторых из того, что у вас есть, убедившись, что предоставленные аргументы находятся в ожидаемом диапазоне и с помощью возвратов на месте (некоторые стандарты кодирования, которые я видел, применяют функции с одной точкой выхода для функций, но я обнаружил, что множественные возвраты очень полезны для избежания кодирования стрелок, а с преобладанием исключений в Java там не так много смысла в обеспечении строгого соблюдения таких правил правило в любом случае, поскольку любое неперехваченное исключение, брошенное внутри метода, в любом случае является возможной точкой выхода). Операторы переключения вложенности - это возможность, но для небольшого диапазона значений, которые вы проверяете здесь, я нахожу, если утверждения более компактны и вряд ли могут привести к большой разнице в производительности, особенно если ваша программа работает пошаговой, а не реальной -time.

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

Кроме того, замена некоторых операторов if на использование тернарного оператора (boolean_expression ? result_if_true : result_if_false) может сделать код более компактным (например, код в предыдущем блоке станет return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;), но это может приводят к более трудно читаемым oneliners, поэтому я бы не рекомендовал его для более сложного разветвления.

Ответ 6

Почему бы не использовать массив?

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

Ответ 7

Это использует немного битмагический (вы уже делаете это, держа два бита информации (низкий/высокий и атаковать/блокировать) в одном целом):

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

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

Ответ 8

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

Вы можете вложить 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; }
}

И просто переформатируйте его так, как вы бы предпочли.

Это не делает код лучше, но, по-моему, он немного ускоряет его.

Ответ 9

Посмотрим, что мы знаем

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

Ответ 10

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

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

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

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

Ответ 11

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

Я бы пошел с простым переключателем. Для этого вам потребуется отдельная оценка номера. Однако для этого случая, поскольку 0 <= one < 4 <= 9 и 0 <= two < 4 <= 9, мы можем преобразовать оба int в простой 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];
}

Ответ 12

Поскольку вы предпочитаете вложенные условные условия 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;
}

Ответ 13

Попробуйте использовать корпус коммутатора...

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

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

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

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

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

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

Ответ 14

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

Ответ 15

((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2

Ответ 17

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

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

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

Надеюсь, что это поможет.

Ответ 18

Хорошим моментом было бы определить правила как текст, тогда вы можете легче получить правильную формулу. Это извлекается из представления laalto nice array:

{ 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 было бы замечательным, выглядит интересно!

Ответ 19

Самое короткое и доступное для чтения решение:

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

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

Ответ 20

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

Ответ 21

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

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

Ответ 22

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

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

Ответ 23

Здесь довольно сжатая версия, похожая на 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

Ответ 24

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

Ответ 25

  • Используйте константы или перечисления, чтобы сделать код более удобочитаемым
  • Попробуйте разделить код на несколько функций.
  • Попробуйте использовать симметрию проблемы

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

Ответ 26

Вместо этого сделайте что-нибудь вроде этого

   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
    }

    }