package ttaomae.connectn;

public class GameManager implements Runnable
{
    private static final int MAX_ATTEMPTS = Integer.MAX_VALUE;
    private Board board;
    private Player playerOne;
    private Player playerTwo;
    private int attemptsAllowed;

    public GameManager(Player playerOne, Player playerTwo)
    {
        this(new Board(), playerOne, playerTwo, MAX_ATTEMPTS);
    }

    public GameManager(Player playerOne, Player playerTwo, int attemptsAllowed)
    {
        this(new Board(), playerOne, playerTwo, attemptsAllowed);
    }

    public GameManager(Board board, Player playerOne, Player playerTwo)
    {
        this(board, playerOne, playerTwo, MAX_ATTEMPTS);
    }

    public GameManager(Board board, Player playerOne, Player playerTwo, int attemptsAllowed)
    {
        if (board == null) {
            throw new IllegalArgumentException("board must not be null");
        }
        if (playerOne == null) {
            throw new IllegalArgumentException("playerOne must not be null");
        }
        if (playerTwo == null) {
            throw new IllegalArgumentException("playerTwo must not be null");
        }
        if (attemptsAllowed <= 0) {
            throw new IllegalArgumentException("attemptsAllowed must be positive");
        }

        this.board = board;
        this.playerOne = playerOne;
        this.playerTwo = playerTwo;
        this.attemptsAllowed = attemptsAllowed;
    }

    @Override
    public void run()
    {
        int attempts = 0;
        while (this.board.getWinner() == Piece.NONE) {
            attempts++;
            int move;
            switch (this.board.getNextPiece()) {
                case BLACK:
                    move = this.playerOne.getMove(this.board.copy());
                    break;
                case RED:
                    move = this.playerTwo.getMove(this.board.copy());
                    break;
                case NONE:
                case DRAW:
                default:
                    move = -1;
                    break;
            }

            // only play valid moves
            if (this.board.isValidMove(move)) {
                this.board.play(move);
                attempts = 0;
            }
            else {
                if (attempts == this.attemptsAllowed) {
                    throw new IllegalMoveException(this.board.getNextPiece() + " attempted "
                                                   + this.attemptsAllowed + " illegal moves");
                }
            }
        }
    }
}

@@ -249,16 +249,78 @@
 lowed;%0A%0A
+    private int move;%0A    private volatile boolean running;%0A%0A%0A
     publ
@@ -1480,16 +1480,46 @@
 llowed;%0A
+        this.running = false;%0A
     %7D%0A%0A 
@@ -1551,32 +1551,61 @@
 oid run()%0A    %7B%0A
+        this.running = true;%0A
         int atte
@@ -1629,16 +1629,32 @@
  while (
+this.running && 
 this.boa
@@ -1721,24 +1721,63 @@
         
-int move
+this.move = -1;%0A            final Player player
 ;%0A      
@@ -1859,36 +1859,38 @@
                 
-move
+player
  = this.playerOn
@@ -1894,35 +1894,8 @@
 rOne
-.getMove(this.board.copy())
 ;%0A  
@@ -1946,16 +1946,201 @@
 e RED:%0A 
+                   player = this.playerTwo;%0A                    break;%0A                case NONE:%0A                case DRAW:%0A                default:%0A                    player = null;%0A
         
@@ -2154,29 +2154,330 @@
     
+ this.
 move = 
-this.
+-1;%0A                    break;%0A            %7D%0A%0A            // use a new thread to get move%0A            Thread myThread = new Thread() %7B%0A                @Override%0A                public void run()%0A                %7B%0A                    if (player != null) %7B%0A                        GameManager.this.move = 
 player
-Two
 .get
@@ -2481,16 +2481,28 @@
 getMove(
+GameManager.
 this.boa
@@ -2513,16 +2513,17 @@
 opy());%0A
+%0A
         
@@ -2538,150 +2538,648 @@
     
-break;%0A                case NONE:%0A                case DRAW:%0A                default:%0A                    move = -1;%0A                    break
+    synchronized (this) %7B%0A                            this.notify();%0A                        %7D%0A                    %7D%0A                %7D%0A            %7D;%0A            myThread.start();%0A%0A            try %7B%0A                synchronized (myThread) %7B%0A                    // wait for thread to get move%0A                    myThread.wait();%0A                %7D%0A            %7D%0A            // if the game manager is interrupted while waiting for a move%0A            catch (InterruptedException e) %7B%0A                // interrupt thread that is getting move%0A                myThread.interrupt();%0A                // end game%0A                this.running = false
 ;%0A  
@@ -3267,16 +3267,21 @@
 lidMove(
+this.
 move)) %7B
@@ -3313,16 +3313,21 @@
 rd.play(
+this.
 move);%0A 
@@ -3670,20 +3670,116 @@
 %7D%0A        %7D%0A
+        this.running = false;%0A    %7D%0A%0A    public void stop()%0A    %7B%0A        this.running = false;%0A
     %7D%0A%7D%0A

update GameManager so that it can be stopped