ICS 32A Fall 2019
Project #2: Send Me On My Way

Due date and time: Friday, November 1, 11:59pm

This project can either done using "pair programming" or individually


Background

There are relatively few interesting programs written in any programming language that are completely self-contained; almost any program you can think of that would fit the description of being "interesting" in some way will either read its input from or write its output to some source external to the program. This is what allows us to use the same program to solve different problems — albeit different problems of the same type — and to use those solutions in other programs.

You've no doubt seen, both in previous coursework and in this course, that one way for a program to take external input is to read it from a file. This is the principle at work when you start a word processor like Microsoft Word by double-clicking on a document stored on your hard drive: The word processor is opened, then it reads the document and displays its contents, along with whatever formatting or art is included within it. Of course, word processors would be much less useful if they were incapable of opening existing documents.

However, files aren't where the story ends. Programs are capable of reading input and writing output in other ways, too, and learning how to use other mechanisms in Python programs pushes out the boundaries around what we can accomplish. Think about the programs you use every day; it doesn't take long to realize that the ones that hold your interest most strongly, that enable the most exciting outcomes, are those that read their input and write their output by connecting to other computers somewhere else in the world via the Internet. We use many programs every day that do this: web browsers, email clients, mobile applications, multiplayer games, and more. So we should want to be able to do the same in the programs we write in Python.

This project allows you to take a first step into a more connected world by introducing you to the use of sockets, objects in Python that represent one end of a connection between one program and another — the other might be on the same machine, on another machine in the same room, or on a machine halfway across the world. You'll learn about the importance of protocols in the communication between programs, and will implement a game that you can play either standalone (on your own computer) or via your Internet connection.

Along the way, you'll also be introduced in more detail to the use of modules in Python, and to writing programs that are made up of more than one module, a technique that we'll revisit repeatedly as the size and complexity of the programs we write begins to increase. You'll find that the design decisions you make, such as keeping functions small and self-contained, organizing your functions and other code by putting it into appropriate modules, will be an important part of being able to complete your work. Additionally, you'll use a small library that I'm providing in order to seed your work on the project.

Be sure to look through the notes and examples related to sockets and the Internet as we continue covering these topics in lecture; they will provide the background that you'll need in order to implement that part of your program.


Pair programming (optional)

You have your option of working on this project either using pair programming or individually. Note that pair programming provide some significant benefits, and we generally recommend that you work with a partner unless you have a specific reason you can't easily do it, but there are a few things you should be aware of.

What to do if you want a partner

During your lab section on Monday, October 21, choose a partner from among the students who are officially enrolled in the same lab section as you. It's fine, even preferable, to read this project write-up on your own ahead of time, though, so you and your partner can hit the ground running when you start working together, but do not start working on your solution until you are partnered up. (We understand that you might be eager, but the goal here is to take a shared journey with someone else, not to arrive on the first day and say "Okay, we're already done!")

Once you've selected a partner, notify your TA of your partnership during the lab section. Assuming you're both enrolled in that section, your TA will approve the partnership and make a note of it, at which time you're officially partners! (Until you've received this approval, you are not yet partners.)

For those of you who are unable to attend lab on Monday, October 21, there's a backup plan if you want a partner. Notify your TA via email — contact information for the TAs of each lab section is in the Course Reference — that you will not be attending lab, but that you would still like a partner.

After labs meet on Monday, October 21, your TA will randomly select partnerships from among the students who did not attend, but who did want a partner, and will notify you and your new partner via email. Once your TA has selected a partner for you in this fashion, we will not allow you to switch to another one, so the best way to control your destiny is to choose a partner yourself during your lab section on Monday, October 21.

If you're having trouble finding a partner, notify your TA during your lab section, so that you can be assisted in finding one.

What to do if you do not want a partner

Nothing. Entering a partnership requires a small amount of action on your part — notifying your TA during lab, or emailing your TA beforehand and expressing an interest in a partner. Inaction means that you'll be working alone on this project.

Is there any kind of penalty for working alone?

No. The way we grade your work is the same, and the due date is the same, regardless of whether you work with a partner or alone. Partnerships are their own reward.


The program

For this project, you'll implement a console-based game that you will initially be able to play on your own computer, but will extend so that you can play via the Internet by connecting to a central, shared server.

The game

For this project, you'll implement a console-based implementation of a game called Connect Four. The rules of the game are straightforward and many of you may already know them; if you're not familiar with the rules of the game, or haven't seen them in a while, Wikipedia's Connect Four page is as good a place to go as any to become familiar with it.

Note that our implementation will include not only the traditional rules regarding dropping pieces into columns, but also the "Pop Out" variation discussed on the Wikipedia page. While Connect Four boards come in a variety of sizes, our implementation will default to 7x6 (i.e., seven columns and six rows).

Also, one very minor wrinkle that we're adding to the rules on the Wikipedia page is that the red player always moves first.

A starting point: the connectfour module

Unlike the previous project, this project begins with a starting point, in the form of a library that I've already implemented that contains the underlying game logic. You will be required to build your game on top of it (and you will not be allowed to change it), which will be an instructive experience; learning to use other people's libraries without having to make modifications (and, in a lot of cases, without being allowed to make them) is a valuable real-world programming ability. Before proceeding much further with the project, it might be a good idea to spend some time reading through the code, its docstrings, and its comments to get an understanding of what's been provided. You can also try some focused experimentation in the Python interpreter so you can understand how the provided module works; you'll need that understanding in order to complete your work. Don't worry if not all of it makes complete sense initially, but do get a feel for what's available at the outset, then gradually fill in the details as you move forward.

The Connect Four game logic is linked below:

Be sure that you respect the constants that are defined in this module. For example, whenever possible, use connectfour.BOARD_COLUMNS to denote the number of columns on the board, connectfour.BOARD_ROWS to denote the number of rows on the board, and so on. It should be possible to change the values of these constants to reasonable alternative values and your program should still work (and should adjust if, for example, the number of columns and rows changes).

The requirements

You will actually be writing two programs to satisfy this project's requirements. The vast majority of the code will be shared between the two programs. In fact, one of your goals is to find a way to reuse as much of it as possible; if you find yourself copying and pasting code between the programs, you should instead consider a way to use the put the code in one place and use it in both.

The first program: a console-only version of Connect Four

One of your two programs will allow you to play one game of Connect Four using only console interaction (i.e., no networks or sockets). The user is repeatedly shown the current state of the game — whose turn it is and what the board looks like. The board should always be shown in the following format:

1  2  3  4  5  6  7
.  .  .  .  .  .  .
.  .  .  .  .  .  .
.  .  .  .  .  .  .
.  .  R  .  .  .  .
.  .  Y  R  .  .  .
.  R  R  Y  .  Y  .

You have some latitude in how you ask the user to specify a move, but it needs to be clear what the user should do. Do not assume that your user will know what to type; tell them (briefly). Columns should be selected by typing a number between 1 (the far-left column) and 7 (the far-right).

When the user is asked to specify a move but an invalid one is specified (such as dropping into a column that is full), an error message should be printed and the user should be asked again to specify a move. In general, erroneous input at the console should not cause the program to crash; it should simply cause the user to be asked to specify his or her move again.

The game proceeds, one move at a time, until the game ends, at which point the program ends.

The second program: a networked version of Connect Four

Your second program will instead allow you to play a game of Connect Four via a network, by connecting to a server that I've provided. Your program always acts as a client.

When this program starts, the user will need to specify the host (either an IP address, such as 192.168.1.101, or a hostname, such as www.ics.uci.edu) where a Connect Four server is running, along with the port on which that server is listening.

Additionally, the user should be asked to specify a username. The username can be anything the user would like, except it cannot contain whitespace characters (e.g., spaces or tabs). So, for example, boo or HappyTonight are legitimate usernames, but Hello There is not.

Once the user has specified where to connect, the program should attempt to connect to the server. If the connection is unsuccessful, print an error message specifying why and end the program. If, on the other hand, the connection is successful, the game should proceed, with the client acting as the red player (and moving first) and the server — which acts as an artificial intelligence — acting as the yellow player. For red player moves, the user should specify the move at the console, as in your first program; for yellow player moves, the program should instead communicate with the server and let the server determine what move should be made.

As in the first program, the game proceeds, one move at a time, until the game ends, at which point the program ends.

Where is the ICS 32 Connect Four server?

Information about where the server is running will be distributed via email; I'll also keep you posted about planned downtime (e.g., if I need to fix a problem, if I know that the machine where it's running will be down, etc.). It may be necessary to move the server from time to time; when I do that, I will let everyone know via email.

Please note that the ICS 32 Connect Four server is running on a machine on the ICS network that is not exposed to the open Internet. In order to connect to it, you will need to be connected to the campus network, which means you'll either need to be on campus or you'll need to be connected to something called the campus VPN, which allows you to access the campus' network from off-campus. Note, also, that certain residential areas are not connected to a part of the campus network that will allow you direct access to the ICS 32 Connect Four server, so you'll need to use the campus VPN in those cases. In general, if you're not able to connect to the ICS 32 Connect Four server, the first thing you should try is using the campus VPN.

Connecting to the campus VPN requires that you install some software, which you can obtain from UCI's Office of Information Technology at the following link:

Open the link above, find the section titled VPN Software (as opposed to WebVPN, which won't work for this purpose), and click the link titled Download the VPN Software. This will require you to log in with your UCInetID and password, at which point you'll be offered links to Windows, Mac OS X, or Linux versions of the VPN software. Download the one that's appropriate for your operating system and install it; instructions for setting it up are available from that page, as well.


The ICS 32 Connect Four Server Protocol (I32CFSP)

What is a protocol?

Though each of you will be writing a completely separate program, your programs are expected to be able to connect to the ICS 32 Connect Four server via the Internet. There is only one server that we'll all share, so that requires us to agree on a single way to converse with it, so that each program will know precisely how to inform the other about what moves are being made as the game progresses.

Part of our agreement is that we'll use a standard abstraction for Internet communication called sockets. A socket is an object that hides the underlying details of a network connection. Though the underlying network technology is complex, and information is actually sent across the Internet by breaking it up into small pieces and sending those pieces out into the network separately (so that they may arrive at their destination in a different order than they were sent, and so that some parts of it may not arrive at all and will have to be re-sent), a socket hides all of this and makes the connection appear, to your program, to consist of two streams, an input stream and an ouptut stream. Data placed into the output stream of one program's socket will arrive in the same order in the input stream of the other's. It is important to realize that networks are unreliable; there's no guarantee that the data you send will ever get to the recipient, but you can be guaranteed that, if it does, it will be placed into the input stream of the recipient's socket in the same order that you sent it.

Using sockets is not enough, though. Any time you want programs to be able to communicate with the Internet, there needs to be a protocol, which is a set of rules governing what each party will send and receive, and when they will do it. You can think of a protocol like a very rigidly-defined kind of conversation, with each participant knowing its role, so that it will know what to say and what to expect the other participant to say at any given time.

Many protocols have been defined that govern how various programs send and receive information via the Internet. For example, the Hypertext Transfer Protocol (HTTP) is what your browser uses to connect to a web server, request a web page, and receive a response. (That protocol is defined in all of its detail at this link. It has nothing to do with this project, but if you're curious how a "real" network protocol is defined, look no further. And note that the primary author of the protocol was here at UCI at the time.) Since all browsers and all web servers conform to the same HTTP protocol, they can interoperate, even though they are written by different groups of people, run on different operating systems, and provide different user interfaces.

For this project, we'll need a protocol. Our protocol is a custom protocol called the ICS 32 Connect Four Server Protocol; since technical people are so fond of acronyms, we'll use an acronym, too: I32CFSP.

The definition of I32CFSP

I32CFSP conversations are relatively simple. They are predominantly centered around sending moves back and forth, with the assumption being that both conversants will be able to determine the game's state simply by applying these moves locally; for this reason, the game's state is not transmitted back and forth.

I32CFSP conversations are between two participants, which we'll call the server and the client. The server is the participant that listens for and accepts the conversation; the client is the participant that initiates it. (You'll be implementing the client in this project; I've already implemented the server.) The client is always the red player and the server is always the yellow player; this means that the client always moves first. I32CFSP conversations proceed in the following sequence.

An example conversation, for a game in which each player continually drops pieces into the same column, looks like this:

Client Server
I32CFSP_HELLO boo
WELCOME boo
AI_GAME
READY
DROP 3
OKAY
DROP 4
READY
DROP 3
OKAY
DROP 4
READY
DROP 8
INVALID
READY
DROP 3
OKAY
DROP 4
READY
DROP 3
WINNER_RED

How you should handle erroneous socket input

Your program is not permitted to assume that all input it receives will be correct. When it receives input that does not conform to the protocol, your program must immediately close the connection. (This is a rudimentary, but nonetheless effective, form of security; if someone connects and won't play by the rules, hang up on them.)

While the server is resilient about accepting invalid moves, the client is not; if the server sends an invalid move, the client should close the connection immediately.

A note about the design of I32CFSP

You may wonder why the first message is more cryptic than the others; the first message is I32CFSP_HELLO instead of just HELLO, while the others are mostly regular English words. Just like it's important that file formats contain enough information to make it clear what format the file is in — for example, the JPEG image format contains the characters Exif in a particular place, as well as a couple of other distinguishing characteristics that have nothing to do with the image they represent — it's also important that a protocol begins with a message that will distinguish it from other protocols. By starting our conversation with something "special" like I32CFSP_HELLO, the server can be sure that the client intends to have a conversation using our protocol, rather than something else. (After all, you can connect any program that uses sockets, even a browser, to the ICS 32 Connect Four Server, though the conversation won't get very far before the server realizes that it's receiving the wrong kind of traffic and hangs up.)


Module requirements

Because one of the goals of this project is to explore writing programs consisting of multiple Python modules, you will be required to separate this program into at least the following five modules:

Other than connectfour.py, which you must not modify, you can name your modules whatever you'd like, but, as usual, each name should be meaningful and indicate the module's purpose. You should also stick to the Python convention of naming your module using all lowercase letters and multiple words (if you have them) separated by underscores (e.g., connectfour_console.py instead of ConnectFourConsole.py or Connect Four Console.py).


Advice about working incrementally

As the programs you write get larger, it becomes progressively more important that you work on them a little bit at a time. As we've talked about already this quarter, you should always be on the hunt for stable ground, a program that does some part (even some very small part) of what it's supposed to do, but that you can verify. Once you've got a portion of your program working and verified, you're on stable ground.

I quite often think about what you might call "big-picture" and "small-picture" kinds of stable ground. I'm generally working toward a bigger-picture goal; within that, I work toward a sequence of smaller-picture goals as I divide up the bigger-picture problem into smaller parts. I recommend a similar approach here. There are a lot of ways to cut a problem like this up, but here's one sequence of bigger-picture goals that you might find helpful.

Reusing code and how it affects your "Quality and Design" score

While there are two separate programs here — the console version and the networked version — there are substantial similarities between them. The game still proceeds move by move, the user is still shown the current state of the board before each move is made, the local player(s) are asked to specify a move at the console, and so on. The parts of your two programs that are the same should be written once. Your goal, from a design perspective, is to find ways to avoid duplicating code between the two programs.

This is one of the important aspects of your design that we'll be considering when assessing your program's Quality and Design score.


Limitations

All of your socket-related code should use only the socket module from the Python Standard Library to handle any sockets; in general, you'll need to open your own sockets, and do your own reading and writing to them. Since our protocol is entirely text-based, I suggest using the pseudo-file objects we saw in lecture (which you create by calling makefile on a socket object once it's connected). There are fuller-featured tools in the standard library, such as asynchat and socketserver, that hide a lot of the underlying details; we may find these modules useful later this quarter, but I'd like you to have the experience of managing your own sockets for this project.


Deliverables

Only one of the two partners should submit the project; we are aware of the partnerships, so we will be able to figure out which project submissions belong to which pairing. Put the names and student IDs of both partners in a comment at the top of each of your .py files, then submit the files to Checkmate. Take a moment to be sure that you've submitted all of your files.

Follow this link for a discussion of how to submit your project via Checkmate. Be aware that I'll be holding you to all of the rules specified in that document, including the one that says that you're responsible for submitting the version of the project that you want greaded. We won't regrade a project simply because you submitted the wrong version accidentally.

Do I have to submit connectfour.py?

That's up to you. It's not necessary, but it doesn't hurt anything. However, please be aware that we'll be testing your program against the original connectfour module provided here, even if you submit one, because one of the requirements in this project is that you use our connectfour module as-is.

Can I submit after the deadline?

Yes, it is possible, subject to the late work policy for this course, which is described in the section titled Late work at this link.


Communicating about your partnership

What to do if your partnership goes well

If your partnership is going well, enjoy working with your partner; there's no need to communicate with us, as we'll assume that partnerships are functioning normally unless we hear otherwise.

What to do if your partner is absent or uncooperative

The goal here is to do pair programming, which is described in more detail on the front page of the Project Guide. If your partner is consistently absent, uncommunicative, or uncooperative when it comes to pair programming, you can feel free to contact me (the instructor) and I will be able to arbitrate the matter. Single absences from lab happen — though you should have the decency to contact your partner, e.g., via email or a text message if you're not going to be able to make it — so I don't need to kept aware of one-off issues like that, but I do want to know about consistent issues. We do expect everyone to participate in pair programming when assigned, and we reserve the right to reduce scores (and overall course grades) of students who partner up and then persistently refuse to participate.