Почему мой блок if-else никогда не попадает под удар, хотя так и должно быть? (Просто нужна еще одна пара глаз.)

#java #if-statement #simulation

#java #if-statement #Симуляция

Вопрос:

Я создаю игру в стиле Falling Sand на Java, и у меня возникают странные проблемы с блоком if-else, который у меня есть. В моем методе doGravity () у меня есть различные блоки условий, которые будут вызывать разные вещи, и по какой-то странной причине один блок НИКОГДА не попадает.

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

 else if(world[x][y 1]==EMPTY amp;amp; (x-1 >= 0) amp;amp; world[x-1][y 1] == EMPTY amp;amp; (x 1 < world.length) amp;amp; world[x 1][y 1]==EMPTY) {
    int r = rand.nextInt(50);
    if(r == 0) {
        world[x-1][y 1] = world[x][y];
        //System.out.println("GO: right");
        countRight  ;
    }
    else if(r == 1) {
        world[x 1][y 1] = world[x][y];
        //System.out.println("GO: left");
        countLeft  ;
    }
    else {
        world[x][y 1] = world[x][y];
        countCenter  ;
    }
    world[x][y] = EMPTY;
}
  

Далее следует это условие, которое также равномерно распределяет влево и вправо.

 else if((x-1 >= 0) amp;amp; world[x-1][y 1] == EMPTY amp;amp; (x 1 < world.length) amp;amp; world[x 1][y 1]==EMPTY) {
    if(rand.nextBoolean()) {
        world[x-1][y 1] = world[x][y];
        //countLeft  ;
    }
    else {
        world[x 1][y 1] = world[x][y];
        //countRight  ;
    }
    world[x][y] = EMPTY;
}
  

Но когда я считаю эти блоки, левый блок НИКОГДА не попадает, даже когда пространство слева открыто. Я чувствую, что это, вероятно, просто какая-то глупая опечатка, которую я по какой-то причине не вижу.

 else if((x-1 >= 0) amp;amp; world[x-1][y 1] == EMPTY) {
    world[x-1][y 1] = world[x][y];
    world[x][y] = EMPTY;
    countLeft  ;
    System.out.println("Hit Left");
}
else if((x 1 < world.length) amp;amp; world[x 1][y 1] == EMPTY) {
    world[x 1][y 1] = world[x][y];
    world[x][y] = EMPTY;
    countRight  ;
    System.out.println("Hit Right");
}
  

ОБНОВЛЕНИЕ: Если я отмечу left блок в конце, абсолютно ничего не изменится. Песок действует точно так же. Если я отмечу right блок в конце, он действует так же, как если бы я отметил оба блока. Я не могу этого понять. Это должно сработать … но этого не происходит.

ОБНОВЛЕНИЕ: Вот полный исходный код. Я понятия не имею, что бы это могло быть. На самом деле, это сведет меня с ума. http://pastebin.com/mXCbCvmb

Комментарии:

1. Получаете ли вы правильные эффекты от блока Countrright ? В частности, имеет ли x разумные значения? Причиной может быть случайное присвоение 0 значению x.

2. Да, в этом аспекте все работает правильно. Если я запущу апплет, вы даже сможете увидеть, как песок падает на пол, начинает скапливаться, затем течет вправо, в то время как левая сторона представляет собой прямую линию. Затем, когда я отмечаю последние два else if блока, песок строит башню прямо в воздухе.

3. Хорошо, моим последним предположением было бы попытаться удалить else перед левой проверкой if (первая строка последнего фрагмента), на случай, если у вас есть какие-то проблемы с вложенностью if / else.

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

5. Что произойдет, если вы закомментируете любое из условий x-1> = 0 и world[…=EMPTY, какое из которых ведет себя неправильно?

Ответ №1:

Ваш код pastebin действительно показывает «Нажать влево», вам просто нужно изменить создание world (строка 65 pastebin) на

     world = new Color[worldWidth][worldHeight 1];
  

Я бы предположил, что из-за части y 1. В остальном она растет как влево, так и вправо.

РЕДАКТИРОВАТЬ: http://pastebin.com/GVmSzN4z Я немного изменил вашу гравитацию, чтобы сделать капли немного более симметричными.

Комментарии:

1. Итак, получается, что левая сторона никогда не попадала под удар из-за того, как работает алгоритм гравитационной обработки. dropParticle () никогда не была проблемой, это просто тот факт, что doGravity () перемещалась слева направо и снизу вверх. Теперь ваша исправленная функция doGravity () работает ужасно медленно, учитывая все, что должно произойти в остальной части скрипта, но это правильная идея. Проблема решена!

2. @Nick, shuffle на самом деле довольно быстрый, просто переместите создание jS = new ArrayList . инициализируйте, и все будет в порядке. Также комментарии Джарродса очень полезны, когда они такие сложные, как этот.

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

Ответ №2:

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

Редактировать

После проверки вашего полного исходного кода я, наконец, нашел, в чем проблема. Ваша doGravity функция обновления всегда идет влево-> вправо, и это вносит асимметрию. Изменив его так, чтобы направление обновления чередовалось между left-> right и right-> left для четных / нечетных строк сканирования, асимметрия исчезает.

 private void doGravity() {
    for(int i = worldHeight - 1; i >= 0; i--) {
        if (i % 2 == 0)
        {
            for(int j = 0; j < worldWidth; j  ) {
                if(world[j][i] != EMPTY) {
                    if(hasGravity(world[j][i])) {
                        dropParticle(j, i);
                    }
                }
            }
        } 
        else
        {
            for(int j = worldWidth-1; j >= 0; --j) {
                if(world[j][i] != EMPTY) {
                    if(hasGravity(world[j][i])) {
                        dropParticle(j, i);
                    }
                }
            }
        }
    }
}
  

Комментарии:

1. Я отредактировал его, чтобы включить промежуточный блок. Этот блок также распределен равномерно. Блок только для левой части просто никогда не попадает. Никогда.

2. Я тоже не вижу ничего плохого… какое if условие вверху? Весь этот код выполняется только в том случае, если это условие равно false…

3. Что ж, весь этот код определенно выполняется. if Утверждение является if(y == worldHeight-1) . Это выполняется только в том случае, если частица находится в самом низу фрейма, что либо a.) удаляет ее, если этаж выключен, либо b.) ничего не делает, если этаж включен. Я могу запустить код, и счетчики работают должным образом в каждом блоке (у меня есть счетчики только в одном разделе за раз), за исключением последнего раздела. Left никогда не выполняется.

4. Тогда ваша проблема в чем-то другом. Этот код действительно увеличивает как левый, так и правый счетчики (я скопировал и вставил из вашего вопроса, написал все остальное, необходимое для превращения его в полноценно работающую программу, и протестировал его).

5. Вот код. pastebin.com/mXCbCvmb Однако я уверен, что этот метод вызывает проблемы.

Ответ №3:

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

 private boolean isEmpty(final int x, final int y)
{
    return world[x][y] == EMPTY;
}
  

Я бы извлек все EMPTY проверки во что-нибудь более читаемое, такое как isLeftEmpty(x,y) и isRightEmpty(x,y) и isNextLeftEmpty(x,y) это поможет вам рассуждать о правильности вашей логики в вашем коде.

Я бы также извлек (x 1 < world.length) to isNextXOutsideWorld(x) , это поможет документировать ваши намерения и поможет с рассуждениями о логике, которую вы намереваетесь использовать.

Это также имеет побочный эффект упрощения логики в if/elseif/else операторах.

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

 else if ((x   1 < world.length) amp;amp; isEmpty(x   1, y   1) amp;amp; 
         (x - 1 >= 0) amp;amp; isEmpty(x - 1,y   1))
  

всегда true, когда я его запускаю, поэтому он никогда не достигает следующего оператора

 else if ((x - 1 >= 0) amp;amp; isEmpty(x - 1,y   1))
  

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

Очень сложно экстраполировать предполагаемое поведение из всего шума в if/elseif/else блоках.