Reference no: EM131492970
Project Assignment
Design and implement an online modified version of the game Tic-Tac-Toe using Internet domain sockets. It includes both a game client program and a game server program, these two programs communicate with each other using your own application-layer protocol. Two players can each run the client program to login to a game server and to play a game with each other.
In addition to a single game, you will also implement the support for multiple concurrent games. Two bonus tasks are support for observing games and making comments. The former involves sending game states to all observers, the latter involves concurrent client that can handle input from the server and input from a user at the same time.
At the time of submission, state at the beginning of your documentation which tasks above you are able to complete.
The Modified Tic-Tac-Toc Game
Modified Tic-Tac-Toc is a board game played between two players. The game board is a 3 by 3 grid consisting of nine cells. The player who moves first uses X shaped pieces and the other player uses O shaped pieces.
The two players take turns placing one of their game pieces into an unoccupied cell. The first player to form a row, column, or diagonal with 3 of his pieces is the loser. So the object of the game is to force the opponent into completing 3 in a row.
An example game is as follows:
. . X
|
. .
|
X .
|
.
|
X
|
O
|
.
|
X
|
O
|
.
|
X
|
O
|
.
|
X
|
O
|
X
|
X
|
O
|
X
|
X
|
O
|
X
|
X
|
. . .
|
. .
|
. .
|
X
|
.
|
.
|
X
|
.
|
.
|
X
|
.
|
.
|
X
|
O
|
.
|
X
|
O
|
.
|
X
|
O
|
X
|
X
|
O
|
. . .
|
O .
|
. O
|
.
|
.
|
O
|
.
|
.
|
O
|
.
|
X
|
O
|
.
|
X
|
O
|
.
|
X
|
O
|
O
|
X
|
O
|
O
|
X
|
The game ends in a draw.
Building a single game using the stream sockets
In the first part of the project, there is a game server that supports just one game. At most two players will login at the server and play. At any time, each player is either "available" or "busy". A player always starts with state "available". When a player is playing a game, his state is "busy". The server is started first and waits at a known port for requests from game clients. The port number that the server listens at can be hard-coded, or can be output to the standard output from your program and used when starting client programs. The client program takes two inputs via command line arguments: (i) the name of the machine on which the server program is running, (ii) the port number that the server is listening at.
Once the client program is started, say, by you, the following commands should be supported:
• help: this command takes no argument. It prints a list of supported commands, which are ones in this list. For each command, it prints a brief description of the command function and the syntax of usage.
• login: this command takes one argument, your name. A player name is a userid that uniquely identifies a player. Your name is entered with this command and is sent to the server. The server keeps track of the location of each logged-in player for upcoming message exchanges. Upon receiving a login message, the server records this player in the initial state of "available". For simplicity, no authentication, e.g., via entering a password, is required.
• place: this command issues a move. It takes one argument n, which is between 1 and 9 inclusive. It identify a cell that the player chooses to occupy at this move.
Upon receiving a move, the server checks if it is the player's turn. If so, it checks if the move is legal, i.e., if the indicated cell is unoccupied. If it is not the sender's turn or if the move is illegal, the server replies the sender indicating errors. The sender can then re-issue a move.
If all is well, the new game state is calculated and sent to both players. If 3 in a row is formed, the winner is congratulated and the loser is so informed. If a move takes the last cell without any 3 in a row, the game is a draw, both players are so informed. In both cases, a game is done. Both players' states become "available".
• exit: the player exits the server. It takes no argument. A player can issue this command at any time. After being notified, the server will always acknowledge it, and remove this player from its record. If the player state is currently "busy", the server will inform the other player the game is stopped and change the state of the other player to "available"; the other player can issue an "exit" to exit or wait to start a new game. Once this command is acknowledged by the server, the client program exits.
After a player logs in, if there is currently no other player logged in, the server informs the sender to wait a moment. If another player is currently logged in, the server starts a new game between them, setting their status to "busy". Each player is told the userid of the other player.
When starting a game, the player who logged in first gets to play first. The game state is defined by the current board positions and the turn indicating who is to play next. Game state is maintained by the server and sent to both players initially and after every move. When a game finishes, both players' status remains "busy" while the server automatically starts a new game between them.
Each above command invokes message exchanges between the client and the server except for the first command. The protocol for the communication between the client and server, including the types of messages, the syntax and semantics of each type of message, and the actions taken when each type of message is sent and received at each party, needs to be designed and specified by you. You can refer to the HTTP protocol (RFC2616) and use formats similar to the HTTP request and response messages (Slides 2-26 to 2-31) for the types of messages that you define for this project. For example, you can use methods such as "LOGIN", "PLACE", "EXIT" in the method field of the message that is sent to the server. The player userid and player address can be put in subsequent fields of the same message.
Each message sent to the server should be properly acknowledged by way of response messages. Status code similar to that in the HTTP response messages should be used. At least two status codes and corresponding phrases: 200 OK and 400 ERROR, should be implemented. You are encouraged to define other more explicit status codes and phrases. The protocol that you define must be clearly specified in the written documentation described below. As indicated, the server needs to maintain state of the logged-in players and the state of the game if the game is on-going. You are free to decide how to maintain this state at the server.
Building multiple games
In this second part of the project, there can be multiple games that are ongoing at the game server. Each game has a unique numerical ID assigned by the server. The game server is first started, and listens at a known TCP port. The machine name and port number can be conveyed to the clients in a similar way to that in the single-game case. All commands except the "help" command are the same as before.
The commands that a client can issue additionally (except "help") are as follows:
• help: this is similar to before, except that all commands available in both parts 1 and 2 (minus the "help" command) should be displayed.
• games: this command triggers a query sent to the server. A list of current ongoing games is returned. For each game, the game ID and game players are listed.
• who: this command has no argument. It triggers a query message that is sent to the server; a list of players who are currently logged-in and available to play is retrieved and displayed.
• play: this command takes one argument, the name of a player X you'd like to play a game with. A message is sent to the server indicating that you'd like to play with X. After receiving this message, if X is available, the server starts a new game between you and X. If X is not available, the server replies you that X is no longer available. You may then choose another player instead.
After a player logs in, instead of trying to match up a game as before, the server should not attempt to match up a game among players. Rather each game is started as the result of a "play" command. For each new game started, the player whose "play" message initiates the game is to move first.
There are anomalous scenarios that your implementation should address. For example, if a player invites herself or a non-existent player, an error message should be returned from the server and displayed.
Again, the protocol (message types, syntax of each, etc.) that is used for message exchanges between the client and server should be designed and specified by you. Each message needs to be acknowledged, a response with at least two status codes (200 OK and 400 ERROR) should be returned to the client. The server should endeavour to maintain the up-to-date player and game state information. The way that the server uses to maintain its state information is also your decision. All these need to be documented in the written documentation. You can start with just two games.
Indicate in your documentation how many games your code can support.
Task 1: observing games
This part implements observation of other games. To achieve so, two additional commands that a client may issue are defined as follows:
• observe: this command takes one argument: gameID. It allows a client to observe the game identified with gameID. The gameID can be obtained from the output of the "games" command specified in Part 2. Each time a move in the observed game is made, the new game state is sent to all observers as well as both players.
• unobserve: this command also takes one command-line argument: the gameID. After a client issues this command, the server checks to see if the client is observing the game gameID, if yes, it stops sending the game state to this client. Otherwise, an error message should be returned to the client.
For simplicity, a client may observe only one game at a time.
Task 2: making comments
This part implements support for game commenting by observers. While watching a game, a game user may make comments, which are sent to the server and broadcast to all observers of the same game. When a comment is displayed to each observer, it must be prepended with the userid of the user who made this remark.
You are free to define your own additional commands and protocol message types that are needed.
Concurrency and robustness
Concurrency is an important subject in Computer Science. In this project, you may implement the first single-game part without using concurrency. The second part and the bonus tasks would require concurrency. E.g., a client may want to simultaneously listen to input from the client user and input from the game server. At the server side, it may manage multiple games and different game clients may send messages to the server at the same time. Concurrency support is provided by all languages that are allowed in this project, e.g., a single threaded program using select(), or a multithreaded program. You may choose any support you like.
Robustness is another important aspect of a network application. E.g., while a client is waiting for the player to make a move, the other player's network connection is broken, or the other player exits the game. The client can wait until the player makes a move to inform him, or can inform the player right away. In this project, you do not need to handle extensive robustness scenarios. Instead you should focus on the basic operations mainly.
Development
You may implement the project using Java, C, or Python. You are free to implement any type of player interface that you like, as long as it allows the player to issue the commands specified above. You may run your code on the Linux machines for our socket programming labs or on Windows. In any case, your code must be able to run on a CS Windows lab machines (locally or via ssh to allv).
Python stream socket programming was covered in Lab 2 as well as the textbook. For Java and C socket programming, some simple example programs are provided to you on this page. You are also free to read online tutorials.
As to concurrency programming. For Python, you can refer to the presentation slides and code samples at An Introduction to Python Concurrency, note that you only need to be concerned with Parts 3 and 4 in this tutorial. For Java, you can refer to Java tutorial for concurrency. For C, you may refer to Keir Davis et. al's book chapter, which contains information on concurrent server design.
Additional concurrent programming pointers can be found again here.
On allv, you are responsible to clean up your processes that might be left around running. Orphan processes consume extensive resources, slowing down both others' work and yours. Under Linux, to kill a process, type "ps aux | grep your_id", where your_id is your allv login id, find the process id(s) of the orphan processes, it is in the second column of the ps command output, type "kill -9 pid" to kill the process with process id pid.