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