我用java和swing(JFrame)完成了一个棋盘游戏(Kamisado)的制作。现在我正在尝试使用客户端 javascrip/typescript (浏览器)进行相同的操作。这个想法是这样的:
while(!isGameOver(board)) {
Player turn = getTurn(board);
Move move = turn.getMove(board);
tryMove(move, board);
}
我有一个抽象类 Player ,具有抽象方法 getMove(Board board)。
所以我可以在主函数中更改播放器的类型。
示例:
Player white = new PlayerAI();
Player black = new PlayerHuman();
PlayerHuman 创建一个视图,人类用户可以在其中单击进行移动,如下所示:
class PlayerHuman extends Player {
View view;
PlayerHuman() {
view = new View();
}
getMove(Board board) {
return view.getMove(board);
}
}
class View {
Move move;
View {
// add even listener which calls click
}
click(Move move) {
this.move = move;
synchronized (monitor)
monitor.notify();
}
getMove(Board board) {
updateView(board);
synchronized (monitor)
monitor.wait();
return move;
}
}
我已经浏览了网站和其他网站上的一些解决方案,但找不到问题的解决方案。
编辑 1:这是我在 Visual Studio 代码、打字稿中已有的内容:
您的 Java 程序似乎基于两个线程,一个线程运行游戏并等待动作作为输入,另一个线程处理事件并创建动作并将它们传递给游戏线程。
虽然这种方法有效,并且适用于简单的游戏,但它相当不寻常 - 特别是在 JavaScript 应用程序是单线程但基于事件的 Web 上。您将有一个
Game
实例作为模型,当单击发生时,单击处理程序将调用 game.makeMove(move)
,然后它会根据当前回合和其他游戏状态执行某些操作,最后告诉视图进行更新。无可否认,这确实需要对游戏进行完全不同的建模,必须将所有状态保留在某种状态机中,而不是将局部变量保留在游戏线程中。
游戏“线程”方法的优点是它可以独立运行 - 与 GUI 线程并行 - 并且可能更快。然而,它也容易出现竞争条件。在您的示例中,当用户单击速度非常快而
tryMove
非常慢时,应用程序可能会错过单击。要正确执行此操作,您需要建立一个队列或移动的通道,以便在线程之间进行通信。
主要缺点是处理多个独立输入相当复杂。如果不仅有可以移动的棋盘,还有用于重置游戏、保存/恢复游戏、撤消上一步、结束或重新启动游戏、交换玩家等的按钮,该怎么办?你可以杀死线程来结束游戏,但它的状态将会丢失。为了允许不同的操作,您需要等待任何一个不同的输入事件发生并以其自己的方式进行处理。此时,您已经重新发明了基于事件的方法:-)
如果你真的愿意的话,仍然可以在 JS 中执行此操作。将游戏代码放入
async
函数和 await
下一步。有关示例,请参阅将“测验”建模为异步“长时间运行的进程”或暂停 javascript 异步函数直到用户操作。不过,停止游戏仍然是一个问题。