2016-03-07 1 views
1

J'ai un jeu de tic-tac-toe de serveur client qui essaye d'exécuter un fil différent (dans différents terminaux) pour chaque joueur, que j'ai construit dans l'éclipse.Pourquoi mon fil d'attente ne se réveille-t-il pas même s'il est notifié?

Mon but est de faire bouger chaque joueur, .notifier() l'autre joueur et ensuite .wait() pour que l'autre joueur fasse son mouvement, et alterner ce processus jusqu'à ce que la partie soit terminée.

toSync est l'objet utilisé pour la synchronisation

public static final Object toSync = new Object() 

et il se trouve dans la classe de lecteur (qui se prolonge par deux XPlayer et OPlayer).

Les lignes qui semblent être à l'origine du problème sont commentés dans XPlayer et oPlayer:

Les deux Xplayer et OPlayer ont des méthodes principales, afin qu'elles puissent être exécutées simultanément. X fait le premier mouvement puis utilise un socket pour communiquer ce mouvement au serveur.
Le serveur passe ce déplacement à O, qui effectue ensuite son propre déplacement et le renvoie au serveur. Cela alterne jusqu'à ce que le jeu soit terminé. Faire le premier coup lorsque le joueur x fonctionne bien, mais une fois le mouvement initial effectué, o est supposé afficher le tableau, puis inviter l'utilisateur à se déplacer. Cela ne se produit cependant pas: x fait son mouvement, et o est censé être notifié, mais en réalité ne se réveille jamais. L'accolade qui termine la boucle while qui est commentée dans OPlayer n'est jamais atteinte (ce que je sais être vrai par le débogage que j'ai fait jusqu'ici).

Classe XPlayer:

import java.io.*; 

public class XPlayer 
extends Player 
implements Runnable 
{ 
    public static volatile boolean xTurn = true; 

    public XPlayer() throws IOException 
    { 
     super(); 
     mark = LETTER_X; 
    } 

    public void run() 
    { 
     try 
     { 
      System.out.println("Okay " + name + ", You will be the X-Player"); 

      synchronized(toSync) 
      { 
       Cell move = makeMove(); 
       out.println(move.toString()); 
       board.addMark 
        (move.row(),move.col(),move.mark()); 
       board.display(); 

       xTurn = false; 
       toSync.notifyAll(); //THIS IS THE LINE THAT ISNT WORKING!! 

       System.out.println(WAITING); 
      } 
      synchronized(toSync) 
      { 
       while (!xTurn) 
        {toSync.wait();} 
      } 

      while (!board.isOver()) 
      { 
       synchronized(toSync) 
       { 
        String line; 
        do {line = in.readLine();} 
        while (line == null); 

        Cell opponentMove = Cell.split(line); 
        board.addMark 
         (opponentMove.row(),opponentMove.col(), opponentMove.mark()); 

        String move = makeMove().toString(); 
        out.println(move); 
        xTurn = false; 
        toSync.notifyAll(); 

        while (!xTurn) 
         {toSync.wait();} 
       } 
      } 

      endGame(); 

      sock.close(); 
      in.close(); 
      stdin.close(); 
      out.close(); 

     } catch (InterruptedException ie) 
     { 
      System.out.println("IE IN XPLAYER! " + ie.getMessage()); 
      System.exit(1); 

     } catch (IOException ioe) 
     { 
      System.out.println("IOE IN XPLAYER! " + ioe.getMessage()); 
      System.exit(1); 
     } 
    } 
    public static void main(String[] args) 
    { 
     try 
     { 
      XPlayer x = new XPlayer(); 
      Thread t = new Thread(x); 
      t.start(); 

     } catch(IOException ioe) 
     { 
      System.err.println 
       ("IOE IN XPLAYER MAIN " + ioe.getMessage()); 
      System.exit(1); 
     } 
    } 

Classe OPlayer:

import java.io.*; 

public class OPlayer 
extends Player 
implements Runnable 
{ 

    public OPlayer() throws IOException 
    { 
     super(); 
     mark = LETTER_O; 
    } 

    public void run() 
    { 
     try 
     {  
      synchronized(toSync) 
      { 
       System.out.println("Okay " + name + ", You will be the O-Player"); 

       System.out.println(WAITING); 
       while(!XPlayer.xTurn) 
        {toSync.wait();} // THIS IS THE LINE THAT ISN'T WAKING UP 

       while (!board.isOver()) 
       { 
        String line; 

        do {line = in.readLine();} 
        while (line == null); 

        Cell opponentMove = Cell.split(line); 
        board.addMark 
         (opponentMove.row(),opponentMove.col(),opponentMove.mark()); 

        Cell move = makeMove(); 
        out.println(move.toString()); 
        board.addMark(move.row(),move.col(),move.mark()); 
        board.display(); 
        XPlayer.xTurn = true; 
        toSync.notifyAll(); 

        System.out.println(WAITING); 
        while (XPlayer.xTurn) 
         {toSync.wait();}  
       } 
      } 

      endGame(); 

      sock.close(); 
      in.close(); 
      stdin.close(); 
      out.close(); 

     } catch (InterruptedException ie) 
     { 
      System.out.println("IE IN OPLAYER " + ie.getMessage()); 
      System.exit(1); 

     } catch (IOException ioe) 
     { 
      System.err.println("IOE IN OPLAYER " + ioe.getMessage()); 
      System.exit(1); 
     } 
    } 

    public static void main(String[] args) 
    { 
     try 
     { 
      OPlayer o = new OPlayer(); 
      Thread t = new Thread(o); 
      t.start(); 

     } catch(IOException ioe) 
     { 
      System.err.println("IOE IN OPLAYER MAIN" + ioe.getMessage()); 
      System.exit(1); 
     } 
    } 
} 

Comme indiqué par le code, l'appel toSync.notifyAll() dans XPlayer ne se réveille pas le fil OPlayer, et je suis coincé dans une impasse une fois le premier mouvement a été fait par le XPlayer

Je crois que seulement ces 2 classes sont nécessaires pour résoudre le problème, mais juste au cas où, voici les classes Player Boar d et TTTServer: Classe du joueur:

import java.net.*; 
import java.io.*; 

public class Player 
implements Constants 
{ 
    protected static final Object toSync = new Object(); 

    protected Socket sock; 
    protected BufferedReader stdin; 
    protected BufferedReader in; 
    protected PrintWriter out; 

    protected String name; 
    protected char mark; 
    protected Board board; 

    public Player() throws IOException 
    { 
     sock = new Socket("localhost",1298); 
     stdin = new BufferedReader(new InputStreamReader(System.in)); 
     in = new BufferedReader(new 
       InputStreamReader(sock.getInputStream())); 
     out = new PrintWriter(sock.getOutputStream(),true); 

     System.out.println(WELCOME); 
     System.out.println("Please Enter your name:"); 
     name = stdin.readLine(); 
     board = new Board(); 
    } 

    public Cell makeMove() throws IOException 
    { 
     board.display(); 

     int row = -1; 
     int col = -1; 
     do 
     { 
      while (row < 0 || row > 2) 
      { 
       System.out.println 
        (name + ", what row would you like your next move to be in?"); 
       row = Integer.parseInt(stdin.readLine()); 
       if (row < 0 || row > 2) 
        {System.out.println("Invalid entry! Try again...");} 
      } 
      while (col < 0 || col > 2) 
      { 
       System.out.println 
        (name + ", what column would you like your next move to be in?"); 
       col = Integer.parseInt(stdin.readLine()); 
       if (col < 0 || col > 2) 
        {System.out.println("Invalid entry! Try again...");} 
      } 

      if (board.getMark(row, col) != SPACE_CHAR) 
       {System.out.println("That spot is already taken Try again...");} 

     } while (board.getMark(row,col) != SPACE_CHAR); 

     return new Cell(row,col,mark); 
    } 

    public void endGame() 
    { 
     if (board.xWins() == 1)  {System.out.println(END + XWIN);} 
     if (board.oWins() == 1)  {System.out.println(END + OWIN);} 
     else   {System.out.println(END + " It was a tie!!");} 
    } 
} 

Classe TTTServer:

import java.net.*; 
import java.io.*; 

public class TTTServer 
implements Constants 
{ 
    public static void main(String[] args) 
    { 
     try 
     { 
      ServerSocket ss = new ServerSocket(1298,2); 
      System.out.println("The Server is running..."); 
      Socket sock; 

      Board board = new Board(); 

      sock = ss.accept(); 
      sock = ss.accept(); 

      BufferedReader in = new BufferedReader(new 
        InputStreamReader(sock.getInputStream())); 
      PrintWriter out = new PrintWriter(sock.getOutputStream(),true); 

      do 
      { 
       String moveString; 

       do {moveString = in.readLine();} 
       while (moveString == null); 

       Cell move = Cell.split(moveString); 
       board.addMark(move.row(), move.col(), move.mark()); 

       out.println(moveString); 

      } while(!board.isOver()); 

      in.close(); 
      out.close(); 
      ss.close(); 
      sock.close(); 

     } catch(IOException ioe) 
     { 
      System.out.println("IOE IN TTTSERVER " + ioe.getMessage()); 
      System.exit(1); 
     } 

    } 
} 

Conseil Classe:

public class Board 
implements Constants 
{ 
    /** 
    * A 2D char array stores the game board and 
    * the total number of marks 
    */ 
    private char theBoard[][]; 
    private int markCount; 
    /** 
    * Default constructor initializes the array and fills it with 
    * SPACE_CHARs from the Constants interface 
    */ 
    public Board() 
    { 
     markCount = 0; 
     theBoard = new char[3][]; 
     for (int i = 0; i < 3; i++) { 
      theBoard[i] = new char[3]; 
      for (int j = 0; j < 3; j++) 
       theBoard[i][j] = SPACE_CHAR; 
     } 
    } 
    /** 
    * Getter for the mark at the location specified by the arguments 
    * 
    * @param row 
    * @param column 
    * 
    * @return mark 
    */ 
    public char getMark(int row, int col) 
     {return theBoard[row][col];} 
    /** 
    * Getter for the number of moves which have been made thus far 
    * 
    * @return markCount 
    */ 
    public int getMarkCount()  {return markCount;} 
    /** 
    * @return true if the game is over, otherwise false 
    */ 
    public boolean isOver() 
    { 
     if (xWins() == 1 || oWins() == 1 || isFull()) 
      {return true;} 

     return false; 
    } 
    /** 
    * @return true if the board has been completely filled with 
    * X_CHARs and O_CHARs from Constants interface, else false 
    */ 
    public boolean isFull() 
     {return markCount == 9;} 
    /** 
    * Runs checkWinner on LETTER_X from Constants interface 
    * 
    * @return true if X has won, else false 
    */ 
    public int xWins() 
     {return checkWinner(LETTER_X);} 
    /** 
    * Runs checkWinner on LETTER_O from Constants interface 
    * 
    * @return true if O has won, else false 
    */ 
    public int oWins() 
     {return checkWinner(LETTER_O);} 
    /** 
    * Uses the formatting helper methods to display the board 
    * in the console 
    */ 
    public void display() 
    { 
     displayColumnHeaders(); 
     addHyphens(); 
     for (int row = 0; row < 3; row++) { 
      addSpaces(); 
      System.out.print(" row " + row + ' '); 
      for (int col = 0; col < 3; col++) 
       System.out.print("| " + getMark(row, col) + " "); 
      System.out.println("|"); 
      addSpaces(); 
      addHyphens(); 
     } 
    } 
    /** 
    * Add the mark in the last argument to the location specified by the 
    * first two arguments 
    * 
    * @param row 
    * @param column 
    * @param mark 
    */ 
    public void addMark(int row, int col, char mark) 
    { 
     theBoard[row][col] = mark; 
     markCount++; 
    } 
    /** 
    * Clears the board by replacing all marks with 
    * SPACE_CHARs from the Constants interface 
    */ 
    public void clear() 
    { 
     for (int i = 0; i < 3; i++) 
      for (int j = 0; j < 3; j++) 
       theBoard[i][j] = SPACE_CHAR; 
     markCount = 0; 
    } 
    /** 
    * Checks if the player with the argument mark has won the game 
    * 
    * @param mark 
    * 
    * @return true if the game was won, else false 
    */ 
    int checkWinner(char mark) { 
     int row, col; 
     int result = 0; 

     for (row = 0; result == 0 && row < 3; row++) { 
      int row_result = 1; 
      for (col = 0; row_result == 1 && col < 3; col++) 
       if (theBoard[row][col] != mark) 
        row_result = 0; 
      if (row_result != 0) 
       result = 1; 
     } 


     for (col = 0; result == 0 && col < 3; col++) { 
      int col_result = 1; 
      for (row = 0; col_result != 0 && row < 3; row++) 
       if (theBoard[row][col] != mark) 
        col_result = 0; 
      if (col_result != 0) 
       result = 1; 
     } 

     if (result == 0) { 
      int diag1Result = 1; 
      for (row = 0; diag1Result != 0 && row < 3; row++) 
       if (theBoard[row][row] != mark) 
        diag1Result = 0; 
      if (diag1Result != 0) 
       result = 1; 
     } 
     if (result == 0) { 
      int diag2Result = 1; 
      for (row = 0; diag2Result != 0 && row < 3; row++) 
       if (theBoard[row][3 - 1 - row] != mark) 
        diag2Result = 0; 
      if (diag2Result != 0) 
       result = 1; 
     } 
     return result; 
    } 

    /** 
    * The final three helper methods are called by display 
    * to format the board properly in the console 
    */ 
    void displayColumnHeaders() { 
     System.out.print("   "); 
     for (int j = 0; j < 3; j++) 
      System.out.print("|col " + j); 
     System.out.println(); 
    } 

    void addHyphens() { 
     System.out.print("   "); 
     for (int j = 0; j < 3; j++) 
      System.out.print("+-----"); 
     System.out.println("+"); 
    } 

    void addSpaces() { 
     System.out.print("   "); 
     for (int j = 0; j < 3; j++) 
      System.out.print("|  "); 
     System.out.println("|"); 
    } 
} 
+3

Beaucoup trop de code pour que quelqu'un veuille lire et résoudre (gratuitement :)) – pczeus

Répondre

3

Voici votre erreur:

Les deux Xplayer et OPlayer ont principales méthodes, afin qu'ils puissent être exécuté simultanément.

Si vous exécutez deux méthodes main(), elles ne s'exécutent pas "simultanément"; ils sont des processus entièrement distincts. Cela signifie pas de threads partagés, de variables, d'objets, de notifications, etc.Si vous voulez partager l'état, vous devez commencer tout à partir d'une seule méthode main():

class StarterClass { 
    public static void main(String[] args) 
    { 
     // start XPlayer thread 
     try 
     { 
      XPlayer x = new XPlayer(); 
      Thread t = new Thread(x); 
      t.start(); 

     } catch(IOException ioe) 
     { 
      System.err.println 
       ("IOE IN XPLAYER MAIN " + ioe.getMessage()); 
      System.exit(1); 
     } 

     // start OPlayer thread 
     try 
     { 
      OPlayer o = new OPlayer(); 
      Thread t = new Thread(o); 
      t.start(); 

     } catch(IOException ioe) 
     { 
      System.err.println("IOE IN OPLAYER MAIN" + ioe.getMessage()); 
      System.exit(1); 
     } 
    } 
} 

Si votre intention est d'avoir chaque exécution Player en tant que client séparé tout en tours alternatif, la synchronisation des threads est le mauvais outil pour la emploi. Vous devrez implémenter une messagerie personnalisée entre votre serveur et vos clients pour les synchroniser.

+0

Merci beaucoup pour la clarification, c'était une subtilité à propos de Java que je ne connaissais pas. La seule contrainte stricte sous laquelle je suis est que les deux joueurs doivent être dans deux terminaux ou consoles différents. Comme je l'ai compris, la mise en place d'une fonction principale pour chacun est la seule façon de le faire. Existe-t-il un moyen de mettre en œuvre la façon dont vous avez montré, en démarrant les deux threads à partir d'une méthode principale commune, tout en affichant les x et o dans différentes consoles (en éclipse)? –

+0

Vous voudrez peut-être ajouter qu'ils s'exécutent dans deux JVM séparées qui ne sont pas du tout conscientes l'une de l'autre. –

+0

@SebastianC S'ils ont besoin d'être dans des consoles séparées, cela signifie qu'ils sont supposés fonctionner en tant que clients autonomes. Vous ne pourrez donc pas utiliser la synchronisation de threads. – shmosel