The client/server application you will build is a simple two-player cooperative game we will call "warmer/colder." Both players see a grid of buttons (say, 15 by 15). Below the grid are two buttons, labeled "warmer" and "colder." One of the players (the "guider") sees that one of the buttons on the grid has a different color (say, yellow) to indicate that it is the "target" square. However, to the other player (the "guesser") all the grid squares look the same. The game proceeds in turns. At each turn, the guesser picks a new grid square and the guider responds to the choice by selecting either warmer or colder to tell the guesser some information about whether or not he/she is on the right track in finding the target. The object of the game is for the guesser to reach the target in as few guesses as possible. (Note that the guider's response to the first guess may not be very meaningful, since the usual interpretation of warmer and colder is relative to the previous guess. However the players may develop any conventions they want for using the warmer/colder buttons when they come up with a strategy for playing the game.)
In this implementation, both the guider and the guesser will be clients. The server will take care of matching up players as they connect to the server and will forward all communication between them. (Also, that way two Applet clients could connect to the server and play the game because they can both talk to the server (which can forward their messages) even though the Applets can't directly open sockets to each other.)
Also, by structuring the game this way, enhancements could be added to the server to make the game competetive. For example, two pairs of players could be given the same target, and the team that reaches the target in the fewest moves would win. With multiple teams, an elimination tournament could be set up. (However, it is unlikely that this particular game will ever be featured in the Olympics.)
Build a GameBoard class that extends java.awt.Frame and has the following components:
All communication between the players in this game will be accomplished by sending messages between them. You need to define a Message object that will hold the relevant data. Your class should "implement Serializable" so that it can be sent over an ObjectStream across the network. (Recall that the Serializable interface has no methods. It simply tells Java that it's ok to serialize the objects.)
When a player wants to communicate with another player, it will create a Message object, passing to the Message constructor all the information that needs to be communicated. For this particular game, we'll have the following information in the instance variables of each message. (You are welcome to include additional information if you like.) All of these instance variables should either be public or have accessor methods.
In addition to the constructor(s), your Message class should provide three methods:
In this part of the lab, you'll write a client program that will be run by both players. All communication between each clients and the local user will be through an instance of the user interface you constructed in Part I. Communication among the two clients (and the server) will be accomplished through a special protocol using the Message objects you defined in Part II. Communication during game play will proceed as follows. As usual, any omitted details are left up to you.
Some implementation notes:
It is suggested that you implement your client program using the client/server framework presented in class and available on the CS102 Code Examples page. You may choose to implement your client within (or as a subclass of) the GUI you developed in Part I. In that case, you will not be able to user the ClientBehaviorAdapter, but will instead implement all of the methods yourself.
The run() method in your client will basically be a loop that
continuously reads message objects from the ObjectInputStream and processes
them as they arrive. You'll need to be careful about making sure you have
appropriately synchronized access to data that could by accessed by other threads.
In determining how to handle each incoming message,
a switch statement with a separate case for each message type
may be nicer than one long if-then-else statement.
(If you haven't used a switch statement before, see pages 141-143 in Eckel,
or look at the example in the paint() method in the
BoardImage class provided for Lab 3.)
Apart from receiveing messages, most of the rest of the activity of your game will be handled by the ActionListeners you register with the buttons of your user interface. In handling the user interaction, remember to use good user interface design principles. In general, it's a good idea for a user interface to prevent unwanted events from occurring. For example, the guesser's warmer and colder buttons should be disabled, and the guider's grid square buttons should be disabled. Furthermore, the all button press events from the guesser should be ignored between the time the guesser selects a grid square and the repsonse (warmer or colder) is received from the guider. (Disabling all the grid squares at each turn is not recommended, since it is rather slow and ugly. Instead, just let your listener decide whether or not to process the event.)
Write your code carefully and test what you can, but most of the testing will need to wait until after you write the server.
Using the framework presented in class, write a server for the game. It should wait for two clients to connect. Then it should send both of them a NEW_GAME message and a TARGET message with randomly chosen target coordinates. (Be sure to send both clients the same target!) Finally, it should start two threads to forward all messages from one client to the other, as discussed in lecture. The server should also look at each message. It should count the number of guesses made, and when a GAME_OVER message is received, it should print out the guess count (just for demonstration purposes), forward the GAME_OVER message to the other client, and should then send NEW_GAME and TARGET messages to both clients to begin the next game.
To test your application, first start the server program running. Then start up the client application twice. At first, try testing with all three running on the same computer. Once everything is working, try running the two clients on different computers and make sure everything still works.
REMEMBER: When the clients try to connect to the server, they must use the right machine name, which is the one the server is running on. Each time you come into the lab in CEC, you'll probably use a different computer, so remember to update your client code to connect to the right one.