Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/383.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 中断侦听器的递归_Java_Recursion_Javafx_Java 8_Javafx 8 - Fatal编程技术网

Java 中断侦听器的递归

Java 中断侦听器的递归,java,recursion,javafx,java-8,javafx-8,Java,Recursion,Javafx,Java 8,Javafx 8,我试图打破一个使代码递归的特殊情况 我有一个Javafx游戏,当轮到他时,每个人都有人和电脑玩家玩,可以玩很多回合 计算机应该自动播放,并立即移动到下一个播放器,并且不向用户界面显示任何直接指示(但可以查看它之后做了什么) 问题是,如果只有计算机播放器,我们将在加载currentBoardPane时来到这里,输入条件,因为所有播放器都是计算机,设置下一个播放器的板,然后在不完成调用的情况下,再次调用相同的函数: currentBoardPane.addListener((e) ->

我试图打破一个使代码递归的特殊情况

我有一个Javafx游戏,当轮到他时,每个人都有人和电脑玩家玩,可以玩很多回合

计算机应该自动播放,并立即移动到下一个播放器,并且不向用户界面显示任何直接指示(但可以查看它之后做了什么)

问题是,如果只有计算机播放器,我们将在加载
currentBoardPane
时来到这里,输入条件,因为所有播放器都是计算机,设置下一个播放器的板,然后在不完成调用的情况下,再次调用相同的函数:

    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
        }
    });
相反,如果我订阅了
GameManager
中的
currentPlayer
属性的侦听器,那么我仍然需要从该侦听器调用
setNextPlayer()
,这也是递归的

我可以做一个特例,如果所有玩家都是一台计算机,然后从
while(true){}
而不是监听器和绑定运行游戏,但是必须有更好的方法来打破这种递归

有没有一种方法可以在仍然有侦听器和绑定的情况下不进入递归

注:


currentBoardPane
表示屏幕上的当前游戏板,它是一个
对象属性

对您的代码做出以下假设:

  • 所有内容当前都在FX应用程序线程上运行
  • currentBoardPane.setValue(…)
    会导致UI更新(因此每次移动都会更新UI)
  • 然后,一种“快速且肮脏”的方法是:

    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
            });
        }
    });
    
    这会将更新委托给一个新的
    Runnable
    ,安排Runnable在FX应用程序线程上执行,并立即退出处理程序。因此,对
    currentBoardPane.setValue(…)
    的调用将在稍后执行,不再是递归的

    事实上,如果你多做一点工作:

    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);
        }
    });
    
    private final Executor aiExecutor=Executors.newSingleThreadExecutor();
    // ...
    currentBoardPane.addListener((e)->{
    如果(gameManager.checkIfCurrentPlayerIsComputer()){
    Task makeMoveTask=新任务(){
    @凌驾
    受保护的无效调用(){
    gameManager.playAutoMovesForCurrentPlayer();
    返回null;
    }
    };
    makeMoveTask.setOnSucceeded(e->{
    //现场更新板
    gameManager.setNextPlayer();//它没有当前玩家属性=下一个玩家
    currentBoardPaneIndex=((currentBoardPaneIndex+1)%gameManager.getPlayers().size());
    currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex));//这是一个递归调用
    });
    aiExecutor.execute(makeMoveTask);
    }
    });
    

    如果计算移动花费了足够的时间,以至于在移动过程中阻止UI是不可接受的,那么这正是您将使用的代码。(如果计算移动只需要很少的时间,这仍然可以正常工作。)这假设
    playAutoMovesForCurrentPlayer()
    不会更新用户界面。

    如果
    playAutoMovesForCurrentPlayer()
    方法花费了很长时间(例如,你在下棋,或者其他一些移动计算量很大的游戏),然后,将对
    任务中的
    PlayAutoMovesforrentPlayer()
    的调用包装起来,在后台线程上执行,并在
    onSucceeded
    处理程序中执行剩余的代码(
    setNextPlayer()
    ,更新索引和窗格)。这将破坏递归,因为处理程序不会直接调用更改。如果你知道移动接近瞬间,这可能不是一个理想的解决方案,但它会解决问题。这是一个简单的游戏,“AI”只是随机选择,所以它是瞬间的。是的,但它仍然解决了问题。如果我理解正确,每次移动都会更新UI,对吗?因此,如果您使用您建议的解决方案(
    while(true){doMoves();}
    ),您必须将其放在后台线程中,并使用
    Platform.runLater(…)
    更新UI,否则您将看不到UI更改(直到循环完成,这似乎永远不会发生)。因此,无论哪种方式,您都需要使用后台线程。while循环将调用使用事件或侦听器更新UI的方法,但它仍然会阻止javafx主线程,直到它完成为止?那么我可能更愿意使用你的第一个建议。如果它在FX应用程序线程上运行,那么是的,它将阻止该线程,直到它完成。(如果不是,您已经创建了一个后台线程,因此无论采用哪种方式,您都可以使用线程。)好的,
    playAutoMovesForCurrentPlayer()
    在控制器中有更新UI的事件或侦听器……所以您可以只使用第一个选项。也许,将计算移动的逻辑(无论多么琐碎)与获取移动表示并用其更新UI的逻辑分离出来会更干净。再次感谢您的帮助。