java - 如何防止 StackOverflowError?
问题描述
我正在制作一个国际象棋程序很长一段时间。我创建了一个名为 Checkmate 的类来检测 Checkmate、Illegal move、Checkmate 和 Stalemate。截至目前,我刚刚实施了检查和非法移动。但是每当我运行程序时,它都会导致 StackOverflowError。
/**
* <code>String player</code> contains the constant of the currently playing player.
* This method will first find out the position of the opponent's King and store it in <code>Point opponentKing</code>.
* After that, it will find all the currently playing player's pieces. It will find the possible moves of each of the piece.
* If the <code>opponentKing</code> is found within the possible moves of any piece, it will return "true, else it will return "false".
* @return "true" if it is check. "false" if it is not a check.
*/
public boolean getIsCheck(String[][] board) {
boolean isCheck = false;
Point opponentKing = new Point(0,0);
outer: for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if(!board[i][j].substring(1).equals(player) && board[i][j].substring(0,1).equals(Board.KING)) {
opponentKing = new Point(i, j);
break outer;
}
}
}
List<Point> friendlyPieces = new ArrayList<>();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (board[i][j].substring(1).equals(player)
&& !board[i][j].equals(Board.EMPTY))
friendlyPieces.add(new Point(i, j));
outer: for(Point p : friendlyPieces) {
List<Point> availableMoves = new ArrayList<>();
switch (board[p.x][p.y].substring(0,1)){
case Board.PAWN: availableMoves = new Pawn(player).getCheckMoves(p); break;
case Board.ROOK: availableMoves = new Rook(player).getAvailableMoves(p); break;
case Board.KNIGHT: availableMoves = new Knight(player).getAvailableMoves(p); break;
case Board.BISHOP: availableMoves = new Bishop(player).getAvailableMoves(p); break;
case Board.KING: availableMoves = new King(player).getAvailableMoves(p); break;
case Board.QUEEN: availableMoves = new Queen(player).getAvailableMoves(p); break;
}
for (Point point: availableMoves) {
if(point.equals(opponentKing)) {
isCheck = true;
break outer;
}
}
}
return isCheck;
}
那是查找棋子当前位置是否处于对王过牌状态的代码。
public boolean getIsIllegal(Point start, Point end, Pieces piece){
String[][] board = Board.board;
board[start.x][start.y] = Board.EMPTY;
board[end.x][end.y] = piece.getPiece() + piece.getPlayer();
return getIsCheck(board);
}
这是同一类 Checkmate 中的 getIsIllegal()。
每当我从棋盘上选择棋子时。它给了我以下错误:-
Exception in thread "main" java.lang.StackOverflowError
at Pieces.Pawn.getCheckMoves(Pawn.java:56)
at Win.Checkmate.getIsCheck(Checkmate.java:60)
at Win.Checkmate.getIsIllegal(Checkmate.java:81)
at Pieces.Knight.getAvailableMoves(Knight.java:30)
at Win.Checkmate.getIsCheck(Checkmate.java:62)
at Win.Checkmate.getIsIllegal(Checkmate.java:81)
at Pieces.Knight.getAvailableMoves(Knight.java:34)
at Win.Checkmate.getIsCheck(Checkmate.java:62)
at Win.Checkmate.getIsIllegal(Checkmate.java:81)
等等。
if (x<7 && y<6 && isEmpty(x + 1, y + 2) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x + 1, y + 2));
if (x>0 && y<6 && isEmpty(x - 1, y + 2) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x - 1, y + 2));
if (x<6 && y>0 && isEmpty(x + 2, y - 1) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x + 2, y - 1));
if (x<6 && y<7 && isEmpty(x + 2, y + 1) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x + 2, y + 1));
if (x<7 && y>1 && isEmpty(x + 1, y - 2) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x + 1, y - 2));
if (x>0 && y>1 && isEmpty(x - 1, y - 2) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x - 1, y - 2));
if (x>1 && y>0 && isEmpty(x - 2, y - 1) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x - 2, y - 1));
if (x>1 && y<7 && isEmpty(x - 2, y + 1) && !check.getIsIllegal(point, new Point(x, y), this)) availableMoves.add(new Point(x - 2, y + 1));
上面的代码是我为获取当前棋盘上骑士的可用动作而编写的行。这就是 getIsCheck() 和 getIsIllegal() 之间不断循环的地方。请参阅例外情况以获得进一步说明。
解决方案
尽管棋子的移动是不合法的,但棋子检查了国王!例如:白 Kc2、Rb2 - 黑 Kh2、Ba3:
在移动 Kc2-c1 之后,Rb2 让牌,尽管 Rb2 不能移动。
改变你对可用和非法移动的定义:可用移动可能是非法的。所以 getAvailableMoves() 不会调用 getIsIllegal()。所以 Rb2-h2 是一个可用的移动,方法 isCheck() 有效。
现在,要获得合法的移动,您必须编写如下代码:
...
Pieces piece;
switch (board[p.x][p.y].substring(0,1)){
case Board.PAWN: piece = new Pawn(player); break;
case Board.ROOK: piece = new Rook(player); break;
case Board.KNIGHT: piece = new Knight(player); break;
case Board.BISHOP: piece = new Bishop(player); break;
case Board.KING: piece = new King(player); break;
case Board.QUEEN: piece = new Queen(player); break;
}
List<Point> availableMoves = piece.getAvailableMoves(p);
List<Point> legalMoves = new ArrayList<>();
for (Point point : availableMoves ) {
if (!getIsIllegal(p, point, piece))
legalMoves.add(point);
}
...
推荐阅读
- ssh - 两个 Mercurial 回购。在远程机器上——一个可以通过 ssh 克隆;另一个,不是
- laravel - 如何在 Laravel Vue 中沿侧引导程序使用 zurb 基础?
- flutter - 如何在 SliverChildListDelegate 中使用 FirestoreAnimatedList?
- android - E/CRASH(7870):信号 11 (SIGSEGV),代码 1 (SEGV_MAPERR),故障地址 0000007c2f67f818
- amazon-web-services - AWS ASG 冷却期和扩展策略中的预热期有什么区别?
- javascript - 在 highcharts 散点图中添加自定义变量作为工具提示
- javascript - 指南针使用博览会记录磁力计的值?
- java - 使用拖放 android 在 PDF 上添加签名或图像(获取 x 和 y 坐标)
- ubuntu-18.04 - Chromium 在 Ubuntu 上构建失败://build_overrides/build.gni:5:1 处的错误:无法加载“/home/chromium/src/build/config/gclient_args.gni”
- vue.js - 如何使用 drf 和 vuejs 设置 github 身份验证?