Как посчитать соседей клетки в клеточном автомате с обтеканием

Итак, я делаю программу, которая имитирует клеточные автоматы, похожие на живые, но у меня возникли проблемы с методом, используемым для подсчета живых соседей клетки. Проблема в том, что я хочу иметь возможность изменять способ обтекания сетки, т. е. оборачиваться ли она слева направо (т. е. цилиндрически), сверху вниз< /strong> и слева направо (т. е. тороидальный) или вовсе нет (т. е. плоский) — и я не могу понять, как сделать мой метод учитывать это. Вот что у меня есть до сих пор:

public int getLiveNeighbors(int row, int col)
{
    int count = 0;

    // "topology" is an int that represents wraparound:
    // 0 = flat; 1 = cylindrical; 2 = toroidal
    int top = topology != 2 ? row - 1 : (row + ROWS - 1) % ROWS;
    int bottom = topology != 2 ? row + 1 : (row + 1) % ROWS;
    int left = topology != 0 ? (col + COLS - 1) % COLS : col - 1;
    int right = topology != 0 ? (col + 1) % COLS : col + 1;

    for (int r = top; r < bottom + 1; r++)
        for (int c = left; c < right + 1; c++)
            if (!(r == row && c == col) && getCell(r, c).equals(LIVE))
                count++;
}

Я думаю, что ключевым является оператор if в цикле for — должен быть какой-то способ проверить, находятся ли r и c в пределах сетки, при этом помня, что определение «границы» будет варьируются в зависимости от того, закручивается ли сетка. В прошлом я обходил это, имея три разных набора (по одному для каждой настройки циклического перехода) из восьми разных if-операторов для индивидуальной проверки каждой из восьми ячеек, составляющих окрестности исходной ячейки; как вы понимаете, это было не очень красиво, но, по крайней мере, работало.

Я не так хорош в объяснении собственного кода, поэтому надеюсь, что это не слишком запутало — я сам чувствую себя немного запутанным (ха). Если у кого-то есть какие-либо вопросы, не стесняйтесь спрашивать!


person Jeffery Huang    schedule 25.08.2017    source источник


Ответы (3)


Вероятно, у вас уже есть класс типа Board с методом типа getCell(x, y) (по крайней мере такой метод присутствует в вашем коде).

Я бы просто сделал этот метод мягким в том смысле, что он будет принимать отрицательные x и y или x и y больше или равные COLS и ROWS. Таким образом, вы можете просто перебирать col - 1 до col + 1 и row - 1 до row + 1 (минус col и row) и не заботиться о том, что эти координаты выходят за рамки. Задача Board состоит в правильном поиске координат.

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

Вы можете сделать это проще, реализуя различные подклассы Board, такие как CylindricalBoard, ToroidalBoard и FlatBoard. Каждый из подклассов будет реализовывать getCell по-разному, но в контексте подкласса это будет понятно.

person lexicore    schedule 25.08.2017

Вы ищете Шаблон стратегии:

Нередки ситуации, когда классы отличаются только своим поведением. В этом случае рекомендуется изолировать алгоритмы в отдельные классы, чтобы иметь возможность выбирать разные алгоритмы во время выполнения.

В этом случае вам нужно что-то вроде этого (сокращенно для ясности):

class Point {
    int x;
    int y;
}
interface WrapStrategy {
    Point moveUp(Point p);
    Point moveDown(Point p);
    Point moveLeft(Point p);
    Point moveRight(Point p);
}
class CylinderWrapping implements WrapStrategy {
    int height;
    int circumference;
    Point moveUp(Point p) {
        if (p.y <= 0)
            return null; // cannot move up
        return new Point(p.x, p.y - 1);
    }
    Point moveDown(Point p) {
        if (p.y >= height - 1)
            return null; // cannot move down
        return new Point(p.x, p.y + 1);
    }
    Point moveLeft(Point p) {
        if (p.x <= 0)
            return new Point(circumference - 1, p.y);
        return new Point(p.x - 1, p.y);
    }
    Point moveRight(Point p) {
        if (p.x >= circumference - 1)
            return new Point(0, p.y);
        return new Point(p.x + 1, p.y);
    }
}
person Andreas    schedule 25.08.2017

Попробуй это:

import java.awt.Point;

public class Neighbours {

    public static void main(String[] args) {
        Neighbours inst=new Neighbours();
        int r=3;//<ROWS
        int c=3;//<COLS
        for(int i :new int[]{0,1,2}){
            inst.type=i;
            System.out.format("There are %d neighbours of point (%d,%d), topography type %d\n", inst.countLiveNeighbours(r, c), c, r,i);
        }
    }

    int ROWS=4;
    int COLS=4;
    int type=0;//0=flat, 1=cylinder, 2=toroid

    /**
     * Is x,y a neighbour of r,c?
     * @return coordinates of neighbour or null
     */
    Point neighbour(int x, int y, int r, int c){
        if((x==c)&&(y==r))
            return null;
        switch (type){
/*this is wrong for the reasons explained below
        case 0: return ((x<COLS)&&(y<ROWS)) ? new Point (x,y) : null;
        case 1: return y<ROWS ? new Point(x%COLS,y) : null;
        case 2: return new Point(x%COLS,y%ROWS);
*/
 //replacement statements produce the correct behaviour
    case 0: return ((x<COLS)&&(x>-1)&&(y<ROWS)&&(y>-1)) ? new Point (x,y) : null;
    case 1: return ((y<ROWS)&&(y>-1)) ? new Point(Math.floorMod(x,COLS),y) : null;
    case 2: return new Point(Math.floorMod(x,COLS),Math.floorMod(y,ROWS));
        }
        return null;
    }

    int countLiveNeighbours(int r, int c){
        int result=0;
        for(int x=c-1; x<c+2; x++)
            for(int y=r-1; y<r+2; y++){
                Point p=neighbour(x,y,r,c);
                if(live(p)){
                    System.out.format("\tpoint (%d,%d)\n",(int)p.getX(),(int)p.getY());
                    result++;
                }
            }
        return result;
    }

    boolean live(Point p){
        boolean result=true;
        if(p==null)
            return false;
        //perform tests for liveness here and set result
        return result;
    }
}
person stegzzz    schedule 26.08.2017
comment
Хорошо, здесь я поторопился из-за случая отрицательных индексов для x и y (сетка начинается с 0,0). Кроме того, оказывается, что оператор % в Java возвращает остаток, а не правильный модуль. Это имеет значение с отрицательными числами (например, -1%4=-1, а не 3). Math.floorMod обеспечивает правильное поведение. См. описание случая замены выше. - person stegzzz; 26.08.2017