Sample screen-shots
See Server
TicTacToeClient.java// Fig. 28.13: TicTacToeClient.java
// Client side of client/server Tic-Tac-Toe program.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.Socket;
import java.net.InetAddress;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import java.util.Formatter;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class TicTacToeClient extends JFrame implements Runnable
{
private JTextField idField; // textfield to display player's mark
private JTextArea displayArea; // JTextArea to display output
private JPanel boardPanel; // panel for tic-tac-toe board
private JPanel panel2; // panel to hold board
private Square[][] board; // tic-tac-toe board
private Square currentSquare; // current square
private Socket connection; // connection to server
private Scanner input; // input from server
private Formatter output; // output to server
private String ticTacToeHost; // host name for server
private String myMark; // this client's mark
private boolean myTurn; // determines which client's turn it is
private final String X_MARK = "X"; // mark for first client
private final String O_MARK = "O"; // mark for second client
// set up user-interface and board
public TicTacToeClient(String host)
{
ticTacToeHost = host; // set name of server
displayArea = new JTextArea(4, 30); // set up JTextArea
displayArea.setEditable(false);
add(new JScrollPane(displayArea), BorderLayout.SOUTH);
boardPanel = new JPanel(); // set up panel for squares in board
boardPanel.setLayout(new GridLayout(3, 3, 0, 0));
board = new Square[3][3]; // create board
// loop over the rows in the board
for (int row = 0; row < board.length; row++)
{
// loop over the columns in the board
for (int column = 0; column < board[row].length; column++)
{
// create square
board[row][column] = new Square(" ", row * 3 + column);
boardPanel.add(board[row][column]); // add square
}
}
idField = new JTextField(); // set up textfield
idField.setEditable(false);
add(idField, BorderLayout.NORTH);
panel2 = new JPanel(); // set up panel to contain boardPanel
panel2.add(boardPanel, BorderLayout.CENTER); // add board panel
add(panel2, BorderLayout.CENTER); // add container panel
setSize(300, 225); // set size of window
setVisible(true); // show window
startClient();
}
// start the client thread
public void startClient()
{
try // connect to server and get streams
{
// make connection to server
connection = new Socket(
InetAddress.getByName(ticTacToeHost), 12345);
// get streams for input and output
input = new Scanner(connection.getInputStream());
output = new Formatter(connection.getOutputStream());
}
catch (IOException ioException)
{
ioException.printStackTrace();
}
// create and start worker thread for this client
ExecutorService worker = Executors.newFixedThreadPool(1);
worker.execute(this); // execute client
}
// control thread that allows continuous update of displayArea
public void run()
{
myMark = input.nextLine(); // get player's mark (X or O)
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
// display player's mark
idField.setText("You are player \"" + myMark + "\"");
}
}
);
myTurn = (myMark.equals(X_MARK)); // determine if client's turn
// receive messages sent to client and output them
while (true)
{
if (input.hasNextLine())
processMessage(input.nextLine());
}
}
// 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); // set mark in square
}
else if (message.equals("Invalid move, try again"))
{
displayMessage(message + "\n"); // display invalid move
myTurn = true; // still this client's turn
}
else if (message.equals("Opponent moved"))
{
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark(board[row][column],
(myMark.equals(X_MARK) ? O_MARK : X_MARK)); // mark move
displayMessage("Opponent moved. Your turn.\n");
myTurn = true; // now this client's turn
}
else
displayMessage(message + "\n"); // display the message
}
// manipulate displayArea in event-dispatch thread
private void displayMessage(final String messageToDisplay)
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
displayArea.append(messageToDisplay); // updates output
}
}
);
}
// utility method to set mark on board in event-dispatch thread
private void setMark(final Square squareToMark, final String mark)
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
squareToMark.setMark(mark); // set mark in square
}
}
);
}
// send message to server indicating clicked square
public void sendClickedSquare(int location)
{
// if it is my turn
if (myTurn)
{
output.format("%d\n", location); // send location to server
output.flush();
myTurn = false; // not my turn any more
}
}
// set current Square
public void setCurrentSquare(Square square)
{
currentSquare = square; // set current square to argument
}
// private inner class for the squares on the board
private class Square extends JPanel
{
private String mark; // mark to be drawn in this square
private int location; // location of square
public Square(String squareMark, int squareLocation)
{
mark = squareMark; // set mark for this square
location = squareLocation; // set location of this square
addMouseListener(
new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
setCurrentSquare(Square.this); // set current square
// send location of this square
sendClickedSquare(getSquareLocation());
}
}
);
}
// return preferred size of Square
public Dimension getPreferredSize()
{
return new Dimension(30, 30); // return preferred size
}
// return minimum size of Square
public Dimension getMinimumSize()
{
return getPreferredSize(); // return preferred size
}
// set mark for Square
public void setMark(String newMark)
{
mark = newMark; // set mark of square
repaint(); // repaint square
}
// return Square location
public int getSquareLocation()
{
return location; // return location of square
}
// draw Square
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawRect(0, 0, 29, 29); // draw square
g.drawString(mark, 11, 20); // draw mark
}
}
public static void main(String[] args)
{
TicTacToeClient application; // declare client application
// if no command line args
if (args.length == 0)
application = new TicTacToeClient("127.0.0.1"); // localhost
else
application = new TicTacToeClient(args[0]); // use args
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Maintained by John Loomis, updated Sun Apr 09 17:09:04 2017