(我希望这不是重复的,因为我遇到的许多问题都不符合我的需要)
我正在开发一个2D网格游戏,其中有2个具有网格的玩家。有两个参与者:蓝色和红色,每个参与者在牢房中放置一块石头。因此,我想找到一条路径,该路径将所有具有相同颜色的单元格返回到起点,但只有在至少有一个包含对手石的单元格时才可以。
从上面的屏幕截图中:右上角的红色石头没有形成有效的路径。而且居中的人也没有形成一条路,即使那应该是一条路。
我能够找到一条路径,但由于某种原因该路径已损坏,无法正常工作。
编辑: Pather类
public class Pather {
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT(0,1,-1),
RIGHT(1,1,0),
DOWN_RIGHT(2,1,1),
DOWN(3,0,1),
DOWN_LEFT(4,-1,1),
LEFT(5,-1,0),
UP_LEFT(6,-1,-1),
UP(7,0,-1);
public int index, x, y;
Neighbor(int index, int x, int y){
this.index = index;
this.x = x;
this.y = y;
}
}
private static Neighbor[] neighbors = Neighbor.values();
public static ArrayList<Path> findPaths(Stone[][] gameBoard){
ArrayList<Path> paths = new ArrayList<>();
ArrayList<Point> checkedPoints = new ArrayList<>();
for (int i = 0; i < gameBoard.length ; i++) {
for (int j = 0; j < gameBoard[0].length; j++) {
if(gameBoard[i][j] != null){
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (i,j);
if(!checkedPoints.contains(origin)) {
potentialPath.add(origin);
checkedPoints.add(origin);
potentialPath = findPath(gameBoard, i, j, potentialPath, gameBoard[i][j].getPaint(), checkedPoints, Neighbor.RIGHT.index); //Changed from Neighbor.DOWN.index
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[i][j].getPaint()));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(Stone[][] gameBoard, int x, int y, ArrayList<Point> path, Paint color, ArrayList<Point> checkedPoints, int cameFrom){
int startClockwiseScanAtDirection = cameFrom + 5;
for (int i = startClockwiseScanAtDirection; i < startClockwiseScanAtDirection + 7; i++) {
// avoid ArrayIndexOutOfBounds
if(x+neighbors[i%8].x < 0 || y+neighbors[i%8].y < 0 || x+neighbors[i%8].x >= gameBoard.length || y+neighbors[i%8].y >= gameBoard[0].length)
continue;
// check if there's a stone that matches the current stone, we're scanning around
if(gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y] != null && gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y].getPaint() == color){
// found one
Point nextStone = new Point(x+neighbors[i%8].x,y+neighbors[i%8].y);
// is the point we just found the origin of the path?
if(nextStone.equals(path.get(0)) && path.size() > MIN_PATH_LENGTH) { //This seems to prevent drawing a path when we have less stone to form a path with
path.add(nextStone);
checkedPoints.add(nextStone);
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard,x+neighbors[i%8].x, y+neighbors[i%8].y, path, color, checkedPoints, i%8);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
路径类
public class Path {
public Paint getColor() {
return color;
}
public void setColor(Paint color) {
this.color = color;
}
public ArrayList<Point> getCoordinateList() {
return coordinateList;
}
public void setCoordinateList(ArrayList<Point> coordinateList) {
this.coordinateList = coordinateList;
}
private ArrayList<Point> coordinateList;
private Paint color;
public Path(ArrayList<Point> coordinatePath, Paint color){
this.coordinateList = coordinatePath;
this.color = color;
}
@Override
public String toString() {
return coordinateList.toString();
}
}
这里有一些案例测试:
在MainActivity的onCreate()中调用:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gameGrid = findViewById(R.id.gameGrid);
bluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bluePaint.setStyle(Paint.Style.FILL_AND_STROKE);
bluePaint.setColor(Color.BLUE);
redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setStyle(Paint.Style.FILL);
redPaint.setColor(Color.RED);
bgrBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrBluePaint.setStyle(Paint.Style.STROKE);
bgrBluePaint.setStrokeWidth(bgrStrokeWdth);
bgrBluePaint.setColor(Color.BLUE);
bgrRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrRedPaint.setStyle(Paint.Style.STROKE);
bgrRedPaint.setStrokeWidth(bgrStrokeWdth);
bgrRedPaint.setColor(Color.RED);
bluePlayer = new Stone(1,bluePaint, bgrBluePaint);
redPlayer = new Stone(2, redPaint, bgrRedPaint);
gameBoard = new Stone[100][100];
gameBoard[47][47]= redPlayer;
gameBoard[46][47]= bluePlayer;
gameBoard[44][48]= redPlayer; //REDs form a path when you place this stone in the last positioon
gameBoard[44][49]= redPlayer;
gameBoard[45][47]= redPlayer;
gameBoard[45][48]= bluePlayer;
gameBoard[45][49]= bluePlayer;
gameBoard[45][50]= redPlayer;
gameBoard[46][50]= bluePlayer;
gameBoard[46][49]= redPlayer;
gameBoard[46][48]= redPlayer;
gameBoard[47][50]= bluePlayer;
gameBoard[47][48]= bluePlayer;
gameBoard[47][49]= redPlayer;
gameBoard[48][50]= redPlayer;
gameBoard[48][49]= redPlayer;
gameBoard[48][48]= redPlayer;
gameBoard[49][50]= bluePlayer;
gameBoard[48][51]= redPlayer;
gameBoard[44][50] = bluePlayer;
ArrayList<Path> paths = Pather.findPaths(gameBoard);
gameGrid.setPaths(paths);
gameGrid.setGameBoard(gameBoard);
}
在以下位置放置石头可以清除路径:
//Adding the following deletes the path
gameBoard[43][50] = redPlayer; //Adding this one in last position clears the path
gameBoard[45][51] = redPlayer;
我需要弄清楚如何制定条件,首先检查附近的对手,然后验证路径。
编辑2:
Stone.java
public class Stone{
private int _player;
private Paint _paint, _bgrPaint;
public Stone(int player, Paint paint, Paint bgrPaint){
_player = player;
_paint = paint;
_bgrPaint = bgrPaint;
}
public int getPlayer() {
return _player;
}
public Paint getPaint() {
return _paint;
}
public Paint get_bgrPaint() {
return _bgrPaint;
}
}
Point.java
public class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object point) {
return this.x == ((Point) point).x && this.y == ((Point) point).y;
}
@Override
public String toString() {
return "("+x+","+y+")";
}
}
有效路径的外观截图
答案 0 :(得分:1)
解决这种问题的一种或多或少的标准方法是“扫描线”算法。为简单起见,假设我们正在寻找包裹红色点的蓝色路径。 (您可以同时或在第二遍处理中处理包裹蓝点的红色路径,但可以稍后进行处理。)
您可以搜索“扫描线算法”以查看它们在相关应用程序中的工作方式。 Wikipedia page不错。
对于此问题,扫描线是一组y间隔。使用最左边的(最少x个)蓝点进行初始化。对于每个垂直相邻的蓝点,它会获得一个间隔。每个间隔代表一个潜在的蓝色多边形的垂直切片。
算法的其余部分是设计将扫描线向右移动一个位置(x递增)时更新扫描线所需的规则。这将是更新每个间隔的问题。当步骤找到一组断开连接的垂直相邻点时,将添加一个新间隔。在某些情况下,间隔将“消失”,因为潜在的多边形边界死角(认为是C形)。在其他情况下,它们将“合并”,因为在相应的x坐标处,存在一组1个或多个垂直相邻的连接点。在其他情况下,多边形将成功完成最终的一组1个或更多垂直相邻点。
细节会很简单,但通过案例分析不难得出结论。
要跟踪成功的多边形,间隔可以包含两条先前的点链:多边形上下边界。
最后一个要考虑的是,成功闭合的多边形是否至少包含一个红点。但这很容易。如果对于任何x坐标,表示多边形的间隔在一个红点旁括起来,则答案为是。可以将其记录为在时间间隔中保留的初始错误布尔值,每次看到这样的红点时都将其设置为true。成功关闭多边形后,检查标志以查看是否应使用它。
通过使用适当的数据结构,例如间隔树,对于大型网格,上述所有内容都可以变得高效。但是,如果网格相对较小,则使用简单列表应该会很好。无论如何,请考虑首先使用扫描线列表作为原型,然后根据需要使用更复杂的数据结构进行优化。
答案 1 :(得分:1)
正如我在评论中所写,没有mvce很难提供详细的帮助。
根据我在代码中看到的信息,您正在尝试映射板上的所有循环单色路径。
我对代码进行了一些记录在案的更改,希望(无法正确检查)它可以帮助您改进代码。
请注意,由于未发布Stone
类,因此我将董事会的代表制更改为int[][]
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Phather {
private static final int RED = 2, BLUE = 1;
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT ( 1,-1),
RIGHT ( 1, 0),
DOWN_RIGHT( 1, 1),
DOWN ( 0, 1),
DOWN_LEFT (-1, 1),
LEFT (-1, 0),
UP_LEFT (-1,-1),
UP ( 0,-1);
int x, y;
Neighbor(int x, int y){
this.x = x;
this.y = y;
}
}
public static Set<Path> findPaths(int[][] gameBoard){
//use set to prevent duplicate paths
Set<Path> paths = new HashSet<>();
for (int x = 0; x < gameBoard.length ; x++) {
for (int y = 0; y < gameBoard[0].length; y++) {
//note that array indexes are [y][x] while point arguments are x,y
if(gameBoard[y][x] != 0){
//use set to prevent duplicate elements. initialize it to allow for
//overlapping paths (paths that contain some shared points)
Set<Point> checkedPoints = new HashSet<>();
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (x,y);
if(checkedPoints.add(origin)) { //add returns false if duplicate
potentialPath.add(origin);
potentialPath = findPath(gameBoard, x, y, potentialPath, checkedPoints);
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[y][x]));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(int[][] gameBoard, int x, int y,
ArrayList<Point> path, Set<Point> checkedPoints){
int color = gameBoard[y][x]; //no need for color as argument. get from stone
for(Neighbor neighbor : Neighbor.values()) {
int neighborX = x + neighbor.x, neighborY = y + neighbor.y;
// avoid ArrayIndexOutOfBounds
//todo: refactor to method isValidAddress(x,y,maxX, maxY)
if((neighborX < 0) || ( neighborY < 0) || (neighborY >= gameBoard.length)
|| (neighborX >= gameBoard[0].length)) {
continue;
}
// check if there's a stone that matches the current stone, we're scanning around
if((gameBoard[neighborY][neighborX] != 0) && (gameBoard[neighborY][neighborX] == color)){
// found one
Point nextStone = new Point(neighborX,neighborY);
// is the point we just found the origin of the path ?
if(nextStone.equals(path.get(0)) && (path.size() > MIN_PATH_LENGTH)) {
path.add(nextStone); //do you want it in path twice ?
//checkedPoints.add(nextStone); //if added to path before, it is already in checkedPoints
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard, neighborX, neighborY, path, checkedPoints);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
class Path {
private ArrayList<Point> coordinateList;
private int color;
Path(ArrayList<Point> coordinatePath, int color){
coordinateList = coordinatePath;
this.color = color;
}
int getColor() { return color; }
@Override
public String toString() {
return coordinateList.toString();
}
List<Point> getPoints() { return coordinateList; }
int size() { return coordinateList.size(); }
@Override
public boolean equals(Object p){
if (p == this) { return true; }
if (p == null) { return false;}
if (!(p instanceof Path)) {return false; }
Path path = (Path)p;
return getPoints().containsAll(path.getPoints())
&& path.getPoints().containsAll(getPoints());
}
}