Breaking recursion from listeners

我只是一个虾纸丫 提交于 2019-12-08 05:13:24

问题


I'm trying to break a special case that makes this code recursive.

I have a Javafx game where there are human and computer players each play when it's his turn and there can be many rounds.

A computer is supposed to play automatically and move to the next player immediately and show no direct indication to the UI (but it's possible to review what it did afterwards).

The problem is in the case where there are only computer players, we will come here the moment the currentBoardPane was loaded, enter the condition since all players are computers, set the board of the next player, and then without finishing the call, call this same function again:

    currentBoardPane.addListener((e) -> {  
        if(gameManager.checkIfCurrentPlayerIsComputer()){

            gameManager.playAutoMovesForCurrentPlayer();
            gameManager.setNextPlayer(); // it does current player property = next player

            //update board on scene
            currentBoardPaneIndex = ((currentBoardPaneIndex + 1) % gameManager.getPlayers().size());
            currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex))); //this is a recursive call
        }
    });

Instead of this, if I subscribe a listener to the currentPlayer property in GameManager then I still need to call setNextPlayer() from that listener which is again recursive.

I can make a special case if all players are a computer, then run the game from a while(true){} instead of listeners and binds but there has to be a better way to break this recursion.

Is there a way to not get into recursion while still having listeners and binds?

Notes:

currentBoardPane signifies the current game board on the screen and it's an ObjectProperty.


回答1:


Making the following assumptions about your code:

  1. Everything is currently running on the FX Application Thread
  2. The currentBoardPane.setValue(...) causes the UI to update (so you update the UI each move)

then a "quick and dirty" way to do this is:

currentBoardPane.addListener((e) -> {  
    if(gameManager.checkIfCurrentPlayerIsComputer()){

        gameManager.playAutoMovesForCurrentPlayer();

        //update board on scene
        Platform.runLater(() -> {
            gameManager.setNextPlayer(); // it does current player property = next player
            currentBoardPaneIndex = ((currentBoardPaneIndex + 1) % gameManager.getPlayers().size());
            currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex))); //this is a recursive call
        });
    }
});

This delegates the updates to a new Runnable, schedules that runnable to execute on the FX Application Thread, and exits the handler immediately. Thus the call to currentBoardPane.setValue(...) is executed later and is no longer recursive.

In fact, if you do just a little more work:

private final Executor aiExecutor = Executors.newSingleThreadExecutor();

// ...

currentBoardPane.addListener((e) -> {  
    if(gameManager.checkIfCurrentPlayerIsComputer()){

        Task<Void> makeMoveTask = new Task<Void>() {
            @Override
            protected Void call() {
                gameManager.playAutoMovesForCurrentPlayer();
                return null ;
            }
        };

        makeMoveTask.setOnSucceeded(e -> {

            //update board on scene
            gameManager.setNextPlayer(); // it does current player property = next player
            currentBoardPaneIndex = ((currentBoardPaneIndex + 1) % gameManager.getPlayers().size());
            currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex))); //this is a recursive call
        });

        aiExecutor.execute(makeMoveTask);
    }
});

then this is exactly the code you would use if computing the move took enough time that it would not be acceptable to block the UI while it was happening. (And if computing the move takes very little time, this will still work just fine.) This assumes that playAutoMovesForCurrentPlayer() doesn't update the UI.



来源:https://stackoverflow.com/questions/39395942/breaking-recursion-from-listeners

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!