aerion
aerion

Reputation: 722

Thread waiting for user to click something

I'm preparing application to play gomoku.

I've created nice looking GUI, now I'm fighting with core engine.

There is a class GameEngine which has synchronized methods to make moves. There are also classes implementing IClient interface which are "players". First thing to do is to implement PlayerClient which is connected to the BoardPanel and moves when MouseListener inBoardPanel (representing gomoku board) sais to. I want my application to be open to implement also computer players. That's why it's so complicated.

So... problem: My GameEngine class looks like this:

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();
        }
    }

}

and my Client class has run() method:

@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();
                }
            }
        }
    }

In place, where I wrote "//waitForPickedMove()" Client should wait for user to click proper place on the board. So... there is a MouseListener in my Board class:

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

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

currentMove is of course move picked by user by clicking board. While movePicked() looks like this:

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

I tried to put wait() in many places. I tried to reverse control and put it into Board class, so it invoked methods in Client class (setCurrentMove(Move move)). Unfortunately I can't reach what I want. I always get IllegalMonitorStateException or my GUI blocks.

I hope I explained my problem enough. I'd be thankful for any help.

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();
                }
            }
        }
    }

}

I'm pretty sure that I should synchronize sth, but I don't really know what.

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();
                }
            }
        }

    }

Upvotes: 0

Views: 504

Answers (3)

Marko Topolnik
Marko Topolnik

Reputation: 200148

There is no need for the Game Engine to constantly occupy a thread, blocking it when there's nothing to do. You should redesign this to be reactive: code executes when there is an event, in the thread where the event handler is executing. Then you won't need any wait-notify mechanisms.

If you want to allow a Player implementation to take its time without occupying the calling thread, design the API with asynchronicity in mind: IClient will have a method representing "other player has moved", which will not return player's move immediately. The IClient will be expected to call a method on the Game Engine with its move.

Upvotes: 4

user1871087
user1871087

Reputation: 31

Hi I would recommend you to you sleep() and notify(). If you want to stop some thread for unknown time give it sleep and when you need it again give it notify().

Upvotes: 0

full.stack.ex
full.stack.ex

Reputation: 1747

Hard to tell exactly what it is: a lot of pieces. But it looks like you should synchronize your notify call as described here:

http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#notify()

Upvotes: 0

Related Questions