Modify the TicTacToeServer /client from deitel How to program
We've been given example code from the deitel & deitel book how to program and have to carry out the following alterations:
a) Modify the TicTacToeServer class to test for a win, loss or draw on each move in the game. Send a message to each client applet that indicates the result of the game when the game is over.
b) Modify the TicTacToeClient class to display a button that when clicked allows the client to play another game. The button should be enabled only when a game completes. Note that both class TicTacToeClient and class TicTacToeServer must be modified to reset the board and all state information. Also, the other TicTacToeClient should be notified that a new game is about to begin so its board and state can be reset.
c) Modify the TicTacToeClient class to provide a button that allows a client to terminate the program at any time. When the user clicks the button, the server and the other client should be notified. The server should then wait for a connection from another client so a new game can begin.
d) Modify the TicTacToeClient class and the TicTacToeServer class so the winner of a game can choose game piece X or O for the next game. Remember: X always goes first.
So far I have only been able to do part a) of the exercise im having a lot of difficulty with part b) which allows the client to play a new game.The code ive done so far is listed below if anyone can help i would be forever greatful. Thanks
// Fig. 18.9: TicTacToeClient.java
// Client that let a user play Tic-Tac-Toe with another across a network.
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import javax.swing.*;
public class TicTacToeClient extends JApplet implements Runnable, ActionListener {
private JButton Button;
private JTextField idField;
private JTextArea displayArea;
private JPanel boardPanel, panel2;
private Square board[][], currentSquare;
private Socket connection;
private DataInputStream input;
private DataOutputStream output;
private char myMark;
private boolean myTurn;
private final char X_MARK = 'X', O_MARK = 'O';
boolean done=false;
Container container;
// Set up user-interface and board
public void init()
{
container = getContentPane();
// set up JTextArea to display messages to user
displayArea = new JTextArea( 4, 30 );
displayArea.setEditable( false );
container.add( new JScrollPane( displayArea ), BorderLayout.SOUTH );
// set up panel for squares in board
boardPanel = new JPanel();
boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) );
// create board
board = new Square[ 3 ][ 3 ];
// When creating a Square, the location argument to the constructor
// is a value from 0 to 8 indicating the position of the Square on
// the board. Values 0, 1, and 2 are the first row, values 3, 4,
// and 5 are the second row. Values 6, 7, and 8 are the third row.
for ( int row = 0; row < board.length; row++ ) {
for ( int column = 0; column < board[ row ].length; column++ ) {
// create Square
board[ row ][ column ] = new Square( ' ', row * 3 + column );
boardPanel.add( board[ row ][ column ] );
}
}
// textfield to display player's mark
idField = new JTextField();
idField.setEditable( false );
container.add( idField, BorderLayout.NORTH );
Button = new JButton("New Game");
container.add(Button, BorderLayout.BEFORE_FIRST_LINE);
// set up panel to contain boardPanel (for layout purposes)
panel2 = new JPanel();
panel2.add( boardPanel, BorderLayout.CENTER );
container.add( panel2, BorderLayout.CENTER );
} // end method init
// Make connection to server and get associated streams.
// Start separate thread to allow this applet to
// continually update its output in textarea display.
public void start()
{
// connect to server, get streams and start outputThread
try {
// make connection
connection = new Socket( getCodeBase().getHost(), 12345 );
// get streams
input = new DataInputStream( connection.getInputStream() );
output = new DataOutputStream( connection.getOutputStream() );
}
// catch problems setting up connection and streams
catch ( IOException ioException ) {
ioException.printStackTrace();
}
// create and start output thread
Thread outputThread = new Thread( this );
outputThread.start();
} // end method start
// control thread that allows continuous update of displayArea
public void run()
{
// get player's mark (X or O)
try {
myMark = input.readChar();
// display player ID in event-dispatch thread
SwingUtilities.invokeLater(
new Runnable() {
public void run()
{
idField.setText( "You are player \"" + myMark + "\"" );
}
}
);
myTurn = ( myMark == X_MARK ? true : false );
// receive messages sent to client and output them
while ( !done) {
processMessage( input.readUTF() );
}
} // end try
// process problems communicating with server
catch ( IOException ioException ) {
ioException.printStackTrace();
}
} // end method run
// process messages received by client
private void processMessage( String message )
{
// valid move occurred
if ( message.equals( "Valid move." ) ) {
displayMessage( "Valid move, please wait.\n" );
setMark( currentSquare, myMark );
}
// invalid move occurred
else if ( message.equals( "Invalid move, try again" ) ) {
displayMessage( message + "\n" );
myTurn = true;
}
// opponent moved
else if ( message.equals( "Opponent moved" ) ) {
// get move location and update board
try {
int location = input.readInt();
int row = location / 3;
int column = location % 3;
setMark( board[ row ][ column ],
( myMark == X_MARK ? O_MARK : X_MARK ) );
displayMessage( "Opponent moved. Your turn.\n" );
myTurn = true;
} // end try
// process problems communicating with server
catch ( IOException ioException ) {
ioException.printStackTrace();
}
} // end else if
else if ( message.equals("Game Over") ){
displayMessage("**** sake" );
}
// simply display message
else
displayMessage( message + "\n" );
} // end method processMessage
// utility method called from other threads to manipulate
// outputArea in the event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
// display message from event-dispatch thread of execution
SwingUtilities.invokeLater(
new Runnable() { // inner class to ensure GUI updates properly
public void run() // updates displayArea
{
displayArea.append( messageToDisplay );
displayArea.setCaretPosition(
displayArea.getText().length() );
}
} // end inner class
); // end call to SwingUtilities.invokeLater
}
// utility method to set mark on board in event-dispatch thread
private void setMark( final Square squareToMark, final char mark )
{
SwingUtilities.invokeLater(
new Runnable() {
public void run()
{
squareToMark.setMark( mark );
}
}
);
}
// send message to server indicating clicked square
public void sendClickedSquare( int location )
{
if ( myTurn ) {
// send location to server
try {
output.writeInt( location );
myTurn = false;
}
// process problems communicating with server
catch ( IOException ioException ) {
ioException.printStackTrace();
}
}
}
// set current Square
public void setCurrentSquare( Square square )
{
currentSquare = square;
}
public void actionPerformed(ActionEvent e) {
// I need code here
}
// private inner class for the squares on the board
private class Square extends JPanel {
private char mark;
private int location;
public Square( char squareMark, int squareLocation )
{
mark = squareMark;
location = squareLocation;
addMouseListener(
new MouseAdapter() {
public void mouseReleased( MouseEvent e )
{
setCurrentSquare( Square.this );
sendClickedSquare( getSquareLocation() );
}
}
);
} // end Square constructor
// return preferred size of Square
public Dimension getPreferredSize()
{
return new Dimension( 30, 30 );
}
// return minimum size of Square
public Dimension getMinimumSize()
{
return getPreferredSize();
}
// set mark for Square
public void setMark( char newMark )
{
mark = newMark;
repaint();
}
// return Square location
public int getSquareLocation()
{
return location;
}
// draw Square
public void paintComponent( Graphics g )
{
super.paintComponent( g );
g.drawRect( 0, 0, 29, 29 );
g.drawString( String.valueOf( mark ), 11, 20 );
}
} // end inner-class Square
} // end class TicTacToeClient
**********************************************************************
// Fig. 18.8: TicTacToeServer.java
// This class maintains a game of Tic-Tac-Toe for two client applets.
import java.awt.*;
import java.net.*;
import java.io.*;
import javax.swing.*;
public class TicTacToeServer extends JFrame {
private char[] board;
private JTextArea outputArea;
private Player[] players;
private ServerSocket server;
private int currentPlayer;
private final int PLAYER_X = 0, PLAYER_O = 1;
private final char X_MARK = 'X', O_MARK = 'O';
int moves=0;
// set up tic-tac-toe server and GUI that displays messages
public TicTacToeServer()
{
super( "Tic-Tac-Toe Server" );
board = new char[ 9 ];
for (int i = 0; i<9; i++){
board = ' ';
}
players = new Player[ 2 ];
currentPlayer = PLAYER_X;
// set up ServerSocket
try {
server = new ServerSocket( 12345, 2 );
}
// process problems creating ServerSocket
catch( IOException ioException ) {
ioException.printStackTrace();
System.exit( 1 );
}
// set up JTextArea to display messages during execution
outputArea = new JTextArea();
getContentPane().add( outputArea, BorderLayout.CENTER );
outputArea.setText( "Server awaiting connections\n" );
setSize( 300, 300 );
setVisible( true );
} // end TicTacToeServer constructor
// wait for two connections so game can be played
public void execute()
{
// wait for each client to connect
for ( int i = 0; i < players.length; i++ ) {
// wait for connection, create Player, start thread
try {
players[ i ] = new Player( server.accept(), i );
players[ i ].start();
}
// process problems receiving connection from client
catch( IOException ioException ) {
ioException.printStackTrace();
System.exit( 1 );
}
}
// Player X is suspended until Player O connects.
// Resume player X now.
synchronized ( players[ PLAYER_X ] ) {
players[ PLAYER_X ].setSuspended( false );
players[ PLAYER_X ].notify();
}
} // end method execute
// utility method called from other threads to manipulate
// outputArea in the event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
// display message from event-dispatch thread of execution
SwingUtilities.invokeLater(
new Runnable() { // inner class to ensure GUI updates properly
public void run() // updates outputArea
{
outputArea.append( messageToDisplay );
outputArea.setCaretPosition(
outputArea.getText().length() );
}
} // end inner class
); // end call to SwingUtilities.invokeLater
}
// Determine if a move is valid. This method is synchronized because
// only one move can be made at a time.
public synchronized boolean validateAndMove( int location, int player )
{
boolean moveDone = false;
// while not current player, must wait for turn
while ( player != currentPlayer ) {
// wait for turn
try {
wait();
}
// catch wait interruptions
catch( InterruptedException interruptedException ) {
interruptedException.printStackTrace();
}
}
// if location not occupied, make move
if ( !isOccupied( location ) ) {
// set move in board array
board[ location ] = currentPlayer == PLAYER_X ? X_MARK : O_MARK;
// change current player
currentPlayer = ( currentPlayer + 1 ) % 2;
// let new current player know that move occurred
players[ currentPlayer ].otherPlayerMoved( location );
notify(); // tell waiting player to continue
// tell player that made move that the move was valid
return true;
}
// tell player that made move that the move was not valid
else
return false;
} // end method validateAndMove
// determine whether location is occupied
public boolean isOccupied( int location )
{
if ( board[ location ] == X_MARK || board [ location ] == O_MARK )
return true;
else
return false;
}
void gameOverCheck() {
boolean over = false;
// Check rows
for (int start=0; start < 9; start += 3)
over = over || ((board[start] != ' ') &&
(board[start] == board[start+1]) &&
(board[start] == board[start+2]));
// Check columns
for (int start=0; start < 3; start++)
over = over || ((board[start] != ' ') &&
(board[start] == board[start+3]) &&
(board[start] == board[start+6]));
// Check diagonals
over = over || ((board[0] != ' ') &&
(board[0] == board[4]) && (board[0] == board[8]));
over = over || ((board[2] != ' ') &&
(board[2] == board[4]) && (board[2] == board[6]));
if (over || (moves>=9))
for ( int i = 0; i < players.length; i++ )
players.gameOver(over ? (currentPlayer+1)%2 : -1 );
}
public static void main( String args[] )
{
TicTacToeServer application = new TicTacToeServer();
application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
application.execute();
}
// private inner class Player manages each Player as a thread
private class Player extends Thread {
private Socket connection;
private DataInputStream input;
private DataOutputStream output;
private int playerNumber;
private char mark;
protected boolean suspended = true;
boolean done = false;
// set up Player thread
public Player( Socket socket, int number )
{
playerNumber = number;
// specify player's mark
mark = ( playerNumber == PLAYER_X ? X_MARK : O_MARK );
connection = socket;
// obtain streams from Socket
try {
input = new DataInputStream( connection.getInputStream() );
output = new DataOutputStream( connection.getOutputStream() );
}
// process problems getting streams
catch( IOException ioException ) {
ioException.printStackTrace();
System.exit( 1 );
}
} // end Player constructor
// send message that other player moved
public void otherPlayerMoved( int location )
{
// send message indicating move
try {
output.writeUTF( "Opponent moved" );
output.writeInt( location );
}
// process problems sending message
catch ( IOException ioException ) {
ioException.printStackTrace();
}
}
void gameOver(int winner) {
try {
output.writeUTF( "Game Over. " +
(( winner == -1 ) ? "No winner." :
( "Winner is " + ( winner == 0 ? 'X' : 'O' ) + "." ) ) );
done=true;
output.writeUTF("Game Over");
} catch( IOException e ) {
e.printStackTrace(); System.exit( 1 );
}
}
public void run()
{
// send client message indicating its mark (X or O),
// process messages from client
try {
displayMessage( "Player " + ( playerNumber ==
PLAYER_X ? X_MARK : O_MARK ) + " connected\n" );
output.writeChar( mark ); // send player's mark
// send message indicating connection
output.writeUTF( "Player " + ( playerNumber == PLAYER_X ?
"X connected\n" : "O connected, please wait\n" ) );
// if player X, wait for another player to arrive
if ( mark == X_MARK ) {
output.writeUTF( "Waiting for another player" );
// wait for player O
try {
synchronized( this ) {
while ( suspended )
wait();
}
}
// process interruptions while waiting
catch ( InterruptedException exception ) {
exception.printStackTrace();
}
// send message that other player connected and
// player X can make a move
output.writeUTF( "Other player connected. Your move." );
}
while ( ! done ) {
// get move location from client
int location = input.readInt();
// check for valid move
if ( validateAndMove( location, playerNumber ) ) {
displayMessage( "\nlocation: " + location );
output.writeUTF( "Valid move." );
gameOverCheck();
}
else
output.writeUTF( "Invalid move, try again" );
}
// close connection to client
} // end try
// process problems communicating with client
catch( IOException ioException ) {
ioException.printStackTrace();
System.exit( 1 );
}
}
// end method run
// set whether or not thread is suspended
public void setSuspended( boolean status )
{
suspended = status;
}
} // end class Player
} // end class TicTacToeServer

