简体   繁体   English

Peg纸牌解决方案更改目的地

[英]Peg solitaire solution change destination

I wrote a program that solves a peg solitaire in java.我写了一个程序来解决 java 中的 peg 纸牌。

My program gets a starting board and a destination board and try to finish the game.我的程序有一个起始板和一个目标板,并尝试完成游戏。

I have a sort of counter that count my turn because I have a destination with more the 1 peg rest and as assume that if I have to remove only 2 pegs so I can solve it in only 2 moves.我有一种计数器来计算我的回合,因为我有一个目的地有更多的 1 个钉子,并且假设如果我只需要移除 2 个钉子,那么我只需 2 个动作就可以解决它。

I have an board class that I create:我有一个我创建的板类:

public class Board {
    private int board[][] = new int[7][7];
    public Board(String place)
    {
        board[0][0]=2;
        board[1][0]=2;
        board[5][0]=2;
        board[6][0]=2;
        board[0][1]=2;
        board[1][1]=2;
        board[5][1]=2;
        board[6][1]=2;
        board[0][5]=2;
        board[1][5]=2;
        board[5][5]=2;
        board[6][5]=2;
        board[0][6]=2;
        board[1][6]=2;
        board[5][6]=2;
        board[6][6]=2;
        int loca=0;//location on the string of place
        for(int i=0;i<7;i++){
            for(int j=0;j<7;j++){
                if(board[i][j]!=2) {
                    if (place.charAt(loca) == 'O') {
                        loca++;
                        board[i][j] = 1;
                    } else if (place.charAt(loca) == '.') {
                        loca++;
                        board[i][j] = 0;
                    }
                }
                System.out.print(board[i][j]);//print for test
            }
            System.out.println();//print for test
        }
        System.out.println();

    }
    public Board(Board copy){
        for(int i=0;i<7;i++)
        {
            for(int j=0;j<7;j++)
            {
                board[i][j]=copy.getValue(i,j);
            }
        }
    }
    public int getValue(int x, int y)
    {
        return board[x][y];
    }
    public boolean isFinished(Board destination)
    {
        for(int i=0;i<7;i++)
        {
            for(int j=0;j<7;j++)
            {
                if(this.getValue(i,j)!=destination.getValue(i,j))
                {
                    return false;
                }
            }
        }
        return true;
    }
    public Board turn(Board board,String direction,int x,int y)
    {
        if(direction.equals("right"))
        {
            board.setValue(x,y,0);
            board.setValue(x+1,y,0);
            board.setValue(x+2,y,1);
            return board;
        }
        else if(direction.equals("left"))
        {
            board.setValue(x,y,0);
            board.setValue(x-1,y,0);
            board.setValue(x-2,y,1);
            return board;
        }
        else if(direction.equals("up"))
        {
            board.setValue(x,y,0);
            board.setValue(x,y-1,0);
            board.setValue(x,y-2,1);
            return board;
        }
        else if(direction.equals("down"))
        {
            board.setValue(x,y,0);
            board.setValue(x,y+1,0);
            board.setValue(x,y+2,1);
            return board;
        }
        else{
            System.out.println("there is not such direction, method turn on board class");
            return null;//just for caution
        }
    }
    public boolean isLegal(int x, int y){
        if(board[x][y]==2)
        {
            return false;
        }
        else{
            return true;
        }
    }
    public boolean canTurn(String direction,int x,int y){
        if(direction.equals("right"))
        {
            if(x<5) {
                if (board[x][y] == 1 && board[x + 1][y] == 1 && board[x + 2][y] == 0) {
                    return true;
                }
            }
        }
        else if(direction.equals("left"))
        {
            if(x>1) {
                if (board[x][y] == 1 && board[x - 1][y] == 1 && board[x - 2][y] == 0) {
                    return true;
                }
            }
        }
        else if(direction.equals("up"))
        {
            if(y>1) {
                if (board[x][y] == 1 && board[x][y - 1] == 1 && board[x][y - 2] == 0) {
                    return true;
                }
            }
        }
        else if(direction.equals("down"))
        {
            if(y<5) {
                if (board[x][y] == 1 && board[x][y + 1] == 1 && board[x][y + 2] == 0) {
                    return true;
                }
            }
        }
        else{
            System.out.println("there is not such direction, method canTurn on board class");
            return false;//just for caution
        }
        return false;
    }
    public void setValue(int x, int y, int value)
    {
        board[x][y]=value;
    }
}

and I wrote my "solver" class.我写了我的“求解器”类。

public class PegSolver {
    public int peg =1;
    Board destinationBoard = new Board("OOOOOOOOOOOOOOOOO..OOOOOOOOOOOOOO");
    Board board = new Board("OOOOOOOOOOOOOOOO.OOOOOOOOOOOOOOOO");

    public void start(){

        solve(0,board);
    }
    public boolean solve(int turn, Board board){
        Board temp = new Board(board);
        if(turn>peg)
        {
            return false;
        }
        else if(turn==peg){
            //todo:check if solve
            if(temp.isFinished(destinationBoard))
            {
                System.out.println("solution");
                return true;
            }
            else
            {
                return false;
            }
        }
        else//lower then 8
        {
            for(int i=0;i<7;i++){
                for (int j=0;j<7;j++)
                {
                    if(board.isLegal(i,j)) {
                        if(board.canTurn("right",i,j) && solve(turn++, temp.turn(temp, "right", i, j)))
                        {
                            return true;
                        }
                        else if(board.canTurn("left",i,j) && solve(turn++, temp.turn(temp, "left", i, j)))
                        {
                            return true;
                        }
                        else if(board.canTurn("up",i,j) && solve(turn++, temp.turn(temp, "up", i, j)))
                        {
                            return true;
                        }
                        else if(board.canTurn("down",i,j) && solve(turn++, temp.turn(temp, "down", i, j)))
                        {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
}

When the program finds a solution, it needs to print "solution" but for some reason my program can't find a solution even when it's a basic destination with one move.当程序找到解决方案时,它需要打印“解决方案”,但由于某种原因,即使它是一个基本目标,我的程序也无法找到解决方案。

Can someone help me please?有人能帮助我吗?

Please review the modified code.请检查修改后的代码。 Many of the changes are related to indices and directions inconsistency across the code: where x represents horizontal index and y represents vertical index: array index should be board[y][x] (and not board[x][y]).许多更改与代码中的索引和方向不一致有关:其中 x 表示水平索引,y 表示垂直索引:数组索引应为 board[y][x](而不是 board[x][y])。
Many "magic numbers" were changed to constants for better readability of the code.许多“神奇数字”被更改为常量以提高代码的可读性。 A toString method was added to the Board class to print out a board state.toString方法添加到Board类以打印出电路板状态。 It uses special characters to make a nice printout :它使用特殊字符来制作漂亮的打印输出:

在此处输入图片说明

This is helpful when debugging.这在调试时很有帮助。

public class PegSolver {

    private final Board startBoard, destinationBoard;

    public PegSolver(Board startBoard, Board destinationBoard) {
        super();
        this.startBoard = startBoard;
        this.destinationBoard = destinationBoard;
    }

    public void start(){
        solve(0,startBoard);
    }
    private boolean solve(int turn, Board board){

        //check if solve
        if(board.isFinished(destinationBoard))
        {
            System.out.println("solved after "+ turn +" turns");
            return true;
        }

        for(int x=0;x<board.boardSize();x++){
            for (int y=0;y<board.boardSize();y++)
            {
                if(board.isLegal(x,y)) {
                    if(board.canTurn("right",x,y)
                            //turn++ changed to turn+1 so turn is incremented before invoking next solve
                            //and avoid changing the value of turn
                            && solve(turn+1, board.turn(new Board(board), "right", x, y)))
                        return true;
                    else if(board.canTurn("left",x,y)
                            && solve(turn+1, board.turn(new Board(board), "left", x, y)))
                        return true;
                    else if(board.canTurn("up",x,y)
                            && solve(turn+1, board.turn(new Board(board), "up", x, y)))
                        return true;
                    else if(board.canTurn("down",x,y)
                            && solve(turn+1, board.turn(new Board(board), "down", x, y)))
                        return true;
                }
            }
        }

        return false;
    }

    public static void main(String[] args) {

        Board[] destinationBoards = {
                //by order of number of turns
                new Board("OOOOOOOOOOOOOO..OOOOOOOOOOOOOOOOO"),  //one right turn
                new Board("OOO.OOOO.OOOOO.OOOOOOOOOOOOOOOOOO"),  //right, down
                new Board("OOO.OO..OOOOOO.OOOOOOOOOOOOOOOOOO"),  //right, down,right
                new Board("OOO.OOO.OOOOO..OOOOO.OOOOOOOOOOOO"),  //right, down,right,up
                new Board("OOOOOOO..OOOO...OOOO.OOOOOOOOOOOO"),  //right, down,right,up,up
                new Board(".OO.OOO.OOOOO...OOOO.OOOOOOOOOOOO"),  //right, down,right,up,up,down
                new Board(".OO.OOO.OOOOO...OOOOO..OOOOOOOOOO"),  //right, down,right,up,up,down, left
                new Board(".OO.OOO.OOOOO...OOOOO.OOOOO.OO.OO"),  //right, down,right,up,up,down,left,up
                new Board(".OO.OO..O.OOO...OOOOO.OOOOO.OO.OO"),  //10 turns
                new Board("..O..O.O..OOO...OOOO..OOOOO..O..O"),  //15 turns
                new Board(".........O................O......"),  //30 turns
                new Board("...................O............."),  //31 turns
        };

        Board startBoard = new Board("OOOOOOOOOOOOOOOO.OOOOOOOOOOOOOOOO");

        for(Board destinationBoard : destinationBoards ){
            new PegSolver(startBoard, destinationBoard).start();
        }
    }
}

class Board {

    //int representation of the three states of a board cell
    private final static int  EMPTY = 0, PEG = 1, BORDER = 2;
    /*cahr representation of the three states of a board cell
      special chars are used to get nice printout
      todo: change board to char[][] to avoid the need for two
      representations (int and char)
     */
    private final static char[] CHAR_REPRESENTATION = {9898,9899,10062};
    private final static char ERROR = '?';
    private final int BOARD_SIZE=7, CORNER_SIZE=2;
    private final int board[][] = new int[BOARD_SIZE][BOARD_SIZE];

    public Board(String place)  {

        int loca=0;
        for(int y=0;y<BOARD_SIZE;y++){
            for(int x=0;x<BOARD_SIZE;x++){
                if(isWithinBoard(x,y)) {
                    if (place.charAt(loca) == 'O') {
                        loca++;
                        board[y][x] = PEG;
                    } else if (place.charAt(loca) == '.') {
                        loca++;
                        board[y][x] = EMPTY;
                    }
                }else{
                    board[y][x] = BORDER;
                }
            }
        }
        //for testing
        //System.out.println(this);
    }

    //copy constructor
    public Board(Board copy){
        for(int x=0;x<BOARD_SIZE;x++)
        {
            for(int y=0;y<BOARD_SIZE;y++)
            {
                board[y][x]=copy.getValue(x,y);
            }
        }
    }

    public int getValue(int x, int y)
    {
        return board[y][x];  //and not return board[x][y];
    }

    public boolean isFinished(Board destination)
    {
        for(int i=0;i<BOARD_SIZE;i++)
        {
            for(int j=0;j<BOARD_SIZE;j++)
            {
                if(this.getValue(i,j)!=destination.getValue(i,j))
                    return false;
            }
        }
        return true;
    }

    public Board turn(Board board,String direction,int x,int y)
    {
        if(direction.equals("right"))
        {
            board.setValue(x,y,EMPTY);
            board.setValue(x+1,y,EMPTY);
            board.setValue(x+2,y,PEG);
            return board;
        }
        else if(direction.equals("left"))
        {
            board.setValue(x,y,EMPTY);
            board.setValue(x-1,y,EMPTY);
            board.setValue(x-2,y,PEG);
            return board;
        }
        else if(direction.equals("up"))
        {
            board.setValue(x,y,EMPTY);
            board.setValue(x,y-1,EMPTY);
            board.setValue(x,y-2,PEG);
            return board;
        }
        else if(direction.equals("down"))
        {
            board.setValue(x,y,EMPTY);
            board.setValue(x,y+1,EMPTY);
            board.setValue(x,y+2,PEG);
            return board;
        }

        System.out.println("there is not such direction, method turn on startBoard class");
        return null;
    }

    public boolean isLegal(int x, int y){
        if(board[y][x]==BORDER)   //and not if(board[x][y]==BORDER)
            return false;

        return true;
    }

    public boolean canTurn(String direction,int x,int y){
        if(direction.equals("right") && x < BOARD_SIZE - 2)
        {
            if (board[y][x] == PEG && board[y][x + 1] == PEG && board[y][x + 2] == EMPTY)
                return true;
        }
        else if(direction.equals("left") && x > 1)
        {
            if (board[y][x] == PEG && board[y][x - 1] == PEG && board[y][x - 2] == EMPTY)
                return true;

        }
        else if(direction.equals("up") && y > 1)
        {
            if (board[y][x] == PEG && board[y - 1][x] == PEG && board[y - 2][x] == EMPTY)
                return true;

        }
        else if(direction.equals("down") && y < BOARD_SIZE - 2)
        {
            if (board[y][x] == PEG && board[y + 1][x] == PEG && board[y + 2][x] == EMPTY)
                return true;
        }

        return false;
    }

    public void setValue(int x, int y, int value)
    {
        board[y][x]=value;  //and not board[x][y]=value;
    }

    //for square nxn board
    public int boardSize(){
        return board.length;
    }

    public boolean isWithinBoard(int x, int y){

        //check bounds
        if (x < 0 || y < 0 || x >= BOARD_SIZE || y >= BOARD_SIZE) return false;
        //left top corner
        if (x < CORNER_SIZE && y < CORNER_SIZE) return false;
        //right top corner
        if(x >= BOARD_SIZE - CORNER_SIZE && y < CORNER_SIZE) return false;
        //left bottom corner
        if(x < CORNER_SIZE && y >= BOARD_SIZE - CORNER_SIZE) return false;
        //right bottom corner
        if(x >= BOARD_SIZE - CORNER_SIZE && y >= BOARD_SIZE - CORNER_SIZE) return false;

        return true;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int[] row : board) {
            for(int cell : row){
                if(cell<CHAR_REPRESENTATION.length && cell >= 0) {
                    sb.append(CHAR_REPRESENTATION[cell]);
                }else{
                    sb.append(ERROR);
                }
            }
            sb.append("\n"); //new line
        }
        return sb.toString();
    }
}

Todo:去做:
The code is working but it can not cope with a solution that requires too many moves for two reasons:该代码正在运行,但由于两个原因,它无法应对需要太多移动的解决方案:
The code performs DFS which means it is searching for a solution, but not necessarily the shortest one (the one with the least moves).该代码执行DFS ,这意味着它正在寻找一个解决方案,但不一定是最短的一个(移动最少的那个)。 For the shortest one you need to implement BFS .对于最短的,您需要实现BFS (BFS can late be enhanced to Dijkstra's Algorithm or A*, A Star, Algorithm). (BFS 可以后期增强为 Dijkstra 算法或 A*,A 星,算法)。
The second reason is the recursion.第二个原因是递归。 When recursion gets too long (deep) it is prone to StackOverflowError exception.当递归变得太长(深)时,它很容易出现StackOverflowError异常。 Change to solution to a iterative one to be able to find solutions which require many turns.将解决方案更改为迭代解决方案,以便能够找到需要多次转弯的解决方案。
(This limitation might not be relevant to this game because of the small number of moves). (由于移动次数较少,此限制可能与此游戏无关)。

If something is not clear, don't hesitate to ask.如果有什么不清楚的,不要犹豫,问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM