Write, using the Java programming language socket interface, a networked Tic Tac Toe game. The game will be a P2P application, and will use TCP for its transport protocol. The game will use port 50000.
The game will a graphical user interface and feature a chat capability. The user interface will be demonstrated in class to give you an idea of what it is supposed to look like.
Although the game is a P2P application, one of the peers will initially play the role of the server, while the other plays the role of the client. A user starting the application will be asked if they want to be a server or a client. The server will set up a passive socket at port 50000 and wait for a connection request from the client. The client side of the application will then ask its user to enter the IP address or DNS name of the server machine. Once this information is supplied by the user, the client will connect to the server and the game will begin.
Here are the requirements of the game.
The game will use the following protocol commands
The user interface consists of 4 major components:
Although not strictly a component, there will be javax.swing.Timer that gener-ates timer events every 101 th of a second. The timer event are used to check the incoming network connection for input from the remote side.
There are to be three event listeners:
I am going to sketch out a design to help you get started. First, we note that the three di erent listeners will all need a lot of the same variables and components, meaning we need what are thinly disguised global variables.
To go with these variables, we need a lot of methods to manipulate them. All this stu will be static members of a class called GameGlobals.
enum PlayerId {LOCAL, REMOTE, NONE}; class GameGlobals { // Game state static Boolean isServer; static boolean isLocalPlayerTurn; static boolean localPlayAgain; // Does local player want to play again? static String localPlayerMarker; static String remotePlayerMarker; static int numberCellsFilled; static boolean isGameOver = false; // Games played. Server goes first when numberGamesCompleted is even, // client goes first when numberGamesCompleted is odd. static int numberGamesCompleted = 0; static int numberGamesWon = 0; static int numberGamesLost = 0; // User Interface Components static final TTTButton[][] tttBoard; // The 9 tic tac toe buttons static final JTextArea chatHistoryTextArea; static final JTextField chatMessageTextField; static final JLabel gameStatusLabel; // Used to announce win or loss // Use a static constructor to create the user interface components static { } // This timer is used to periodically check for network input static Timer netInputMonitorTimer; // Does all start of game initializations and starts the // the netInputMonitorTimer. static void init() { } /** * Process a line of input read sent from the remote side * @param input */ static void processInput(String input) { } /** * Process a remote user move to given row and column * @param row * @param col */ static void processRemoteMove(int row, int col) { } /** * Check to see if a player has won. * @param player * @return */ static boolean hasWon(PlayerId player) { return false; } /** * Process a play again message from the remote player * Note: You get this only after the local player has made his or * her decision, which is then stored in GameGlobals.localPlayAgain? * @param flag */ static void processPlayAgainMessage(boolean flag) { } /** * Process a move by the local player * @param row * @param col */ static void processLocalMove(int row, int col) { } } It is also useful to have a class dedicated to network communications. class NetComm { public static ServerSocket serverSocket; public static Socket clientSocket; public static BufferedReader netReader; // Used to read data from socket public static PrintWriter netWriter; // Used to write data to socket /** * Set up network reader and writer streams after client sockets are connected. * @throws IOException */ private static void setUp() throws IOException { } public static void setUpServer() throws IOException { } public static void setUpClient() throws UnknownHostException, IOException { } }
According to this design, All user interface components will be created in a static block of the GameGlobals class. In your main class, you will need to create a frame, set up the correct layout, and add the already created user interface components to the frame. You can then make the frame visible. Once that is done, you can use a JOptionPane to solicit connection information from the user. Here is the main set up code.
public class TicTacToe { public static void main(String[] args) throws IOException { JFrame frame = new TTTFrame(); // Find out if should be a server or a client String[] possibleValues = { "Tic Tac Toe Server", "Tic Tac Toe Client" }; String selectedValue = (String)JOptionPane.showInputDialog(frame, "Select a Tic Tac Toe Role", "CSC 469/569 Tic Tac Toe", JOptionPane.INFORMATION_MESSAGE, null, possibleValues, possibleValues[0]); System.out.println("You selected " + selectedValue); if (selectedValue.contains("Server")) { NetComm.setUpServer(); } else { NetComm.setUpClient(); } GameGlobals.init(); } } class TTTFrame extends JFrame { public TTTFrame() throws IOException { super("CSC 469 Tic Tac Toe"); JPanel mainPanel = new MainTTTPanel(); this.add(mainPanel); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.pack(); this.setVisible(true); } }
The TTTMainPanel class, not shown, is used to construct the user interface. Once the network connections are in place, the GameGlobals.init() method will initial-ize the game and start the net input monitor timer.
There are two steps to safely handling game continuation. You will realize that the game is over when you are processing a move by either the local or remote player. Take the following steps in order:
The main obstacle to the conversion is the fact that there is no easy way to test a UDP datagram socket to see if data has arrived from the remote side. We will get around this obstacle by creating a separate thread to read network input. This network monitor thread will stay in an in nite loop. During each loop iteration, the thread will issue a blocking call to receive data from the UDP socket. When data arrives, the blocking receive call returns with a newly received DatagramPacket. The thread will extract the message from the DatagramPacket and add the message to a message queue implemented as a Java BlockingQueue object.
In the last assignment, we had a timer listener that monitored the network input stream side of a TCP socket and returned a protocol message when one arrived. We will still have a timer listener. However, this time the timer listener will retrieve messages from the BlockingQueue message queue mentioned above. To determine if there is a message in the queue, the listener can call the isEmpty() method of BlockingQueue.
Note that this change means that we have two threads running concurrently that both need to access the message bu er. The Java BlockingQueue has put() and take() methods to add messages to the bu er, and to remove a message from the bu er. The BlockingQueue is synchronized, meaning that di erent threads can safely call its methods without fear of data corruption.
Here is further information on structuring your code. Your program will need a single UDP Datagram socket. Like before, the server will start running rst and create its socket, with the port number set to 50000. The client will also create its datagram socket, use the connect() method to connect" to the server, and send a protocol message "hello". When the server receives this rst packet, it extracts the client's socket address from the received packet, and uses the connect() method to connect its socket to the client's socket. After this, both sides will be able to send subsequent packets without addressing them.
Once the datagram sockets are connected, the program should create and run the thread for monitoring the datagram sockets for messages from the other side.