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


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

суббота, 24 мая 2014 г.

Обходим массивы

Недавно случилась задача. В ходе реализации алгоритма игры 2048.

Был двухмерный массив - поле. И (допустим) один полезный метод, с какой-о сложной логикой модификации содержимого поля.
public class Numbers {

    public static final int NONE = 0;
    private final int size;
    private int[][] data;

    public Numbers(int size) {
        this.size = size;
        data = new int[size][size];
    }

    private void merge() {
        for (int y = 0; y < size; y++) {
            for (int x = 0; x <= size - 1; x++) {
                if (data[x][y] == NONE) continue;

                for (int x2 = x - 1; x2 > -1; x2--) {
                    if (data[x2][y] == NONE) {
                        data[x2][y] = data[x2 + 1][y];
                        data[x2 + 1][y] = NONE;
                    } else if (data[x2][y] == data[x2 + 1][y]) {
                        int val = 2 * data[x2 + 1][y];
                        data[x2][y] = val;
                        data[x2 + 1][y] = NONE;
                        break;
                    } else {
                        break;
                    }
                }
            }
        }
    }
}
Реализован был случай прохода сверху вниз, справа налево. Так случилось, что надо еще 3 версии подобного метода, в котором перебор будет еще:
- сверху вниз, слева направо
- слева направо, снизу вверх
- слева направо, сверху вниз
Но писать еще три метода методом копипаста как-то не хотелось. Так же была попытка написать мострообразный метод, в котором циклы тюнятся через спец константы - свой набор констант для своего кейса. Это было громоздко и не читабельно.

Решение приснилось :) Им и поделююсь. Суть вкратце в том, что для начала мы вводим интерфейс доступа к массиву вместо непосредственного его использования
public class Numbers {

    public static final int NONE = 0;
    private final int size;
    private int[][] data;

    public Numbers(int size) {
        this.size = size;
        data = new int[size][size];
    }

    private void merge(Mirror data) {
        for (int y = 0; y < size; y++) {
            for (int x = 0; x <= size - 1; x++) {
                if (data.get(x, y) == NONE) continue;

                for (int x2 = x - 1; x2 > -1; x2--) {
                    if (data.get(x2, y) == NONE) {
                        data.set(x2, y, data.get(x2 + 1, y));
                        data.set(x2 + 1, y, NONE);
                    } else if (data.get(x2, y) == data.get(x2 + 1, y)) {
                        int val = 2 * data.get(x2 + 1, y);
                        data.set(x2, y, val);
                        data.set(x2 + 1, y, NONE);
                        break;
                    } else {
                        break;
                    }
                }
            }
        }
    }

    interface Mirror {
        int get(int x, int y);
        void set(int x, int y, int val);
    }
После этого используем разные реализации этого фильтра, один нормальный:
class XY implements Mirror {
    @Override
    public int get(int x, int y) {
        return data[x][y];
    }

    @Override
    public void set(int x, int y, int val) {
        data[x][y] = val;
    }
}
и три зеркальных
class _XY implements Mirror {
    @Override
    public int get(int x, int y) {
        return data[size - 1 - x][y];
    }

    @Override
    public void set(int x, int y, int val) {
        data[size - 1 - x][y] = val;
    }
}

class Y_X implements Mirror {
    @Override
    public int get(int x, int y) {
        return data[y][size - 1 - x];
    }

    @Override
    public void set(int x, int y, int val) {
        data[y][size - 1 - x] = val;
    }
}

class YX implements Mirror {
    @Override
    public int get(int x, int y) {
        return data[y][x];
    }

    @Override
    public void set(int x, int y, int val) {
        data[y][x] = val;
    }
}
А метод merge дергаем как-то так
merge(this.new _XY());
merge(this.new Y_X());
merge(this.new XY());
merge(this.new YX());
Вот и вся магия...

2 комментария: