【问题标题】:Invalidate an element forming a path if it is surrounded如果被包围,则使形成路径的元素无效
【发布时间】:2017-05-21 03:54:53
【问题描述】:

我正在构建一个由单元格组成的 2D 网格游戏,玩家必须在其中放置标记并尝试包含(包围)对手的标记。现在每个单元格可以有 3 种状态:空、包含红色标记或包含蓝色标记。

所有可以形成“路径”的单元格都在一个列表中,沿着该路径我可以绘制经过单元格中心的线(多边形)。 还有一个包含令牌的列表,被包围的那个,

现在我想找到一种方法来“使”被包围的标记“无效”,以便路径计算可以忽略它

请看下面的例子:

  1. 蓝色标记首先被圈起来,它们不能脱离任何进一步的路径计算。

  1. 这是不允许的。先遏制,先赢。

以下所有代码均来自路径类:

class Path extends Stack<int[]>{

    private Token[][] grid;

    //a path shorter than min can not surround any cell
    private static final int MIN_PATH_LEGTH = 3;

    //a collection of cells that has been tested
    private ArrayList<int[]>checked;

    //represents the cell where the search starts from
    int[] origin;
    //represents the token of the origin
    Token originToken;

    private int rows;
    private int cols;

    //represents the path bounds: min/max row/col in path
    private int minPathRow, maxPathRow, minPathCol, maxPathCol;

    Path(Token[][] grid){

        this.grid = grid;
        rows = grid.length;
        cols = grid[0].length;
    }

    //search for a path
    boolean findPath(int[] origin) {

        this.origin = origin;

        int row = origin[0] , col =  origin[1];

        //represents the token of the origin
        originToken = grid[row][col];

        //initialize list of checked items
        checked = new  CellsList();

        boolean found = findPath(row, col);

        if(found) {
            printPath();
        } else {
            System.out.println("No path found");
        }

        return found;
    }

    //recursive method to find path. a cell is represented by its row, col
    //returns true when path was found
    private boolean findPath(int row, int col) {

        //check if cell has the same token as origin
        if(grid[row][col] != originToken) {
            return false;
        }

        int[] cell = new int[] {row, col};

        //check if this cell was tested before to avoid checking again
        if(checked.contains(cell)) {
            return false;
        }

        //get cells neighbors
        CellsList neighbors = getNeighbors(row, col);

        //check if solution found. If path size > min and cell
        //neighbors contain the origin it means that path was found
        if((size() >= MIN_PATH_LEGTH) && neighbors.contains(origin)  ) {

            add(cell);
            return true;
        }

        //add cell to checked
        checked.add(cell);

        //add cell to path
        add(cell);

        //if path was not found check cell neighbors
        for(int[] neighbor : neighbors ) {

            boolean found = findPath(neighbor[0],neighbor[1]);
            if(found) {
                return true;
            }
        }

        //path not found
        pop(); //remove last element from stack
        return false;
    }


    //use for testing
    private void printPath() {

        System.out.print("Path : " );
        for(int[] cell : this) {
            System.out.print(Arrays.toString(cell));
        }
        System.out.println("");

        List<int[]> containedCells = getContainedWithin();

        System.out.print(containedCells.size() +" cell contained : " );
        for(int[] cell : containedCells) {
            System.out.print(Arrays.toString(cell));
        }
        System.out.println("");
    }

    CellsList getPath() {

        CellsList cl = new CellsList();
        cl.addAll(this);
        return cl;
    }
}

以下代码查找单元格的邻居(path.java):

//return a list of all neighbors of cell row, col
        private CellsList getNeighbors(int  row, int col) {

            CellsList neighbors = new CellsList();

            for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1  ) {

                for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1  ) {

                    if(!((colNum == col) && (rowNum == row))) {

                        if(isWithinGrid (rowNum, colNum )  ) {

                            neighbors.add( new int[] {rowNum, colNum});

                        }
                    }
                }
            }

            return neighbors;
        }

        private boolean isWithinGrid(int colNum, int rowNum) {

            if((colNum < 0) || (rowNum <0) ) {
                return false;
            }
            if((colNum >= cols) || (rowNum >= rows)) {
                return false;
            }
            return true;
        }
    }

以下代码通过路径查找所有有界单元格(所有包含或包围的标记),并且它们的标记与路径的颜色相反:

List<int[]> getContainedWithin() {

        //find path max and min X values, max and min Y values
        minPathRow = grid[0].length; //set min to the largest possible value
        maxPathCol = grid.length;
        maxPathRow = 0;              //set max to the largest possible value
        maxPathCol = 0;

        //find the actual min max x y values of the path
        for (int[] cell : this) {
            minPathRow = Math.min(minPathRow, cell[0]);
            minPathCol = Math.min(minPathCol, cell[1]);
            maxPathRow = Math.max(maxPathRow, cell[0]);
            maxPathCol = Math.max(maxPathCol, cell[1]);
        }

        List<int[]> block = new ArrayList<>(25);
        int[] cell = get(0);//get an arbitrary cell in the path
        Token pathToken = grid[cell[0]][cell[1]]; //keep a reference to its token

        //iterate over all cells within path x, y limits
        for (int col = minPathCol; col < (maxPathCol); col++) {

            for (int row = minPathRow; row < (maxPathRow); row++) {

                //check cell color
                Token token = grid[row][col];
                if ((token == pathToken) || (token == Token.VIDE)) {
                    continue;
                }
                if (isWithinLoop(row,col)) {
                    block.add(new int[] {row, col});
                }
            }
        }

        return block;
    }

    //check if row, col represent a cell within path by checking if it has a 
    //path-cell to its left, right, top and bottom 
    private boolean isWithinLoop(int row, int col) {

        if(  isPathCellOnLeft(row, col)
             &&
             isPathCellOnRight(row, col)
             &&
             isPathCellOnTop(row, col)
             &&
             isPathCellOnBottom(row, col)
          ) {
            return true;
        }

        return false;
    }     
}

如果你需要更多的元素,现在就告诉我,我会更新必要的。

【问题讨论】:

    标签: java arraylist 2d path-finding


    【解决方案1】:

    此要求意味着以前的路径会影响当前路径的计算。
    可以通过多种方式实现。在当前程序结构中,最简单的可能是在所有路径中添加包含单元格的静态集合。
    请参阅allContainedWithin 以及它在代码中的使用方式。
    另外请注意,我将getContainedWithin() 重构为getter,并将其功能移至新方法findContainedWithin()。 所有更改对其他类没有影响。

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Stack;
    
    //a stack representing cells in the path
    //each cell represented by [row,col]
    class Path extends Stack<int[]>{
    
        private Token[][] grid;
    
        //a path shorter than min can not surround any cell
        private static final int MIN_PATH_LEGTH = 3;
    
        //a collection of cells that has been tested
        private ArrayList<int[]>checked;
    
        //represents the cell where the search starts from
        int[] origin;
        //represents the token of the origin
        Token originToken;
    
        private int rows;
        private int cols;
    
        //represents the path bounds: min/max row/col in path
        private int minPathRow, maxPathRow, minPathCol, maxPathCol;
    
        //a collection of all cells that are bounded by the path
        //and their token is of the opposite color of the path
        private List<int[]> containedWithin;
    
        //a STATIC collection that holds all containedWithin cells, of
        //current and previous paths
        private static CellsList allContainedWithin = new CellsList();
    
        Path(Token[][] grid){
    
            this.grid = grid;
            rows = grid.length;
            cols = grid[0].length;
        }
    
        //search for a path
        boolean findPath(int[] origin) {
    
            this.origin = origin;
    
            int row = origin[0] , col =  origin[1];
    
            //represents the token of the origin
            originToken = grid[row][col];
    
            //initialize list of checked items
            checked = new  CellsList();
    
            boolean found = findPath(row, col);
    
            if(found) {
    
                //find bounded cells
                findContainedWithin();
                //update the collection all
                allContainedWithin.addAll(containedWithin);
    
                printPath();
            } else {
                System.out.println("No path found");
            }
    
            return found;
        }
    
        //recursive method to find path. a cell is represented by its row, col
        //returns true when path was found
        private boolean findPath(int row, int col) {
    
            //check if cell has the same token as origin
            if(grid[row][col] != originToken) {
                return false;
            }
    
            int[] cell = new int[] {row, col};
    
            //check if this cell was tested before to avoid checking again
            if(checked.contains(cell)) {
                return false;
            }
    
            //check if this cell was contained in previously calculated paths
            if(allContainedWithin.contains(cell)) {
                return false;
            }
    
            //get cells neighbors
            CellsList neighbors = getNeighbors(row, col);
    
            //check if solution found. If path size > min and cell
            //neighbors contain the origin it means that path was found
            if((size() >= MIN_PATH_LEGTH) && neighbors.contains(origin)  ) {
    
                add(cell);
                return true;
            }
    
            //add cell to checked
            checked.add(cell);
    
            //add cell to path
            add(cell);
    
            //if path was not found check cell neighbors
            for(int[] neighbor : neighbors ) {
    
                boolean found = findPath(neighbor[0],neighbor[1]);
                if(found) {
                    return true;
                }
            }
    
            //path not found
            pop(); //remove last element from stack
            return false;
        }
    
        //return a list of all neighbors of cell row, col
        private CellsList getNeighbors(int  row, int col) {
    
            CellsList neighbors = new CellsList();
    
            for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1  ) {
    
                for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1  ) {
    
                    if(!((colNum == col) && (rowNum == row))) {
    
                        if(isWithinGrid (rowNum, colNum )  ) {
    
                            neighbors.add( new int[] {rowNum, colNum});
                        }
                    }
                }
            }
    
            return neighbors;
        }
    
        private boolean isWithinGrid(int colNum, int rowNum) {
    
            if((colNum < 0) || (rowNum <0) ) {
                return false;
            }
            if((colNum >= cols) || (rowNum >= rows)) {
                return false;
            }
            return true;
        }
    
        //use for testing
        private void printPath() {
    
            System.out.print("Path : " );
            for(int[] cell : this) {
                System.out.print(Arrays.toString(cell));
            }
            System.out.println("");
    
            List<int[]> containedCells = getContainedWithin();
    
            System.out.print(containedCells.size()+" cell contained : " );
            for(int[] cell : containedCells) {
                System.out.print(Arrays.toString(cell));
            }
            System.out.println("");
        }
    
        CellsList getPath() {
    
            CellsList cl = new CellsList();
            cl.addAll(this);
            return cl;
        }
    
        //finds all cells that are bounded by the path
        //and their token is of the opposite color of the path
        private void findContainedWithin() {
    
            containedWithin = new ArrayList<>();
    
            //find path max and min X values, max and min Y values
            minPathRow = grid[0].length; //set min to the largest possible value
            maxPathCol = grid.length;
            maxPathRow = 0;              //set max to the largest possible value
            maxPathCol = 0;
    
            //find the actual min max x y values of the path
            for (int[] cell : this) {
                minPathRow = Math.min(minPathRow, cell[0]);
                minPathCol = Math.min(minPathCol, cell[1]);
                maxPathRow = Math.max(maxPathRow, cell[0]);
                maxPathCol = Math.max(maxPathCol, cell[1]);
            }
    
            //todo remove after testing
            System.out.println("x range: "+minPathRow + "-"
            + maxPathRow + " y range: " + minPathCol + "-" + maxPathCol);
    
            int[] cell = get(0);//get an arbitrary cell in the path
            Token pathToken = grid[cell[0]][cell[1]]; //keep a reference to its token
    
            //iterate over all cells within path x, y limits
            for (int col = minPathCol; col < (maxPathCol); col++) {
    
                for (int row = minPathRow; row < (maxPathRow); row++) {
    
                    //check cell color
                    Token token = grid[row][col];
                    if ((token == pathToken) || (token == Token.VIDE)) {
                        continue;
                    }
                    if (isWithinLoop(row,col)) {
                        containedWithin.add(new int[] {row, col});
                    }
                }
            }
        }
    
        //returns a collection of all cells that are bounded by the path
        //and their token is of the opposite color of the path
        List<int[]> getContainedWithin() {
    
            return containedWithin;
        }
    
        //check if row, col represent a cell with in path by checking if it has a
        //path-cell to its left, right, top and bottom
        private boolean isWithinLoop(int row, int col) {
    
            if(  isPathCellOnLeft(row, col)
                 &&
                 isPathCellOnRight(row, col)
                 &&
                 isPathCellOnTop(row, col)
                 &&
                 isPathCellOnBottom(row, col)
              ) {
                return true;
            }
    
            return false;
        }
    
        private boolean isPathCellOnLeft(int cellRow, int cellCol) {
    
            for ( int col = minPathCol; col < cellCol ; col++) {
    
                if(getPath().contains(new int[] {cellRow, col})) {
                    return true;
                }
            }
    
            return false;
        }
    
        private boolean isPathCellOnRight(int cellRow, int cellCol) {
    
            for ( int col = cellCol; col <= maxPathCol ; col++) {
    
                if(getPath().contains(new int[] {cellRow, col})) {
                    return true;
                }
            }
    
            return false;
        }
    
        private boolean isPathCellOnTop(int cellRow, int cellCol) {
    
            for ( int row =minPathRow; row < cellRow ; row++) {
    
                if(getPath().contains(new int[] {row, cellCol})) {
                    return true;
                }
            }
    
            return false;
        }
    
        private boolean isPathCellOnBottom(int cellRow, int cellCol) {
    
            for ( int row = cellRow; row <= maxPathRow; row++) {
    
                if(getPath().contains(new int[] {row, cellCol})) {
                    return true;
                }
            }
    
            return false;
        }
    }
    

    请注意,我只运行一些基本测试,例如:

    【讨论】:

    • 感谢您的快速回答,非常感谢您的帮助
    • 你救了我几天的头痛!
    【解决方案2】:

    除了上一个答案,我想添加一个替代方案,它需要对程序进行更深入的更改。
    处理要求的更好方法是更改​​单元格的表示。与其使用 int[]{row, col} ,不如考虑用 Cell 类来表示它,该类具有行、列、令牌、包含等属性。
    Cell 的一个简单实现可以是:

    public class Cell {
    
        private int row, col;
        private Token token;
        private boolean isContained;
    
        Cell(int row, int col) {
    
            this(row, col, Token.VIDE);
        } 
    
        Cell(int row, int col, Token token) {
    
            this.row = Math.abs(row); //to allow only positve addresses
            this.col = Math.abs(col);
            this.token = (token == null) ? Token.VIDE : token;
        }
    
    
        int getRow() {
            return row;
        }
    
        int getCol() {
            return col;
        }
    
        Token getToken() {
            return token;
        }
    
        boolean isContained() {
            return isContained;
        }
    
        void setRow(int row) {
            this.row = row;
        }
    
        void setCol(int col) {
            this.col = col;
        }
    
        void setToken(Token token) {
            this.token = token;
        }
    
        void setContained(boolean isContained) {
            this.isContained = isContained;
        } 
    
        int[] getAddress() {
            return new int[] {row, col};
        }
    
        @Override
        public String toString() {
            return Arrays.toString(getAddress()) +"-"+ token;
        }
    
        @Override
        public boolean equals(Object cell) {
    
            if ((cell == null) || !(cell instanceof Cell)) {
                return false;
            }
    
            return Arrays.equals(getAddress(), ((Cell)cell).getAddress());
        }
    
        @Override
        public int hashCode() {
    
            return 31*row + 17*col;
        }
    }
    

    注意:应该在整个程序中更改此表示。
    (未测试)

    【讨论】:

    猜你喜欢
    • 2017-03-21
    • 1970-01-01
    • 2014-01-27
    • 1970-01-01
    • 2017-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    相关资源
    最近更新 更多