简体   繁体   English

使用递归来解决Peg Solitaire

[英]Using Recursion to solve Peg Solitaire

The issue I am currently having is my code is failing to solve different variations of a peg solitaire board. 我目前遇到的问题是我的代码无法解决挂钉单板的不同变化。 My test program tests 4 simple solvable boards. 我的测试程序测试了4个简单的可解决板。 (1 move solutions) one move up, one move down, one move left, one move right. (1移动解决方案)一个向上移动,一个向下移动,一个移动向左移动,一个向右移动。 My code solves these with no problems along with testing an unsolvable board. 我的代码解决了这些问题,同时测试了无法解决的板。 The issue I am having is with solving more complicated problems such as a plus, a rhombus, and a standard board. 我遇到的问题是解决更复杂的问题,如加号,菱形和标准板。

在此输入图像描述

I'm not quite sure how to add the recursion to this problem. 我不太确定如何将递归添加到此问题中。 I have added it at the end of the solveHelp method calling setupMove again but that breaks the rest of my code. 我已经在solveHelp方法的末尾添加了它,再次调用setupMove但是这会破坏我的其余代码。 Not allowing the simple solutions to be solved correctly. 不允许正确解决简单的解决方案。

What would be the best way to apply the recursion for this problem? 对这个问题应用递归的最佳方法是什么?

public static boolean setupMove(boolean[][] pegs, int startX, int startY, int jumpX, int jumpY, int endX, int endY) {

        // Look at all of the pegs in the board
            for(int x = 0; x < pegs.length; x++) {
                for(int y = 0; y < pegs[x].length; y++) {
                    if(pegs[x][y]) {
                        startX = x;
                        startY = y;

                        if(startX <= 5 && pegs[startX][startY] == true && pegs[startX + 1][startY] == true && pegs[startX + 2][startY] == false) {
                            tryMove(pegs, startX, startY, startX + 1, startY, startX + 2, startY);
                        }
                        if(startX >= 2 && pegs[startX][startY] == true && pegs[startX - 1][startY] == true && pegs[startX - 2][startY] == false) {
                            tryMove(pegs, startX, startY, startX - 1, startY, startX - 2, startY);
                        }
                        if(startY <= 5 && pegs[startX][startY] == true && pegs[startX][startY + 1] == true && pegs[startX][startY + 2] == false) {
                            tryMove(pegs, startX, startY, startX, startY + 1, startX, startY + 2);
                        }
                        if(startY >= 2 && pegs[startX][startY] == true && pegs[startX][startY - 1] == true && pegs[startX][startY - 2] == false) {
                            tryMove(pegs, startX, startY, startX, startY - 1, startX, startY - 2);
                        }
                    }
                }
            }
        if(win) {
            return true;
        } else {
            solution = null;
            return false;
        }
    }

    public static void tryMove(boolean[][] pegs, int startX, int startY, int jumpX, int jumpY, int endX, int endY){
        pegs[startX][startY] = false;
        pegs[jumpX][jumpY] = false;
        pegs[endX][endY] = true;
        prevSolution = solution;
        solution = solution + " " + startY + " " + startX + " " + endY + " " + endX;
        solveHelp(pegs, startX, startY, jumpX, jumpY, endX, endY);
    }

    public static void solveHelp(boolean[][] pegs, int startX, int startY, int jumpX, int jumpY, int endX, int endY) {
        for(int x = 0; x < pegs.length; x++) {
            for(int y = 0; y < pegs[x].length; y++) {
                if(pegs[x][y]) {
                    pegCount++;
                }
            }
        }
        if(pegs[3][3] && pegCount == 1) {
            // WE WIN!!!
            win = true;
        }

        if((!win && pegCount == 1) || (endX < 0 || endY < 0 || endX >= pegs.length || endY >= pegs[endX].length || (endX < 2 && endY < 2) || (endX >= 5 && endY < 2) || (endX < 2 && endY >= 5) || (endX >= 5 && endY >= 5))){
            pegs[startX][startY] = true;
            pegs[jumpX][jumpY] = true;
            pegs[endX][endY] = false;
            pegCount++;
            solution = prevSolution;
        }
        pegCount = 0;

    }

Expressing an algorithm recursively is about writing it as a method that solves the problem by transforming it into slightly simpler versions of itself. 递归地表达算法是将其编写为一种方法,通过将其转换为稍微简单的版本来解决问题。 Then call the same method recursively to solve those. 然后递归调用相同的方法来解决这些问题。

The method finally stops the chain of self-calls by checking for one or more "base cases." 该方法最终通过检查一个或多个“基本案例”来停止自调用链。 These are when the problem is so simple that the answer is obvious. 这些问题很简单,答案很明显。

You've chosen a method signature that won't allow a recursive solution. 您选择了一种不允许递归解决方案的方法签名。 There's no dishonor in this. 这没有羞辱。 Getting a usable recursive method signature is a skill acquired through lots of practice. 获得可用的递归方法签名是通过大量练习获得的技能。

Here you'll do better if the method accepts just a solitaire board configuration and the number of pegs it contains, say N. Then for each legal way of making a jump, it makes that jump, producing a new version of the board with N-1 pegs. 如果方法仅接受单人纸板配置及其包含的钉数,那么你会做得更好。比如N.然后对于每个合法的跳跃方式,它会跳跃,产生一个新版本的N -1钉。 It calls itself to solve this new, smaller problem. 它要求自己解决这个新的,更小的问题。 The base case is 1 peg, which of course means you win. 基础案例是1 peg,这当然意味着你赢了。

void play(boolean [][] board, int nPegs) {
  if (nPegs == 1) { System.out.println("We win!"); System.exit(0); }
  else {
    for (int i = 0; i < 7; ++i) { // i is the row
      for (int j = 0; j < 7; ++j) { // j is the column
        if (!board[i][j]) continue; // No pin. Skip this position
        if (isLegalToJumpEast(board, i, j)) {
          // Do the jump east and solve recursively.
          board[i][j] = board[i][j + 1] = false;
          board[i][j + 2] = true;
          play(board, n - 1);
          // Undo the jump so the search can continue.
          board[i][j] = board[i][j + 1] = true;
          board[i][j + 2] = false;
        }
        // .. similar if()'s for West, North, and South.
      }
    }
  }
}

Of course this skips interesting parts of the problem like printing the solution once it's found. 当然,这会跳过问题的有趣部分,例如一旦找到解决方案就打印出来。

Also, this algorithm is incredibly wasteful because it will re-evaluate the same board configuration many times. 此外,这种算法非常浪费,因为它会多次重新评估相同的电路板配置。 This is totally unnecessary. 这完全没必要。 Once you've explored all possible outcomes of a given board, there's no sense in doing it again. 一旦你探索了特定董事会的所有可能结果,再做一次就没有任何意义了。

You can fix this problem by "memoizing" the recursive method. 您可以通过“记忆”递归方法来解决此问题。 At the start of the method, check a set of already-explored boards and do nothing if the current board is found. 在方法开始时,检查一组已经探索过的电路板,如果找到当前电路板则不执行任何操作。 Else save a copy in the set and proceed. 否则在集中保存副本并继续。

The Java details of copying and saving 2d matrices in a Set<> are another topic for you to explore. Set<>中复制和保存2d矩阵的Java细节是您要探索的另一个主题。 You might need to figure this out for your algorithm to finish in reasonable time. 您可能需要弄清楚算法是否在合理的时间内完成。

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

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