Я создаю шахматное приложение с графическим интерфейсом, работа которого состоит в том, чтобы отображать доску и фигуры и предотвращать ввод недопустимых ходов.
Он также должен иметь функции, связанные с взаимодействием с шахматным движком (например, stockfish). Это то, с чем я сейчас борюсь. Шахматный движок представляет собой исполняемый файл, доступ к которому осуществляется с помощью ProcessBuilder:
Process chessEngineProcess = new ProcessBuilder(chessEngineUrl).start();
InputStream processInputStream = chessEngineProcess.getInputStream();
OutputStream processOutputStream = chessEngineProcess.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(processOutputStream));
BufferedReader reader = new BufferedReader(new InputStreamReader(processInputStream));
Я хочу отправить строки (команды в протоколе UCI) движку, на который он отвечает, непрерывно выводя текст в течение нескольких секунд или дольше. Это зависает в графическом интерфейсе. Мне нужно обновить textArea (в режиме реального времени) в графическом интерфейсе на основе вывода движка. Это не будет разовая операция. Я хотел бы делать это случайным образом (отправлять команду и обновлять графический интерфейс в режиме реального времени) всякий раз, когда происходят определенные события графического интерфейса (например, пользователь делает ход).
Я знаю, что мне нужно выполнять чтение потока в другом потоке, и я знаю о SwingWorker, но я просто не могу заставить его работать должным образом.
Что я пробовал: поскольку чтение потока является блокирующей операцией (мы продолжаем ждать вывода от движка), поток чтения потока никогда не завершается.
Имея это в виду, я попытался создать класс, который расширяет SwingWorker<Void, String>
и устанавливает и содержит chessEngineProcess
(а также его потоковое чтение и запись) в качестве закрытой переменной-члена. Я реализовал методы doInBackground
и process
. У меня также был публичный метод в этом классе для отправки команды движку.
public void sendCommandToEngine(String command) {
try {
writer.write(command + '\n');
writer.flush();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
}
Я выполняю потоковое чтение в методе doInBackground
, а затем публикую вывод и обновляю графический интерфейс в методе process
.
Это приводит к очень странному поведению, когда я отправляю команды движку из своих классов графического интерфейса (например, из прослушивателей событий). Отображаемый вывод (иногда частично, а иногда и полностью?) неправильный, и часто я получаю исключения.
Я в растерянности и очень отчаянно, пожалуйста, помогите! Это очень важный проект. Не стесняйтесь предлагать любое решение, которое, по вашему мнению, будет работать!
EDIT: я получаю исключение нулевого указателя со следующей трассировкой стека:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at Moves.Move.isMovePossible(Move.java:84)
at Moves.Move.executeMove(Move.java:68)
at gui.ChessBoard.performEngineMove(ChessBoard.java:328)
at gui.MainFrame.receiveEnginesBestMove(MainFrame.java:180)
at gui.EngineWorker.process(EngineWorker.java:91)
at javax.swing.SwingWorker$3.run(SwingWorker.java:414)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:832)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:842)
at javax.swing.Timer.fireActionPerformed(Timer.java:313)
at javax.swing.Timer$DoPostEvent.run(Timer.java:245)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Некоторые подробности: в основном у меня есть класс «MainFrame», который представляет собой JFrame, который содержит все мои элементы графического интерфейса. Здесь я добавляю прослушиватели событий к своим компонентам. В определенных прослушивателях событий я вызываю sendCommandToEngine
. Это запустит заблокированный doInBackground
, когда движок начнет отправлять ответы.
Затем метод process
может вызвать performEnginesMove
для chessBoard
(который является компонентом MainFrame, отображающим шахматную доску), если он обнаружит, что движок вывел «лучший ход».
Функция performEnginesMove
проверяет правильность (возможность) хода и затем делает ход на доске (с помощью класса Move).
По какой-то причине это работает неправильно.
engine.exe < input.txt > output.txt
- person Olivier   schedule 08.03.2020