2012-12-07 2 views
0

Я готовлю приложение для игры в гомоку.Нить, ожидающая щелчка пользователя

Я создал красивый графический интерфейс, теперь я сражаюсь с основным двигателем.

Существует класс GameEngine, который имеет синхронизированные методы для совершения ходов. Существуют также классы, реализующие интерфейс IClient, которые являются «игроками». Первое, что нужно сделать, это осуществить PlayerClient, который подключен к BoardPanel и перемещается, когда MouseListener в BoardPanel (представляющий гомоку). Я хочу, чтобы мое приложение было открытым для внедрения и компьютерных игроков. Вот почему это так сложно.

Итак ... проблема: Мой GameEngine класс выглядит следующим образом:

public class GameEngine { 
    private IClient blackPlayer; 
    private IClient whitePlayer; 
    private GameState gameState; 

    ... 

    public void play() { 
     blackPlayer.run(); 
     whitePlayer.run(); 
    } 

    public synchronized void move(Move move) throws IllegalMoveException { 
     gameState.move(move); 
     if (move.isBlackMove()){ 
      whitePlayer.notify(); 
     }else{ 
      blackPlayer.notify(); 
     } 
    } 

    public synchronized void waitForYourTurn(boolean color){ 
     try { 
      while (gameState.isBlackToMove()!=color) { 
       wait(); 
      } 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

и мой класс Клиент запуска() метод:

@Override 
    public void run() { 
     while (gameState.getWinner() == 0) { 
      move = null; 
      boolean moved = false; 
      while (!moved) { 
       gameEngine.waitForYourTurn(black); 
       try { 
        //waitForPickedMove(); 
        gameEngine.move(move); 
        moved = true; 
       } catch (IllegalMoveException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

В месте, где я написал «//waitForPickedMove() «Клиент должен дождаться, когда пользователь наберет правильное место на доске. Итак ... есть MouseListener в моем классе Board:

private class MoveMouseListener implements MouseListener { 
     @Override 
     public synchronized void mouseReleased(MouseEvent e) { 
      if (gameState == null) { 
       return; 
      } 

      if (currentMove != null) { 
       movePicked(); 
       repaint(); 
      } 
     } 
    } 

currentMove, конечно, двигаться подобран пользователем, нажав плату. В то время как movePicked() выглядит следующим образом:

private synchronized void movePicked() { 
    if (gameState.isBlackToMove() && blackClient != null) { 
     blackClient.notify(); 
     clearCurrentMove(); 
    } 
    if (!gameState.isBlackToMove() && whiteClient != null) { 
     whiteClient.notify(); 
     clearCurrentMove(); 
    } 
} 

Я пытался поставить wait() во многих местах. Я попытался изменить управление и поместить его в класс Board, поэтому он вызывает методы в классе Client (setCurrentMove (Move move)). К сожалению, я не могу достичь того, чего хочу. Я всегда получаю IllegalMonitorStateException или мои графические блоки.

Надеюсь, я объяснил свою проблему достаточно. Я был бы благодарен за любую помощь.

EDIT:

public class MockComputerClient implements IClient { 
    private GameState gameState; 
    private GameEngine gameEngine; 
    private boolean black; 
    private ExecutorService executorService; 
    private boolean myTurn = false; 
    private int i = 3; 

    public MockComputerClient(GameEngine gameEngine, GameState gameState, 
      boolean black) { 
     this.gameEngine = gameEngine; 
     this.gameState = gameState; 
     this.black = black; 
     executorService = new ExecutorService(); 
     executorService.run(); 
    } 

    @Override 
    public void enemyMoved() { 
     myTurn = true; 
     executorService.notify(); 
    } 

    private class ExecutorService implements Runnable { 

     @Override 
     public void run() { 
      while (gameState.getWinner() == 0) { 
       try { 
        while (!myTurn) { 
         wait(); 
        } 
        // Hardcoded Moves 
        gameEngine.move(new Move(black, 3, i++)); 
        myTurn = false; 
       } catch (InterruptedException | IllegalMoveException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

} 

Я уверен, что я должен синхронизировать STH, но я действительно не знаю, что.

EDIT 2:

@Override 
    public void enemyMoved() { 
     myTurn = true; 
     executorService.move(); 
    } 

    private class ExecutorService implements Runnable { 

     @Override 
     public void run() { 
      while (gameState.getWinner() == 0) { 
       System.out.println("Calculating: j= " + j); 
       if (j == 3) 
        j = 4; 
       else 
        j = 3; 

       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 

      } 
     } 

     public void move() { 
      while (myTurn) { 
       try { 
        gameEngine.move(new Move(black, j, i++)); 
        myTurn = false; 
       } catch (IllegalMoveException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

    } 
+0

* «Вот почему это так сложно». * Чтобы лучше помочь, отправьте [SSCCE] (http://sscce.org/) вашу лучшую попытку только с многопоточными и щелчками мыши. –

+0

Свинг. Ну, это большое приложение с множеством классов. Я пытался вырезать важные вещи. Многопоточность между игрой и клиентами отлично работает. Я попытался сделать hardcode this currentMove. Проблема возникает, когда я пытаюсь подождать GUI. Я просто не уверен, что следует ждать, когда и где его следует уведомить. Так что я не знаю, как это сделать: Переместить waitForPickedMove() и уведомление как-то ... не так вот в movePicked() – aerion

ответ

4

Игроку не требуется постоянно занимать нить, блокируя ее, когда вам нечего делать. Вы должны переделать это, чтобы быть реактивным: код выполняется, когда есть событие, в потоке, где выполняется обработчик событий. Тогда вам не понадобятся никакие механизмы wait - notify.

Если вы хотите, чтобы реализация плеера выполняла свое время, не занимая вызывающий поток, проектируйте API с учетом асинхронности: IClient будет иметь метод, представляющий «перемещенный другой игрок», который не будет немедленно возвращать игроку , Ожидается, что IClient вызовет метод на Game Engine с его перемещением.

+0

Не могли бы вы показать мне простой пример вашего метода? Думаю, я этого не понимаю. Многопоточность затруднена, пока вы не поймете это очень хорошо. :) Я имею в виду, например, простое приложение, в котором два потока ожидают друг друга, и каждый из них ждет нажатия мыши и, например, записывает координаты курсора на консоль. – aerion

+0

Ваш проект a-priori соединяет игрока с нитью, это то, что неправильно. Поместите метод в IClient, который вызывается, когда противоположный игрок совершил движение; это может быть метод «void» (классический обратный вызов). Также у Game Engine есть метод обратного вызова, который получает переход от Игрока. Человеческому игроку не нужен нить, он отвечает на действия в GUI. Компьютерный проигрыватель также не является нитью: он может ** использовать поток внутри ** для выполнения своей обработки. Когда вызывается метод «противоположный игрок», он отправляет задачу в ExecutorService, который вычисляет ... –

+0

... перемещение и вызывает соответствующий метод игры с его перемещением. –

0

Привет, я бы порекомендовал вам сон() и уведомить(). Если вы хотите остановить какой-либо поток в течение неизвестного времени, дайте ему спать, и когда вам это нужно, снова сообщите ему уведомление().

+0

Спасибо, но здесь все не так просто. :) – aerion

Смежные вопросы