Othello/Reversi
Due 29 Apr
For our third project, you are going to implement a board game on your own.
Reversi is a strategy boardgame often known by the trademarked name "Othello". It is played with two players on an 8x8 grid. The players each start with 32 disks, colored black on one side and white on the other. The goal of the game is to have the most discs of your color facing up when the game ends.
You are going to create a complete program that will allow two players on different computers to compete against each other head-to-head. You are going to create a network connection between the two players, draw the game board, and enforce the rules.
We are not going to give you any code to start this project, and we are not going to explain the full rules of the game. Before you start, it is important that you read the rules at the game manufacturer's official site below, and that you understand how points for the project will be allocated. There are multiple "official" versions of rules for Reversi. In order to reduce ambiguity, we are going to use the formal "Othello" version described below.
Learning Othello
The official rules you need to follow are listed here
Here are some online versions to help you understand the game:
- http://www.mathsisfun.com/games/reversi.html
- http://www.flasharcade.com/puzzle-games/othello-game.html
You can use the following commands on your Linux VM to install a copy of the game that you can run locally:
sudo apt-get install grhino # installs the program grhino # runs the game
Overall Program
Your program must run from a file named "Reversi.java".
The purpose of this program is to allow two people to play Reversi head-to-head over a network connection. Each will be running the program in their own terminal.
Here are some thoughts on architecture:
- This is a "Server&Client" application. The first player starts a server that listens on a port. The second player acts as the client and connects to the port.
- The server is initialized using a command like this: java Reversi -s 12300. The port number (12300 in this case) is a user-supplied value that can change for any game. This command will open a socket on the local machine and wait for another application to connect.
- The client joins the network game with the IP address and port number of the server. e.g. java Reversi -c 10.10.1.4 12300
- Use the ifconfig command to find your IP address in Linux.
- For testing both the client and server on the same machine, you should be able to use the IP address of 127.0.0.1, which always points to the localhost.
- The client process should automatically be designated as the black player and make the first move.
- Play should alternate between the two players. Each player moves by entering a single location to place their piece, e.g. "D3".
- The program should be able to recognize illegal moves or improperly-formatted strings, such as "Z9" or "qwerty". It should tell the user what they did wrong and give them another turn.
- The actual game-engine needs to run only in the server process. The client does not do any game calculations. This is part of "Server&Client" architecture. When the player with the client process makes a move, the process sends the requested move (e.g. "D3") over the network. The server checks that the move is legal, flips pieces, and sends a picture of the new board back to the second player. (Or to be more precise - the unicode text that the client needs to use to draw the board.)
- One player will always be waiting for the other to move. You need to think about how to coordinate the remote messages so that the players know when it is their turn.
- For testing, you should run this program from two different terminal windows on a single machine, one as the server and one as the client. There is no need to test or develop with terminals on two different computers.
You should submit code for the highest step that you achieve in its entirety. Note that these points are not cumulative; that is, if you have a perfect Step 2, you have 20 points (out of 100). You do not get 10 for Step 1 and another 20 for Step 2.
Javadoc is recommended for your personal use, but is not required for points.
The Assignment
Step 1: [10 pts] Usage Statement
Your program parses the command line and prints a usage statement if the user passes the "-h" flag. Sample usage is shown below.
$ java Reversi -h
Usage:
Reversi -s <port> Starts the Server for a networked game on the requested port
e.g. java Reversi -s 12300
Reversi -c <IP> <port> Connects as a Client to an existing game on the requested socket
e.g. java Reversi -c 10.10.1.4 12300
Reversi -h prints this usage
Step 2: [20 pts] Create an initial socket between two processes
This step establishes a basic connection between the client and server processes. It does not need to move any text across the channel, it just needs to verify that connectivity has been achieved.
The "server" and "client" processes are both from code within Reversi.java. They act very differently. When you parse the command-line, you want to check whether a process is a server or client and act accordingly.
The server process opens a socket and starts listening for a connection.
The client process tries to connect to the remote server application.
Once both processes have completed the connection, they each need to print a message to the user saying that the connection is established. The program can end at this point.
The following requirements must be met to get points for this step:
- A player starts the server process from the command-line, e.g. java Reversi -s 12300
- The server process prints a helpful message (e.g. "Server started. Waiting for another player to connect...") to let the player know to wait.
- A second player starts the client process from the command-line and connect to the server, e.g. java Reversi -c 10.10.1.4 12300
- The client process is automatically designated the black player and has the first move.
- When the client first establishes the connection, each user gets a printed message that says:
Game started. You are the (Black||White) player. It is Black's turn.
Step 3: [40 pts] Create a two-way communications channel between the processes
This step builds the necessary logic to let two players alternate typing text to place their game pieces. You will essentially create a Chat Window where only one person can speak at a time.
Assume that the two users are in separate buildings and cannot communicate except through the game. This text channel has to tell them when to move and when to wait.
Only one user can make a move at a time. The other user needs to know whether they are waiting or should be making a move. For this Step, a "move" consists of entering any text. So first the Black player enters text, then the White player, and so forth.
A player should not be able to enter text if it is not their turn.
Players are notified when it is their turn.
Scanner objects can read from either sockets or from the terminal. Since a socket "blocks" the program (pauses the program at the socket.nextLine() method) it is difficult to listen to both the terminal and the socket at the same time.
The best way to implement this is to have two Scanners running in each application - one listening to the terminal and one to the keyboard.
- If it is the client's turn, then the client should listen to the Scanner aimed at the keyboard (ignoring the socket) until the player types some characters and hits enter.
- During the server's turn, the client should listen to the Scanner aimed at the socket (ignoring the keyboard) to hear the server's move.
The server and client need to coordinate their moves so that one is always listening to the keyboard and the other is listening to the socket.
The workflow should look like this:
- The Black player goes first. It remains their turn until they enter some text (Assume they type "D4")
- Once a player enters text, it appears on both players' screens:
e.g.
Black's move: D4
- White then is able to input text at the terminal. Black listens at the socket to hear White's move. They continue to alternate, with only one being able to type at a time.
The following shows an example of what should be displayed on each terminal as the players alternate their moves. On Moves #1 & 3, Only the Black player (client) can enter text. On Moves #2 & 4, only the White player (server) can enter text.
| Move no. | White's Terminal | Black's Terminal |
|---|---|---|
| 1 | Game started. You are the White Player. It is Black's turn... | Game started. You are the Black Player. It is your turn. |
| 2 | Black's move: D4. It is your turn. | Black's move: D4. It is White's turn... |
| 3 | White's move: A1. It is Black's turn... | White's move: A1. It is your turn. |
| 3 | ... | ... |
Step 4: [50 pts] Initial error-checking of moves
This Step validates the move that each player enters to ensure that it is a 2-character entry that corresponds to a valid location on the Reversi board.
Valid codes include "A1" through "H8" and "a1" through h8".
A player's turn ends when they have entered a string that represents a valid space on the board. It does not matter whether that space can actually be played at the time.
The most important aspect of this Step is to correctly coordinate both processes when a player makes an illegal move. The game will stop functioning correctly if both players think they are allowed to move at the same time.
Make sure that your solution to this Step is extensible. You will want to use the same logic for a future Step where you have to check whether a move is within the rules.
HINT - you should put all of the game intelligence into the server process. Have the client send its text to the server to be checked. Do not do it in the client process. This will make things easier later on.
The following requirements must be met to get points for this step:
- If the active player enters any spot on the board from A1 to H8 or a1 to h8, their move will be printed out for both players and their turn will end.
- If the active player types anything else, they will be told they have made an illegal move, and requested to try again.
The following shows an example of what should be displayed on each terminal if one player enters an illegal move. In this case, the White player enters "QWERTY" in Move 2, and has to try again. He correctly enters "A1" the second time around:
| Move no. | White's Terminal | Black's Terminal |
|---|---|---|
| 1 | Game started. You are the White Player. It is Black's turn. | Game started. You are the Black Player. It is your turn. |
| 2 | Black's move: D4. It is your turn. | Black's move: D4. It is White's turn... |
| White entered "QWERTY". That is not a legal move. Try again. | ||
| 3 | White's move: A1. It is Black's turn... | White's move: A1. It is your turn. |
| ... | ... |
Step 5: [60 pts] Graceful exit
This Step will allow either player to end the game. Ending the game means closing the socket and shutting down both processes.
If you do not close the socket correctly, the operating system may think that you are still using it. The port number would be inaccessible to other applications until the computer reboots.
You should call the close() method for both the Socket and any Streams that access it.
Either player can shut down the game by typing /quit during their turn.
The following requirements must be met to get points for this step:
- If either user types /quit, their process sends a message to the other process telling it to shut down.
- Once the message has been sent, each process closes its network socket, InputStreams, and OutputStreams.
- Each process prints a helpful message about what is happening, e.g.: White has quit. Game is over.
- Each process exits.
NOTE - in Linux, you can use the command netstat -lpunt to see what port numbers are open for listening. You can use this to verify whether your ports are being opened and closed.
Step 6: [70 pts] Render the board
Render the basic gameboard and its pieces. Rendering it with unicode characters
as we did with Chess is perfectly acceptable.
The board must be redrawn on each player's terminal at the end of each player's turn.
The players' pieces do not have to be rendered yet for this Step.
You are free to recycle any code you want from the Chess project.
The following requirements must be met to get points for this Step:
- The board must be an 8x8 grid.
- The gridlines must be clearly shown.
- The grid must be labeled "A" though "H" across the top and "1" through "8" down the left side. (As shown in the image to the right)
- The board is redrawn at the end of each player's turn.
- The board is displayed on each player's terminal.
HINT - you should put all of the game-engine into the server process. Do all of the drawing in the server, not the client. At the end of each turn, have the server generate the unicode string that draws the board, and then just send it across the text channel to the client.
Here are some helpful links that show some unicode characters you may want to use for drawing a grid and some disc pieces.
- http://en.wikipedia.org/wiki/Box-drawing_character
- http://en.wikipedia.org/wiki/Geometric_Shapes
Step 7: [80 points] Place pieces in any open space on the board
In this Step, you will implement logic to allow the players to place their colored discs in any open space on the board.
You will need to implement a data structure that holds the location and color of each piece.
You do not have to put your pieces adjacent to an opponent's - any open space is fine.
You do not flip any of your opponent's pieces over in this Step.
The following requirements must be met to get points for this step:
- The players alternate placing pieces.
- A player's turn only ends when they have chosen the location of an open grid on the board.
- The server's internal data structure is updated so that it knows about the new piece.
- If a player chooses a grid location that is already full, they get a helpful error message (e.g. That location is full. Choose another.) It is still their turn.
- The new pieces must be rendered on the board after they are placed.
- The game ends when all 64 locations have been filled. (Or a player types /quit)
Step 8: [95 points] Flip over your opponent's pieces
In this Step, when a player places a disc, you will flip over any of the opponent's pieces that should be flipped according to the rules.
It is not necessary that an opponent make a legal move (i.e. one that will cause discs to flip.) Players may place their discs on any open location.
The following requirements must be met to get points for this step:
- Whenever a player makes a move, all of the opponent discs that should be flipped, are flipped.
- Remember that it is possible for a disc to flip other discs in multiple directions (e.g. to the north and to the east) on a single move.
- The server must update its internal data structure with the new colors of the pieces.
- The rendering of the board must show the newly flipped pieces.
Step 9: [100 points] Legal placement of pieces only
In this Step, you will implement the most important rule in Reversi: a player is only allowed to make a move if they are able to flip over an opponent's piece. Implementing this rule has vast implication on the players' strategy.
You will also start keeping score. Every move, the player's will see a message indicating the last move and the current score. e.g.
White's move: D4 Score: Black 26 White 8. It is your turn.
The following requirements must be met to get points for this step:
- Players can only make a move that results in flipping a disc.
- After each turn, the players get a printout of the last move and the current score.
EXTRA CREDIT*: 5 points each
5 points for each of these bullet points. These will ONLY be considered and awarded points if you have a successful Step 9 project.
- [+5] End a player's turn if no legal moves exist for that player. Implement Rule 2 from the official rules: "If on your turn you cannot outflank and flip at least one opponent disc, your turn is forfeited and your opponent moves again" Be sure to implement Rule 10 with this as well, and end the game if neither player can move.
- [+10] Allow unlimited table-talk. Modify the text channel so that either play can send
a text message to the other at any time during play. Text messages should begin with the
"/" character so they are not interpreted as moves. For example, either player should be
able to type "/All your disc are belong to us" and have it display on the other
player's screen at any time - even if it is not their turn.
This will probably require using threads and multiple sockets.
You may want to plan this one early in your design if you intend to implement it.
- [+3] Trash-talk macro You must complete "table-talk" to do this one. You can score extra points (13 total for table-talk and trash) if you add a /trash mode which allows the user to send a random trash-talk message from a predefined set of messages. You must include at least ten trash messages to get credit. Let's talk some trash!
- [+5] Add sound effects. Have the computer play a sound for each move. Only one sound is required.
- [+3] Creativity You must complete "sound effects" to do this one. You can score extra points (8 total for sound effects) for showing some imagination and having multiple sounds that match the gameplay. At least 5 different effects are required for the extra 3 points. E.g. special sounds for an especially good (or bad) move, or adding tension music when a player is losing badly. Keep it clean. Be sure to explain exactly what you did in the README.txt file.
- [+5] Implementing a Hint. Sometimes a player just does not know what move to make. They want your program to tell them what to do. If a player types "/hint" during their turn, you should tell them the best move. Here is your first piece of AI: Use a Greedy Algorithm to choose a move. This algorithm picks the move that will turn over the most discs. How do you implement that? Iterate over each open space on the board and count how many discs you could flip if you made that move. Then tell the player which spot gives them the highest score. Note that this is not necessarily the best long-term strategy. You can learn some much better ones in SI420.
* Remember - there is no such thing as "Extra Credit". Get all the points you can, whenever you can.
What to hand in
- Write a README.txt that states the highest step you achieved and what extra features you've implemented (for example, if you implemented Hints, tell us so we know and can test for it).
- Submit all of your *.java files, along with README.txt.
- NOTE - do not forget any .java file. We should be able to compile and run with this command: javac Reversi.java && java Reversi -h
- Delete any *.class or other unneeded files prior to submitting
- Delete all the files created by running javadoc.
- All of the above must be in a directory named 'Project3'
- When you run the submit script, the directory and its contents get zipped and sent to your instructor. Ask for help early if you are having problems running the submit script.
Make sure that you run the correct submit script for your instructor:
- CDR Blenkhorn: /courses/blenk/submit Project3
- Dr. Taylor: /courses/taylor/submit Project3
Notes 1:
START EARLY. You cannot write this program in 1-2 sittings. Start work before this weekend so you get a feel for the complexity of this assignment.
Notes 2:
Review the course policy statement on Honor and Projects.
- Projects allow much less collaboration than Labs.
- You are not allowed to look at another student's Project code or discuss how to solve part of a problem - you are on your own to implement this assignment.
- You can ask each other questions to understand what the assignment is asking, such as questions about the rules of Othello/Reversi. But this conversation may not become a discussion about how to implement those rules in Java.
- You are encouraged to seek faculty help whenever you need it. We will be available to answer any programming questions.
Why do we make these rules for Projects? It is true that you can learn a lot about programming by working with another student. That is why we let you collaborate for Labs. The downside is that the relationship is often uneven - one student usually does the bulk of the programming. The weaker student can often skate by an entire semester without really being challenged to program themselves. We insist that everybody program their Projects alone so that we (and you) can identify weak programmers early and get them some extra instruction.
IN SUMMARY:
Asking Professors == OKAY
Asking other students == BAD