CSE 333: Systems Programming
This course was one of the most challenging courses I've taken thus far. It's a partner-project-based course, with a few exercises and a midterm and final (which are 45% of your grade but which we spend the least time preparing for/ discussing).
- CSE351: rudimentary knowledge of C programming; the ability to write, run, and debug programs; some familiarity with Linux and the use of Linux compilation, editing, and debugging tools; a solid mental model of the relationship between high-level code (C) and assembly-level compiled code; simple data structures such as linked lists, trees, hash tables, and queues.
- courage and perseverance, even in the face of complexity and uncertainty.
Several practices embody courage. One is the commandment to always design and code for today and not for tomorrow. This is an effort to avoid getting bogged down in design and requiring a lot of effort to implement anything else. Courage enables developers to feel comfortable with refactoring their code when necessary. This means reviewing the existing system and modifying it so that future changes can be implemented more easily. Another example of courage is knowing when to throw code away: courage to remove source code that is obsolete, no matter how much effort was used to create that source code. Also, courage means persistence: A programmer might be stuck on a complex problem for an entire day, then solve the problem quickly the next day, if only they are persistent.
Assignments/ The project (The core of the course)
- incrementally make a candy crush game from scratch - from our own libraries from scratch (aside from GTK+ for the view)
- separate the model-view and run the game over a network
- build a solver (which implements threading)
- then run the solver over a network
"This course is designed to give you substantial experience with programming. There will be approximately one programming assignment due every two weeks; the assignments are designed to build on top of each other, so it is in your interest to make sure that earlier assignments are rock-solid. All of our assignments assume you will be programming within a CSE Linux environment."
Homework #1: A C Implementation of a Generic Doubly-Linked List
This assignment, unlike the others, was quite straightforward and "given" in a sense (code templates, libraries, and Makefile were all given). The structure provides an outline for how our code should be structured and styled for further assignments. All the assignments after this one are mostly open-ended and contribute to the partner project.
Homework #2: A C implementation of a generic 2D Array
"In HW1 you completed the implementation of a generic linked list library. HW2 gives you the chance to design and implement a different generic container library, one providing 2-dimensional arrays. It is up to you to design the interface - the set of methods provided by your library. You will be using this library for the remainder of the homeworks, though, so you'll want to implement now all the methods you expect to need, and probably nothing more."
If you look at the homework specifications and scroll down to the "Library Design" section, you'll find this:
This is a pretty good representation of this class as a whole. Following that is a section called:
This is also a really good representation of the class. Guidelines are open-ended, directions are vague. In previous programming classes, at least some sort of functional specification was given. In this class, it's even doubtful as to what the functional specifications, let alone style or structure. You ask question, to get less slightly less abstract answers, until you reach a level of concreteness that's no where near concrete but is concrete enough for you to start creating something that'll work towards the direction of what they/ the assignment requires.
This is the API that we made to document our 2D Array library:
Homework #3: Using GTK+ to create the "view" of our game
"This assignment takes us one step closer to our final goal, which involves a Model-View-Controller implementation of a simplified but distributed and multi-threaded version of Candy Crush. In this assignment we concentrate on implementing a first version of the view component.
For now, the model is simply a 2d array whose elements are integers from some small, consecutive range, representing candy colors.
The controller in our implementation fields events generated by interactions with the view. The events are:
select a candy. In our implementation that happens by clicking on one.
swap two candies. In our implementation that happens by clicking on one of the four direction arrows, causing the selected candy to be swapped with the candy in the indicated direction (unless there is no candy in that direction). A side-effect (in the model) of swapping candies is that the moves made field is incremented by one. When the view is refreshed after a swap, it shows the updated number of moves made.
The main goal of all this is to make sure you can display a board, can provide some way for the user to select and swap adjacent candies, and can update the number of moves made field. That's it. It doesn't much matter exactly what your board looks like nor what the actions are that cause candies to be moved. Additionally, there are no real elements of game play implemented. That comes next, when we implement the model component.
Please note that what you build now will be enhanced (modified) in some small ways in later assignments. We'll need a way to display which cells in the board have been fired and which not (something like jelly in the original game). We'll need a way to save a game, which is both a board and a number of moves made. We might want to have some notion of gravity. We'll need to have some notion of what candy sequences cause explosions, and which cells are removed when explosions occur, etc. For now, though, making sure we can create the display and field events on it are the only goals."
Homework #4: Simplified Candy Crush Model using C++
"In this step we use C++ to complete the implementation of a very simplified Candy Crush game. The implementation allows game play to be suspended and restarted by serializing current game state to a file and later deserializing back into a running instance. In the next assignment we will send serialized game state over the Internet, allowing the display to be separated from the game logic. In the final step, we will modify the now "headless" game logic to play the game, replacing the human player with software chosen moves."
Homework #5: Separating Model and View - Communicating via TCP
"In this project you divide your application into separate model and view components. Each runs in its own process. You communicate between them using a versatile form of inter-process communication, an Internet protocol (in particular, TCP). This allows you to run the two components on separate machines, or on the same machine if you want. It also lets you pair your model component with someone else's view component, and vice versa. The possibilities are endless, including what we'll do in HW6.
In HW4 your view component used procedure call to notify your model component that a move was being attempted. In this project we replace that direct communication with network communication. We aren't implementing a full-blown remote procedure call system, but that's still a useful way to think of what is happening. This presents some new challenges:
- In HW4 the model and view found each other statically, at link time. The caller simply statically gave the called method's name, and the linker, well, linked the two together.
In HW5 the two components have to find each other dynamically, at run time. They identify the other component using an IP address and port number. In some sense, the linker in HW5 is you, as you have to manually supply to one component the name (IP and port) of the other so it can contact it
- In HW4, arguments were supplied simply as in memory values transferred from caller to callee via the stack.
Going over the network in HW5, that doesn't work. Instead, arguments are serialized by the caller and sent to the callee, which deserializes them.
- In HW4, at run time one component "named" a method of the other by supplying the memory address where its code starts. The linker was in charge of translating from the string name of the method, given in the source code, to the memory address required at run time.
In HW5, the caller identifies the (logical) method to be called basically as an additional argument. You can think of an IP:port as naming the remote process, but not a particular method in that process. You pass an identifier for the (logical) procedure you want to call as part of the data.
We'll use the term message for the serialization sent to either request invocation or to return a response from a requested invocation. We serialize using json."
Homework #6: An intelligent, multi-threaded solver
"In this final piece of the project we adapt the model component of HW5 so that it chooses the next move to be made. Your model contacts a server to obtain a game to be played. The server then periodically contacts your model component to ask for its next move, which must be supplied promptly. Because finding a good move in limited real time is a constraint, your search for moves should be multi-threaded."
still under construction
Links and Files