Reputation: 55
I am working on a client/server Tic-Tac-Toe game that consists of one server, and a client that consists of two threads. The entire program includes a TicTacToeServer class, TicTacToeService class, and TicTacToeClientPanel (which is the GUI and the client put together).
The main problem I am facing is within the client class itself. I launch two windows of the GUI/client (for the two different players), and am able to place one marker (X) on the first player. After this, the threads seem to halt, and I am unable to continue playing the game.
If I attempt to put client 1's (player 1) thread to sleep, it sleeps for its given allotment, but client 2's (player 2) thread never begins.
Is there any way that I can alternate between these two threads and go through my program, dependent on which player's turn it is?
import java.awt.*; //Color and GridLayout
import java.awt.event.*;
import java.io.*; //DataInputStream & DataOutputStream
import java.net.Socket;
import java.util.Scanner;
import javax.swing.*; //JPanel & JPanel
import javax.swing.border.LineBorder;
/**
* This is the Main Panel for the TicTacToe Client.
* It uses a displayBoard of Cell objects to display the TicTacToe board
* @author Professor Myers
*
*/
public class TicTacToeClientPanel extends JPanel implements Runnable {
//instance variables and constants
private Cell displayBoard[][] = new Cell[3][3];
private Scanner fromServer;
private PrintWriter out;
private Boolean myTurn, waiting, inputReady;
private Thread thread;
private char mySymbol;
private int rowSelected, columnSelected;
private JLabel statusLabel, playerInfo;
public static final int PLAYER1 = 1, PLAYER2 = 2;
public TicTacToeClientPanel()
{
//give initial values to instance variables
mySymbol = ' ';
myTurn = false;
JPanel sub1 = new JPanel();
playerInfo = new JLabel("");
statusLabel = new JLabel("");
sub1.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//initialize Cells in board array and add to display
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 3; x++)
{
displayBoard[y][x] = new Cell(x+1, y+1);
c.gridx = y;
c.gridy = x;
c.fill = GridBagConstraints.BOTH;
sub1.add(displayBoard[y][x], c);
}
}
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JPanel().add(playerInfo));
add(sub1);
add(new JPanel().add(statusLabel));
connectToServer();
}
private void connectToServer()
{
try
{
//create socket
//set up Scanner and PrintWriter
Socket s = new Socket("localhost", 8880);
InputStream instream = s.getInputStream();
OutputStream outstream = s.getOutputStream();
fromServer = new Scanner(instream);
out = new PrintWriter(outstream);
}
catch (Exception e)
{
System.err.println(e);
}
//start the thread
thread = new Thread(this);
thread.start();
}
public void run()
{
int otherPRow, otherPColumn;
try {
int player = Integer.parseInt(fromServer.nextLine()); //Begin the game
int message = 0;
//set up symbol
//keep track of who's turn it is
//Display the player number and symbol (JLabel)
//Display the status of the player (who's turn is it)
if (player == PLAYER1) {
mySymbol = 'X';
playerInfo.setText("Player 1 with symbol \'X\'");
statusLabel.setText("My turn");
myTurn = true; //player1 goes first
message = Integer.parseInt(fromServer.nextLine());
}
else if (player == PLAYER2) {
mySymbol = 'O';
playerInfo.setText("Player 2 with symbol \'O\'");
statusLabel.setText("Waiting for Player 1 to move");
//what to do with waiting?
myTurn = false;
while (!myTurn) {
if (fromServer.hasNextLine()) {
System.out.println("ITS HAPPENING");
myTurn = true;
thread.setPriority(thread.MAX_PRIORITY);
}
}
}
while(message != 1 && message != 2 && message != 3) //CHANGE TO GAME NOT OVER
{
if(player == PLAYER1)
{
//wait for user to select a cell - sleep for awhile
//"write" the row and column to server
//"read" from the server - perform the appropriate action
//this code is only reached if server passes 5 or 4 to the first player
if (myTurn) {
waiting = true;
while(waiting) {
Thread.sleep(1000);
} //thread sleeps until something is clicked
}
//if this cell value is not empty (WRITE)
if (displayBoard[rowSelected-1][columnSelected-1].getSymbol() != ' ') {
System.out.println("Success");
out.println(rowSelected + '\n' + columnSelected);
out.flush();
statusLabel.setText("Waiting for Player 2 to move");
}
waiting = true;
while (waiting)
Thread.currentThread().sleep(1000);
//READ from server
message = Integer.parseInt(fromServer.nextLine());
if (message == 1) {
statusLabel.setText("I Won! (X)");
return;
}
else if (message == 2) {
//update from player 2's turn
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('O');
statusLabel.setText("Player 2 has won (O)");
return;
}
else if (message == 3) {
//update from player 2's turn
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('O');
statusLabel.setText("Game is over, no winner");
return;
}
else if (message == 4) { //traverses back to beginning of loop
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine()); //What kind does it send? normal or +1?
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('O');
statusLabel.setText("My turn");
myTurn = true;
}
}
else if(player == PLAYER2)
{
//"read" from the server - perform the appropriate action
//wait for the user to select a cell - sleep for a while
//"write" the row and column to server
myTurn = true;
statusLabel.setText("My turn");
message = Integer.parseInt(fromServer.nextLine());
System.out.println(message);
//player1 has won or game is full
if (message == 1 || message == 3) {
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('X');
if (message == 1) {
statusLabel.setText("Player 1 (X) won");
return;
}
else {
statusLabel.setText("Game is over, no winner");
return;
}
}
else if (message == 2) { //player2 has won
statusLabel.setText("I won! (O)");
return;
}
else if (message == 4) {
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('X');
//SLEEP for user input
statusLabel.setText("My turn");
myTurn = true;
waiting = true;
while (waiting) {
Thread.sleep(1000);
}
//WRITE to server
char s = displayBoard[rowSelected-1][columnSelected-1].getSymbol();
if (s != ' ' && s != 'X' && waiting == false) {
out.println(rowSelected + '\n' + columnSelected);
out.flush();
}
statusLabel.setText("Waiting for Player 1 to move");
if (!myTurn)
Thread.currentThread().sleep(10000);
}
}
}
}
catch (Exception e)
{
}
}
public class Cell extends JPanel
{
int row;
int column;
private char symbol;
public Cell(int r, int c)
{
row = r;
column = c;
symbol = ' ';
setBorder(new LineBorder(Color.black,1));
setPreferredSize(new Dimension(100,150));
addMouseListener(new ClickListener());
}
public void setSymbol(char c)
{
symbol = c;
repaint();
}
public char getSymbol()
{
return symbol;
}
protected void paintComponent (Graphics g)
{
super.paintComponent(g);
if(symbol == 'X')
{
g.drawLine(10, 10, getWidth()-10, getHeight()-10);
g.drawLine(getWidth()-10, 10, 10, getHeight()-10);
}
else if(symbol == 'O')
{
g.drawOval(10, 10, getWidth()-10, getHeight()-20);
}
}
private class ClickListener extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{
System.out.println("Clicked: " + row + " " + column);
if(symbol == ' ' && myTurn)
{
setSymbol(mySymbol);
myTurn = false;
rowSelected = row;
columnSelected = column;
statusLabel.setText("Waiting for the other player to move");
waiting = false;
}
}
}
}
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setBounds(0, 0, 1000, 1200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TicTacToeClientPanel ttt = new TicTacToeClientPanel();
frame.getContentPane().add(ttt);
frame.pack();
frame.setVisible(true);
}
}
Upvotes: 2
Views: 844
Reputation: 759
I quickly looked at your code. One tip: the variable waiting is not volatile. Therefore, there is no guarantee that the following loop will ever end:
waiting = true;
while(waiting) {
Thread.sleep(1000);
} //thread sleeps until something is clicked
On a mouse click waiting is set to false. That happens in a different thread. Because the variable waiting is not volatile, the JVM is allowed to optimize the above loop to:
while (true) {
Thread.sleep(1000);
}
Try making waiting volatile. That forces each write to this variable to become visible by other threads. If the variable is not volatile, then each thread may keep its own local copy of this variable.
Upvotes: 1