Repository: cis198-2016s/homework Branch: master Commit: 880f1833e3f6 Files: 11 Total size: 65.7 KB Directory structure: gitextract_0bxq24dp/ ├── .gitmodules ├── final/ │ └── README.md ├── hw00/ │ └── README.md ├── hw01/ │ ├── README.md │ └── tests_provided.rs ├── hw02/ │ └── README.md ├── hw03/ │ └── README.md ├── hw04/ │ └── README.md ├── hw05/ │ └── README.md ├── hw06/ │ └── README.md └── hw07/ └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitmodules ================================================ [submodule "hw02/starter"] path = hw02/starter url = git@github.com:cis198-2016s/hw02.git branch = master [submodule "hw03/starter"] path = hw03/starter url = git@github.com:cis198-2016s/hw03.git branch = master [submodule "hw04/starter"] path = hw04/starter url = git@github.com:cis198-2016s/hw04.git branch = master [submodule "hw05/starter"] path = hw05/starter url = git@github.com:cis198-2016s/hw05.git branch = master [submodule "hw06/starter"] path = hw06/starter url = git@github.com:cis198-2016s/hw06.git branch = master [submodule "hw07/starter"] path = hw07/starter url = git@github.com:cis198-2016s/hw07.git branch = master ================================================ FILE: final/README.md ================================================ # Final Project This final project gives you the freedom to do whatever you've been dying to do with this fancy new programming language you've just learned. ## Schedule * Tue 3/29 - Ideas Due (on Piazza megathread) (for participation credit) * Thu 3/31 - Proposal Draft Due (PDF via email) * Fri 4/01 - Proposal Meeting (by appointment) * Wed 4/06 - Proposal Presentation (in class) (4 min) * Please also send: final proposal, repository link, and documentation link. * Wed 4/13 - Milestone 1 Presentation (in class) (2 min) * Wed 4/20 - Milestone 2 Presentation (in class) (2 min) * Wed 4/27 - Final Presentation (in class) (6 min) * Wed 4/27 - Final Report (PDF via email) **IMPORTANT:** After your proposal is submitted, we want to talk to you before the proposal presentation to discuss and finalize. There will be a sign-up sheet for meetings, probably on Friday, April 1. ## Grading This project is worth 40% of the final course grade. * 15%: Proposal and Presentation * 20%: Milestone 1 Presentation and Status * 20%: Milestone 2 Presentation and Status * 45%: Final Presentation, Status, and Report **Important:** "Status" grading will be based primarily on your documentation. Spend time on this and write in-depth documentation! You'll need to use rustdoc to generate documentation before each presentation (see below: [Documentation](#documentation)). ## Project Guidelines * Groups may be 1-3 people, 2 preferred. Collaborate via GitHub (obviously). Make your own repository if applicable. * You can either make your own project or contribute to an existing open-source project. If you do the latter, make sure that the project owners are okay with this. They will expect very high-quality contributions - so don't drop a half-done pull request on them. * If you make a new project, we _prefer_ (but don't require) things that haven't been done in Rust before. * Your idea needs to be doable in 3 weeks. This will be all of your 198 work during those weeks, so schedule accordingly. * We expect you to do about (3 weeks * 0.5 credit) worth of work per person - but what that means is subjective. More work will mean a more impressive project - but it's much more important that you finish! * If your idea is in danger of being too big, you need to have a plan for how to scale it back. Carve out a reasonable subset of the idea as your baseline, and have additional goals on top of that. * We would really like your project to be open-source! You don't have to if you really don't want to, but contributing to the Rust ecosystem is ideal. You don't need to pick licensing immediately, but consider Apache and MIT. * If you have no ideas you like, you might consider putting out a call for project ideas to the Rust community (e.g. via Reddit). (If you take a community idea, your project must be open-source.) ### Ideas These are our ideas - none of them have been investigated and you don't have to use any of them. * http://www.ncameron.org/rust.html * https://www.rust-lang.org/contribute.html * https://www.reddit.com/r/rust/comments/3bjl53/rust_language_project_ideas/ * https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md * https://github.com/servo/servo/blob/master/CONTRIBUTING.md * https://github.com/rust-lang-nursery * https://github.com/redox-os/redox#contributing * Parallel computation (e.g. scientific computing or ray tracing) * Other fast scientific computing * Safe(!) bindings for a C API * Rewriting slow parts of other programs (e.g. Python/Ruby) * Some interesting distributed system * Some kind of game ## Proposal Document * Your proposal document should be about 1 page. A Google Doc is recommended. (But please submit as a PDF!) * List your group members. * Abstract: Your idea in a sentence or two. New project or contribution? * Open-source or not. If not, why? You don't need to pick a license right now, but Apache and MIT are good. * Project outline: Your minimal goals, expected goals, and stretch goals. * This should be detailed enough that we can judge the complexity and merit. * Include appropriate technical details of each goal. * Tentative Schedule: Which goals do you want to finish each week? Milestone presentations will be at the beginning of class each week. ## Milestone Presentations These will be very quick (~2 min). Just update us on your progress. You may present documentation or markdown on GitHub. Send a link before class so we can open it on the classroom computer. All of your work must be in your repository by class time, including rustdoc documentation for the work you're presenting (see below). If you're contributing, this means it should be in your fork. ## Documentation If you are working on a standalone project, you should have rustdocs documenting all of the work you've done so far, at each milestone and the final presentation. This doesn't mean documenting every single function - but you need detailed documentation for the modules, functionality, and all important structs/functions. **Important:** Spend time on this and write in-depth documentation. It will be a significant part of our grading! For any general information, put it on your crate root module. You can use this syntax for module documentation: ``` //! if you put this documentation comment style in a //! module, it will apply to the module itself instead //! of the thing after it ``` Please host docs somewhere online. You can use [hosting on Eniac](http://www.seas.upenn.edu/cets/answers/webpage.html), or you may use a [GitHub Pages](https://pages.github.com/) project site. By default, `cargo doc --no-deps` will export documentation for everything _public_ in your crate. However, for the project, you'll likely want to export everything. To do this, use: ```sh cargo rustdoc -- --no-defaults --passes collapse-docs --passes unindent-comments ``` Here is some [example rustdoc output](http://cis198-2016s.github.io/final-sample-rustdoc/webchat/). This shows the output of the above command on our HW07 solution. Note that it doesn't have any _module-level_ documentation - which will be most important for you. If you're contributing to another project, you should send us a compilation of the documentation for everything **you** have written as part of your project. If there is any additional extra information that you want to send to us but think doesn't belong in the documentation, email it to us before your presentation. ## Final Report Write your report in markdown, and save it as `REPORT.md` in your repository root, and we'll find it there. If you don't have a dedicated repository for this project (e.g. you are forking another project) or you plan to immediately publicize your project, put the report in a [Gist](https://gist.github.com/) and email us the link. If you have screenshots or images, save them in the repo and embed them in `REPORT.md`. We'll expect about 2-3 printed-pages-worth of info. Include whatever is appropriate or important for your project, such as: * Summary * Approximate time spent * Accomplishments * Components, structure, design decisions * Testing approach and results * Benchmarks * Limitations * Postmortem * What went well * What you would do differently * etc. If your project would benefit from having a video, that would be great. You can, e.g., record using [OBS](https://obsproject.com/) and upload to YouTube. **Your documentation is still a part of the final milestone grade.** Anything you've written for documentation can be copied to your report if it's important. Make sure you have an appropriate amount of documentation. If you have a ton of code but haven't written much documentation for it, your rustdoc output will look very sparse and it'll be harder for us to tell how much you have done. Comments don't count, as they don't appear in the rustdoc output. Keep in mind that if your repo is public, your report will be as well. If you have any private feedback, email us. ================================================ FILE: hw00/README.md ================================================ # Homework 00: Hello Rust & Hello Cargo! **Due 2016-01-25, 11:59pm.** For questions, please post on Piazza (Penn students) or Google Groups (other). Links on homepage. #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click the link on the course Piazza. If there is no starter code, such as in this homework, you can use Cargo to initialize the git repository for you. See below. But first, Rust! ## Installing Rust For this homework, all you'll have to do is install the Rust compiler (rustc) and the Rust package manager (Cargo). We'll be using Rust v1.6 for this class. (Version 1.6 is set to be released next Thursday! This homework doesn't depend on the version of Rust, so it's fine to get started early.) We recommend using [multirust][multirust], a tool to manage multiple installations of Rust on your system. Multirust supports Linux, OS X, and Windows (via MSYS2). Unfortunately, there is no support for Windows if you are not using MSYS2. Multirust maintains a user default toolchain version (stable, beta, or nightly). Run `multirust default stable` to set your user preference to stable. This will also download the stable toolchain. **On Linux, OS X, or Windows+MSYS2:** Install multirust by following the [instructions on their README][multirust]. You can also use your local friendly package manager. [multirust]: https://github.com/brson/multirust **On Windows (without MSYS2):** You can either use the standard Rust installer from [the website](https://www.rust-lang.org/downloads.html), or you can use multirust on Eniac. **On Eniac:** If you don't want to install Rust, multirust is also available on Eniac. Add this line to your `~/.bashrc` on Eniac: `export PATH=$PATH:/home1/c/cis198/local/bin` To check that Rust and Cargo are installed properly, run the following commands and make sure the output matches below: ``` $ rustc --version rustc 1.5.0 (3d7cd77e4 2015-12-04) $ cargo --version cargo 0.6.0-nightly (e1ed995 2015-10-22) ``` When version 1.6 is released next week, you should update your version of Rust, using `multirust update stable`. ## Hello, Rust! Now that Rust is ready to roll, let's write our first "hello world" program. Create a file named `main.rs` and modify the code snippet below to print out "Hello, \!". ```rust fn main() { let name = "Ferris"; // Ferris is the name of Rust's unofficial crustacean mascot println!("Hello, {}!", name); } ``` Once you've created your program, compile it using `rustc main.rs` and run the resulting `main` binary to test it. Boom! You did it! You're a Rust programmer now! 🎊🎉👍 ## Hello, Cargo! Rust has a fantastic package and build manager, Cargo, which is modeled from years of learning from other languages. Cargo handles all the gory build automation and dependency management details for you, so that you don't have to worry about it when creating (or building or updating) a project. Using `rustc` directly is fine for small projects, but Cargo really helps to remove a lot of the friction of manual project management. If you've ever used `rake` or `pip` for dependency management, Cargo is like those, plus all of the build power provided by a good `Makefile`. To make a new project for homework 0, run the command `cargo new --bin hw00` (which will *create* the folder `hw00`). Why `--bin`? We want this project to create a standalone executable, rather than a library that can be rolled into other projects. If you are not already in a git repository when you create your project, Cargo will create a git repository (and `.gitignore`) for you. Then, you can add this your GitHub repository as a git remote as follows: ``` git remote add origin git@github.com:cis198-2016s/hw00-.git git push -u origin master ``` Cargo creates this directory structure for you: ``` hw00 ├── Cargo.toml └── src └── main.rs 1 directory, 2 files ``` You may notice that the `main.rs` file Cargo creates looks suspiciously similar to the "Hello World" code above. Since you've already written your "Hello World" program, move the file you created into `src`, and overwrite the `main.rs` that Cargo generated. To finish things off, let's build our project. Simply run `cargo build` from any directory in the project tree! You can run your executable with `cargo run`. Pretty magic, huh? ## Bonus: Configuration Adding to your personal environment setup is one of the many joys of starting a new programming language. Rust has a pretty decent amount of support for being new to the game. Take a look at [this list of editor configs][configs.md]. There are more unofficial or less-supported ones out there, so it's worth looking around. If you aren't sure what to use, all three of your instructors use vim :) [configs.md]: https://github.com/rust-lang/rust/blob/master/src/etc/CONFIGS.md ## Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. **Make sure it is visible on Github!** This is your submission. (Work must be in the master branch at the due time.) That's it! ok bye get outta here :point_right: ================================================ FILE: hw01/README.md ================================================ # Homework 01: Rust Finger Exercises **Due ~~2016-01-27~~ 2016-01-28, 11:59pm.** For questions, please post on Piazza (Penn students) or Google Groups (other). Links on homepage. ## Overview ## In this assignment, you'll get your first crack at writing some short functions in a Rust library, building and testing using Cargo, and writing modules. This assignment isn't intended to be especially difficult, but will make sure you're set up properly in the Rust ecosystem, and will give you some good experience using the Rust compiler. #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/d7a2cbae7d57de44e29c302c8e353a43 ## Provided Code ## Just one file, `tests_provided.rs`, which contains several test cases. We're asking you to write everything else from scratch, but we'll give you function signatures below, as a starting point. ## Part 01: Cargo ## Don't clone your GitHub repository just yet. To start off, let's create a new Rust library: `cargo new hw01`. If you are not already in a git repository when you create your project, Cargo will create a git repository (and `.gitignore`) for you. Then, you can add this your GitHub repository as a git remote, with something like: ``` git remote add origin git@github.com:cis198-2016s/hw01-.git git push -u origin master ``` (If you're not using SSH for GitHub, you need to use the HTTPS URL of your repository.) Cargo creates this directory structure for you: ``` hw01 ├── .git/ ├── .gitignore ├── Cargo.toml └── src └── lib.rs ``` You can build this project from anywhere in the project tree with `cargo build`. Remember to compile periodically as you work. ### Modules ### Before you go any further, a word on modules and crates. Crates are any Rust library or package. Modules are the inner logical sections of a crate. You will want to organize each section of your crate into a different module, so consumers can only import the parts that they need. You could do this in `lib.rs`: ```rust pub mod problem1 { } pub mod problem2 { } pub mod problem3 { } pub mod problem4 { } // ... ``` But this gets unwieldy pretty fast. You can instead put these modules in separate files: ``` hw01 ├── Cargo.toml └── src ├── problem1.rs ├── problem2.rs ├── problem3.rs ├── problem4.rs └── lib.rs ``` Every `.rs` file defines a module that is the same as its filename, so `problem1.rs` implicitly defines the module `problem1`. Crates are organized into trees of files, where one file is the root, typically `src/lib.rs` (or `src/main.rs`). To include a module, you need to declare it in the crate root. To add `problem1` as a module, add the line `pub mod problem1;` to the top of `src/lib.rs`. (This is equivalent to defining a module in the file itself, with `pub mod problem1 { ... }`.) Until you add this directive, Cargo will not try to build `problem1` part of your crate. The `pub` keyword in this directive exposes the module `problem1` to any other crate that imports your library. You can omit `pub` to leave modules private; however, if a function is not exported or used internally, it will emit a dead code warning. Within a module, all members (functions, types, submodules) are private by default. The `pub` keyword can also be used to make any of these available from outside of the module. ```rust // problem1.rs /// Functions are private (only available to this module) by default. /// Use the `pub` keyword to mark this function as public. pub fn sum(slice: &[i32]) -> i32 { // ... } ``` There are a few different ways to import items from a different module: 1. To add `sum` to the scope of a file, add the line `use problem1::sum` to the top of the file. You can call the function with `sum()`. Try to import only what you need to use. 2. To import multiple things from a module, use curly braces: `use problem1::{sum, dedup};` 3. To import all items from a module, write `use problem1::*`. 4. To import an entire module, write `use problem1;`. This form requires you to qualify members of the module with the module name (e.g. `problem1::sum()`). This is more verbose but does not pollute the namespace of your scope. We provided a `tests_provided.rs` file with a few test cases to start you off with. You should add this to your library as a separate module, as you did with each `problemX` module (but the tests don't need to be `pub`). You can read more about modules in the Rust book [here](https://doc.rust-lang.org/book/crates-and-modules.html). ## Part 02: Basic Functions ## ### Preface ### For consistency, we ask that you put each problem into its own file, and name the file as `problem1.rs`, etc. Remember to declare each module at the top of `lib.rs` as well! ### Problem 01: Vector & Slice Manipulation ### *Vectors, iteration, pass-by-ref, mutability, function pointers.* Create a new module in your library named `problem1` inside `problem1.rs`. To get a first taste of basic Rust, complete the following three functions. Note that all of these functions take their arguments by reference, rather than by value. Don't use any of the standard library methods on the `Vec` class which implement the target behavior, since using them would defeat the point of this exercise :). (However, basic functions such as `contains()` and `push()` are fine). ```rust /// Computes the sum of all elements in the input i32 slice named `slice` pub fn sum(slice: &[i32]) -> i32 { // TODO unimplemented!(); } /// Deduplicates items in the input vector `vs`. Produces a vector containing /// the first instance of each distinct element of `vs`, preserving the /// original order. pub fn dedup(vs: &Vec) -> Vec { // TODO unimplemented!(); } /// Filters a vector `vs` using a predicate `pred` (a function from `i32` to /// `bool`). Returns a new vector containing only elements that satisfy `pred`. pub fn filter(vs: &Vec, pred: &Fn(i32) -> bool) -> Vec { // TODO unimplemented!(); } ``` #### Testing Before you move on, take a look at `tests_provided.rs`. At the top of this file, there's a `#![cfg(test)]` attribute. `cfg`, short for "configuration", is part of how Rust handles conditional compilation. This is similar to (but way better than) the use of `#ifdef`s and include guards in C. In this case, `#![cfg(test)]` tells the compiler that this module is not to be compiled unless the `--test` flag is used with `rustc` (`cargo test` adds this flag under the hood). This is handy because it means that you don't have to waste time recompiling your tests every time you build unless you're actually going to run them. All of the functions in `test.rs` are annotated with the `#[test]` attribute; this tells the compiler that they're tests. Test functions must have the signature `fn() -> ()`, or else they will not compile. Tests will be run when you invoke `cargo test`. Any test which doesn't cause a `panic!` is considered to pass. You should use [`assert!`][assert] or [`assert_eq!`][assert_eq] to check guarantees and equality (respectively). [assert]: https://doc.rust-lang.org/std/macro.assert!.html [assert_eq]: https://doc.rust-lang.org/std/macro.assert_eq!.html As an aside, the `cfg` attribute has many other uses, like knowing which OS or architecture you're compiling for. We have provided a few tests to start you off with. You should add at least one non-trivial test for each function that you implement. Write these in a new module, `tests_student.rs`, and declare the module in `lib.rs`. ### Problem 02: Matrix Multiplication ### *Vectors, iteration, pass-by-reference, structs.* Create a new module in your library named `problem2` inside `problem2.rs`. We define a `Matrix` as a type alias to `Vec>`. Write a function that takes in two `Matrix`es by reference and returns the product `mat1 * mat2`. The function signature is provided below. You should make sure that the two input matrices are actually compatible. Remember that you can't multiply two matrices if the number of columns in the first matrix is not equal to the number of rows in the second matrix. Use `assert!` or `assert_eq!` to `panic!` if this condition is not met. (Of course, crashing is bad - we'll learn about fixing this later.) ```rust /// Represents a matrix in row-major order pub type Matrix = Vec>; /// Computes the product of the inputs `mat1` and `mat2`. pub fn mat_mult(mat1: &Matrix, mat2: &Matrix) -> Matrix { // TODO unimplemented!(); } ``` ### Problem 03: Sieve of Eratosthenes ### *Vectors, iteration, mutability...* Create a new module in your library named `problem3` inside `problem3.rs`. The [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) is used to find all primes below some given number (`n`), and is an efficient way to find small primes. Iterate through the numbers from `2` to `n`. For each number `i`: 1. If `i` has been crossed-out from previous iterations, skip it. 2. If `i` isn't crossed-out yet, then it is prime. Cross-out all multiples of `i` from `i*i` to `n`. These are non-prime. Head over to the Wikipedia page for a more detailed description and some zesty examples. Your function will take a number, `n`, and return a list of all prime numbers less than `n`. (Notice that you can't use a Rust array in this case, since you don't know the length at compile time.) ```rust /// Find all prime numbers less than `n`. /// For example, `sieve(7)` should return `[2, 3, 5]` pub fn sieve(n: u32) -> Vec { // TODO unimplemented!(); } ``` ### Problem 04: Towers of Hanoi ### *Mutability, type aliases, vecs, tuples, enums.* [The Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi) is a classical mathematics and computer science puzzle. Imagine you have three pegs, one of which holds a stack of discs in increasing size from bottom to top. Your goal is to move all of the discs from the first peg to the third peg, using the second peg as an intermediate. Your only restrictions are that you may only move one disc at a time, and a disc may only be placed on a disc larger than it (or on an empty peg). Check out the Wikipedia page for more details and snazzy animations. In this instance, we've provided you with an enum type containing the possible names of `Peg`s and a type alias defining a move between two pegs. This function will take in a number of discs, and the names of the three pegs, and return a vector of `Move`s. ```rust /// #[derive(...)] statements define certain properties on the enum for you for /// free (printing, equality testing, the ability to copy values). More on this /// when we cover Enums in detail. /// You can use any of the variants of the `Peg` enum by writing `Peg::B`, etc. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Peg { A, B, C, } /// A move between two pegs: (source, destination). pub type Move = (Peg, Peg); /// Solves for the sequence of moves required to move all discs from `src` to /// `dst`. pub fn hanoi(num_discs: u32, src: Peg, aux: Peg, dst: Peg) -> Vec { // TODO unimplemented!(); } ``` ## Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. **Make sure it is visible on Github!** This is your submission. (Work must be in the master branch at the due time.) Your repository should look like this: ``` [git root] ├── Cargo.toml └── src ├── lib.rs ├── problem1.rs ├── problem2.rs ├── problem3.rs ├── problem4.rs ├── tests_provided.rs [same as given] └── tests_student.rs [your tests] ``` `cargo test` should run all of the tests in your homework. Make sure you have written at least one test for each function you have written. ================================================ FILE: hw01/tests_provided.rs ================================================ #![cfg(test)] use problem1::{sum, dedup, filter}; use problem2::mat_mult; use problem3::sieve; use problem4::{hanoi, Peg}; // // Problem 1 // // Part 1 #[test] fn test_sum_small() { let array = [1,2,3,4,5]; assert_eq!(sum(&array), 15); } // Part 2 #[test] fn test_dedup_small() { let vs = vec![1,2,2,3,4,1]; assert_eq!(dedup(&vs), vec![1,2,3,4]); } // Part 3 fn even_predicate(x: i32) -> bool { (x % 2) == 0 } #[test] fn test_filter_small() { let vs = vec![1,2,3,4,5]; assert_eq!(filter(&vs, &even_predicate), vec![2,4]); } // // Problem 2 // #[test] fn test_mat_mult_identity() { let mut mat1 = vec![vec![0.;3]; 3]; for i in 0..mat1.len() { mat1[i][i] = 1.; } let mat2 = vec![vec![5.;3]; 3]; let result = mat_mult(&mat1, &mat2); for i in 0..result.len() { for j in 0..result[i].len() { assert_eq!(result[i][j], mat2[i][j]); } } } // // Problem 3 // #[test] fn test_sieve_basic() { assert_eq!(vec![2,3,5,7,11], sieve(12)); } // // Problem 4 // #[test] fn test_hanoi_1_disks() { let result = hanoi(1, Peg::A, Peg::B, Peg::C); assert_eq!(vec![(Peg::A, Peg::C)], result); assert_eq!(1, result.len()); } ================================================ FILE: hw02/README.md ================================================ # Homework 2: A Mediocre Binary Search Tree **Due 2016-02-03, 11:59pm.** For questions, please post on Piazza (Penn students) or Google Groups (other). Links on homepage. ## Overview This assignment is modeled after Alexis Beingessner (Gankro)'s [_Learning Rust With Entirely Too Many Linked Lists_][TMLL] (herein referred to as TMLL, because that's much easier to pronounce, right?) [TMLL]: http://cglab.ca/~abeinges/blah/too-many-lists/book/ Even though we're implementing a [BST][], you should read Chapters 1-2 (introduction and first linked list); they're a great place to start. Gankro's writing is really fun to read and the content is enlightening. [BST]: https://en.wikipedia.org/wiki/Binary_search_tree #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/9f6544879f4d5995f8a23cf35caf2133 ## Instructions Write your implementation in the `src/first.rs` file. You'll also write tests in this file. The main file, `lib.rs`, is rather boring and done for you. Your code doesn't have to follow any exact interface, just the guidelines below. You'll write the data structure, `insert`, `search`, and tests. Unlike in HW1, if you create your repository using Classroom for GitHub (link on Piazza), your Git repository will be pre-populated (you won't need to use `cargo new`). Just clone it to get going. (See the `starter` subrepo for the starter repository contents.) You should edit `Cargo.toml` to add yourself as the author. #### Aside: Clippy [Clippy][] is a compiler plugin which runs [lints][] on your code. It only works in nightly Rust, however, so it's not enabled by default here. If you feel like trying it out, switch to the nightly channel (e.g. `multirust override nightly` while inside the project directory), then build with the `clippy` feature: ``` cargo build --features clippy cargo test --features clippy ``` This is recommended as it will help catch some stylistic errors as you learn the language. (But, for ease of grading, please be sure to submit code which works on Rust stable.) [Clippy]: https://github.com/Manishearth/rust-clippy [lints]: https://en.wikipedia.org/wiki/Lint_%28software%29 (If for some reason Clippy causes problems for your stable Rust build, just remove the `[dependencies]` and `[features]` sections from `Cargo.toml` and the Clippy-related lines at the top of `lib.rs`.) ### Implementation This _roughly_ corresponds to [TMLL 2](http://cglab.ca/~abeinges/blah/too-many-lists/book/first.html). For more details, refer there. As in TMLL 2: * Write a `BST` type similar to the one in TMLL 2: a `pub struct` with a `root` element of type `Link`. Implement `BST::new()` which creates an empty BST. * This will be the only `pub struct`. The others are implementation details. * Define `Link` as an `enum` with two instances: `Empty` and `More`, where `More` contains a boxed `Node`. * Define `Node` as a `struct` containing an `i32` element. Instead of a single `next` element, it should also contain two child `Link`s: `left` and `right`. * Add `#[derive(Debug)]` before each of the three types. This allows you to debug-print a value, e.g.: `println!("{:?}", bst);` * Use `println!("{:#?}", bst);` for multi-line, indented debug prints! * To be able to see printed output of successful tests, use `cargo test -- --nocapture`. Now, instead of TMLL's `push` and `pop`, we'll implement: * (`pub`) `bst.insert(i32) -> bool`: Insert an element into the BST. Return true if successful, or false if the element was already in the BST. * (`pub`) `bst.search(i32) -> bool`: Search for an element in the BST. Return true iff the element was found. #### Hints from our reference implementation You should need: `struct`, `enum`, `impl`, `match`/`ref`/`ref mut`, `Box`, `&`/`&mut`, dereferencing (`*`). You should not need: named lifetimes, `Option`, `Vec`, `use`. In our reference implementation, `BST::insert` and `BST::search` are short functions which just call longer, recursive member functions of `Link`: * `link.insert(i32) -> bool` (~25 lines) * If inserting into an empty link, place the element in this link (making it no longer empty). * If inserting into a non-empty link: * return `false` if the element is in this node; otherwise, * recurse to the left if the new value is less than the node's value * recurse to the right if the new value is greater than the node's value * You may not end up needing `mem::replace` like in TMLL. * `link.search(i32) -> bool` (~15 lines) * If searching an empty link, return `false`; the element can't be found. * If searching a non-empty link: * return `true` if the element is in this node; otherwise, * recurse to the left if the target value is less than the node's value * recurse to the right if the target value is greater than the node's value ### Tests You can run tests with `cargo test`. To show any printed output of successful tests, use `cargo test -- --nocapture`. Tests should be defined in the way specified in [TMLL 2.6](http://cglab.ca/~abeinges/blah/too-many-lists/book/first-test.html). This keeps unit tests close to the code that they test. ```rust #[cfg(test)] mod test { use super::List; #[test] fn test_push_pop() { // ... } } ``` You should test `search` and `insert` in various orders, for both true and false results. Use `assert!()` and/or `assert_eq!()`. They don't have to be any more complex than the tests in TMLL 2.6. ### Submission Just like in homework 01, commit and push your work to the master branch of your Classroom for Github repository for this HW. Make sure it is visible on Github! This is your submission. (Work must be in the master branch at the due time.) `cargo test` should work to run all of the tests in your homework on stable Rust. Make sure you have written tests which cover every one of your functions. ================================================ FILE: hw03/README.md ================================================ # Homework 3: "Iterating" On Your Binary Search Tree **Due 2016-02-10, 11:59pm.** For questions, please post on Piazza (Penn students) or Google Groups (other). Links on homepage. ## Overview This assignment is modeled after Alexis Beingessner (Gankro)'s [_Learning Rust With Entirely Too Many Linked Lists_][TMLL] (TMLL). [TMLL]: http://cglab.ca/~abeinges/blah/too-many-lists/book/ This time, you should look at Chapter 3. We won't do everything in there, but the content is similar. #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/678e4f3868daaec525ce8f75f23fc53c ## Instructions Write your implementation and tests in `src/second.rs`. `lib.rs` is provided. As before, your code doesn't have to follow an exact interface, but the function signatures provided will probably work best. You'll likely want to start off with your solution for HW2 - a lot will carry over. You'll modify the data structure, `insert`, `search`, and the tests, and write the code necessary to turn your BST into various iterator types: `IntoIter`, `Iter`, and `IterMut`. Like in HW2, your GitHub repository will be pre-populated with a skeleton Cargo project. You should edit `Cargo.toml` to add yourself as the author. ### Implementation This _roughly_ corresponds to [TMLL 3](http://cglab.ca/~abeinges/blah/too-many-lists/book/second.html). For more details, refer there. We will use: * `Option` and `take` (but *not* `map`). * Type aliases. * Generic type parameters (`BST`) with trait bounds (`Ord`). * Named lifetimes and lifetime bounds on type parameters. * Traits, trait implementations, and associated types. * Iterators. We'll essentially be modifying HW2, so you should copy `first.rs` over as `second.rs` in HW3. **NOTE:** [TMLL 3.1][] introduces `map` and anonymous functions and closures. Since we haven't covered this in class, you can use `match`es instead of closures everywhere in this assignment. However, you're welcome to use `map`/closures if you want the extra practice - simple closures aren't hard. > Second, `match option { None => None, Some(x) => Some(y) }` is such an > incredibly common idiom that it was called `map`. `map` takes a function to > execute on `x` in the `Some(x)` to produce the `y` in `Some(y)`. We could > write a proper `fn` and pass it to `map`, but we'd much rather write what to > do *inline*. #### Details As in TMLL 3: * Convert `BST` and `Link` to take a generic element `T` instead of `i32`. * Replace the `Link` type with a type alias for `Option>>`, because your life has felt strangely lacking in angle brackets recently. Then, update all of the code which uses `Link`. You may not have used `mem::replace` in HW2, in which case you won't need `take` yet. * If you want, take a look through the [`Option` documentation][optdoc] to find useful methods. [optdoc]: https://doc.rust-lang.org/std/option/enum.Option.html * Now that `Link` is a type alias and not a struct, you cannot directly `impl` methods for it, because you don't own the type `Option`. However, you do not feel constrained by the ~~silly~~ very serious and important rules that Rust imposes on your life, so you can work around this by defining a generic trait `InsertSearch` which provides your two functions `insert(&mut self, e: T) -> bool` and `search(&self, e: T) -> bool`. * Proceed to exercise your unbounded power by implementing `InsertSearch` for `Link`, by adapting the functions you wrote for HW02 to be generic over `T`. Make sure your old tests still pass. * Now you will transform your ordinary BST into an extraordinary BST iterator! We're going to use the [`Iterator`] trait. * Build yourself an `IntoIter` struct like the one in [TMLL 3.4][]. * Since this is a BST, and not a list, we're going to cheat a bit and just iterate over the rightmost edge of the tree, since that's less annoying. (If you feel like it, you can try doing an in-order traversal of the tree instead. We can't guarantee that you can do it with the material we have covered so far, so try the cheaty version first!) * Implement the [`Iterator`][] trait for `IntoIter`. This requires an associated type, `type Item`, and an implementation of `next(&mut self) -> Option`. * Instead of implementing `BST::into_iter` as a plain member function, we're going to do something _way_ cooler: * [`IntoIterator`][] is a trait with one method, `into_iter`. This is the sugar that fuels Rust's `for` loops. Go ahead and `impl IntoIterator for BST`. Don't forget: this trait requires you to declare associated types! Read the [docs][`IntoIterator`] for the deets. [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html * BAM! Now your BST can harness the power of `for` loops. Try this: ```rust let mut bst = BST::new(); bst.insert(1); bst.insert(2); bst.insert(3); for elt in bst { // calls bst.into_iter() println!("{}", elt); } ``` * But your power is yet incomplete. What about borrowed iteration? Implement an `Iter` struct, as described in [TMLL 3.5][] (similar to `IntoIter`). * Again, instead of implementing `BST::iter`, we're going to lord our superiority over Gankro's tutorial and use `IntoIterator`. But you can't just implement a trait _twice_ for the same struct; that would be absurd. And confusing. It would break so many rules. Instead, we'll implement this for `&BST`. * Again, iterate over the rightmost edge of the tree. * You're going to need named lifetimes here! To start, you have to implement `impl<'a, T> IntoIterator for &'a BST`. * This is what allows: ```rust for elt in &bst { // calls (&bst).into_iter() println!("{}", elt); } ``` * Finally, we'll move on to [TMLL 3.6][], `IterMut`. You know what you need to do. ```rust for elt in &mut bst { // calls (&mut bst).into_iter() println!("{}", elt); } ``` [TMLL 3.1]: http://cglab.ca/~abeinges/blah/too-many-lists/book/second-option.html [TMLL 3.4]: http://cglab.ca/~abeinges/blah/too-many-lists/book/second-into-iter.html [TMLL 3.5]: http://cglab.ca/~abeinges/blah/too-many-lists/book/second-iter.html [TMLL 3.6]: http://cglab.ca/~abeinges/blah/too-many-lists/book/second-iter-mut.html ### Tests Test all three of your iterators. To do this, you should check two things: * For a BST `bst`, you should be able to compile a for loop over `bst`, `&bst`, or `&mut bst`. * The values returned by the iterator should be correct. You can explictly get an iterator (with, for example, `(&mut bst).into_iter()`), then `assert_eq!` the values returned by `next()`. ### Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. Make sure it is visible on Github! This is your submission. (Work must be in the master branch at the due time.) `cargo test` should work to run all of the tests in your homework on stable Rust. Make sure you have written tests which cover every one of your functions. If you have any comments or feedback on this assignment, include them in the README of your submission. ================================================ FILE: hw04/README.md ================================================ # Homework 4: Reverse Polish ~~Sausage~~ Notation Calculator **Due 2016-02-17, 11:59pm.** For questions, please post on Piazza (Penn students) or Google Groups (other). Links on course homepage. ## Overview Reverse Polish notation, which is [not exactly][rpn] the reverse of Polish notation, is a convention for mathematical expressions for ~~nerds~~ ~~hipsters~~ ~~enterprising young computer scientists~~ ~~bored students~~ you! Implementing an RPN calculator in Rust will cover several standard library modules and common patterns. [rpn]: https://en.wikipedia.org/wiki/Reverse_Polish_notation#Explanation Reverse Polish notation defines expressions with _postfix_ operators (`1 2 +`), rather than the typical _infix_ expression form (`1 + 2`). Postfix form is very easy to evaluate using a state machine with a stack data structure. When the calculator encounters a numeric literal (`1`), it pushes the number onto the stack. When an operator (`+`) is encountered, the machine pops one or more numbers off the stack, evaluates the expression, then pushes the result back onto the stack. #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/4247c727a1c77eb1982e03bc4083a574 ## Instructions Write your calculator implementation in `rpn.rs`, and your parser implementation in `parser.rs`. `main.rs` is provided. Hooray! You're finally writing an executable program! Like in HW3, your GitHub repository will be pre-populated with a skeleton Cargo project. You should edit `Cargo.toml` to add yourself as the author. ##### Stack Element Types Your stack will support two types: `i32` and `bool`. We're starting you off with a convenient enum to store these, `Elt`, in `rpn.rs`. ##### Operators You're only required to implement the following operations because ~~they're the ones that won out in a fight~~ implementing all operations would be tedious: _Addition_ adds the top two elements of the stack, or type-errors on booleans. _Negation_ computes logical-not of booleans, and inverts the sign of integers. _Equality_ compares the top two elements and pushes a boolean onto the stack. _Swap_ reverses the order of the top two elements on the list. _Random_ pops a value off of the stack; if that value is an integer `x`, it will use the `rand` library to generate a random number between `0` and `x` and push it onto the stack. Otherwise, it type-errors. _Quit_ exits the program. We're providing an `Op` type to represent the various operators you may encounter in the wild, located in `rpn.rs`. ##### Result Aliases When your calculator hits an error, it should exit. Conveniently, there's also a handy-dandy provided `Error` enum which defines a few different types of possible errors: undeflow, type mismatch, syntax, IO, and quit (which is not an error, but represents an exit value). Because you're a good software engineer, you're going to create your own `Result` alias for use in your calculator. Use the provided `Error` enum in `rpn.rs` to define an alias over `std::result::Result` - a similar aliasing can be seen in `std::io::Result`. ##### External Crates: `rand` `rand` is a Rust library which is part of the `rust-nursery` project. (It had previously been part of `std`, but was moved when Rust decided to downsize its standard library.) Good thing Cargo makes it so easy to add dependencies! To use `rand`, you'll need to add it as a dependency to your `Cargo.toml` file. [The guide][crates-guide] on crates.io (which hosts crates) explains how Cargo adds and manages project dependencies. You can find name and version information on [the page for `rand` on crates.io](https://crates.io/crates/rand) [crates-guide]: http://doc.crates.io/guide.html#adding-dependencies You also need to declare it (`extern crate rand`) in your crate's top-level file (`main.rs`) in order to import and use modules from `rand` in your project. ##### Stack API You should define a `Stack` struct, which should define at least the following public methods: ```rust /// Creates a new Stack pub fn new() -> Stack { unimplemented!() } /// Pushes a value onto the stack. pub fn push(&mut self, val: Elt) -> Result<()> { unimplemented!() } /// Tries to pop a value off of the stack. pub fn pop(&mut self) -> Result { unimplemented!() } /// Tries to evaluate an operator using values on the stack. pub fn eval(&mut self, op: Op) -> Result<()> { unimplemented!() } ``` You may implement the internals of your stack however you like, but we'd suggest using a `Vec`. ##### Calculator API The tokens your parser should recognize and their corresponding interpretations are as follows: | Input token | Action | | ----------- | ---------------------- | | any integer | push Elt::Int(integer) | | "true" | push Elt::Bool(true) | | "false" | push Elt::Bool(false) | | "+" | eval Op::Add | | "~" | eval Op::Neg | | "<->" | eval Op::Swap | | "=" | eval Op::Eq | | "#" | eval Op::Rand | | "quit" | eval Op::Quit | Any other input is considered an error. Your calculator can read multiple tokens on a single line, and will evaluate them in order. You can parse strings into integers by using `i32::from_str()`. We started two functions in `parser.rs` for reading and manipulating input: `read_eval_print_loop` will do just what it says on the tin: reads from `stdin`, evaluates some tokens, and then prints out a result. This tin comes with some ~~cookies~~ 🍰 stub code, which prints a prompt and then (`try!`s to) flush `stdout` so you can read input. `evaluate_line` is a helper function to `read_eval_print_loop`. It takes the input `String` and your calculator's `stack` and evaluates the tokens in that line. Currently, it takes the input string, trims off whitespace from the ends, and creates a `SplitWhitespace` iterator over the string. ## Tests Tests have been provided in the starter code. You can add as many tests as you want (but you don't need to write any). Obviously, you should test your calculator by using it. ## Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. Make sure it is visible on Github! This is your submission. (Work must be in the master branch at the due time.) `cargo test` should work to run all of the tests in your homework on stable Rust. Make sure you have written tests which cover every one of your functions. If you have any comments or feedback on this assignment, include them in the README of your submission. ================================================ FILE: hw05/README.md ================================================ # Homework 05: The ~~Fun-Time Reference Sharing~~ _Darkest_ Dungeon ### It's not just a phase, _Mom_! **Due Friday, 2016-02-26, 11:59pm.** For questions, please post on Piazza (Penn students) or Google Groups (other). Links on course homepage. ## Overview You have found yourself standing at the entrace of a large, mysterious, ~~smelly~~ castle. You must have taken a wrong turn when you were wandering through that dark, imposing, werewolfy forest. Why were you doing that? Nevermind, it's not relevant. Just go on inside. There's treasure. I think. There's definitely at least gold. This wouldn't be an adventure game if you couldn't even find gold! Ahem. Let me try this again: > _Wander forth for promises of grand treasures hidden in glamorous bejeweled chests and decrepit iron maidens, dangerous traps filled with poisonous spikes. Climb over the forgotten corpses of the fallen adventurers who have come before you yet were not as blessed with luck._ Wait, seriously, _where_ is that smell coming from? #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/d7fc86f713d77fa96deed930a9e75244 ## Instructions - In this assignment we are going to build a text adventure game! Okay, we lied. The castle doesn't exist yet. All you have is some poorly written down map that someone shoved into your hand earlier. They didn't even draw it out properly! It's written in some weird... "JSON" format. - Finish the JSON parser. The provided code parses a JSON file to create a series of rooms connected by hallways. See below for more detail. [*Look around you*][]. What can you see? Nothing! You haven't implemented eyes yet. That doesn't make any sense. What are you doing. Nevermind, nevermind. Stop asking questions. [*Look around you*]: https://www.youtube.com/watch?v=gaI6kBVyu00 - Print out a description of a room when you enter it, containing adjacent rooms accessable from your current location. We promised you gold, and so there shall be gold! Some of these rooms contain treasure and glory! Chests contain gold; food provides nourishment and restores health. Be sure to look closely: some curios, such as iron maidens and fallen adventurers, contain yet more items within them. But beware! There is danger afoot. When you encounter a spike trap or iron maiden, it will deal damage to your person. - Use all items in a room when you enter it, and output to the player ~~a tale of their misery~~ a description of what happened to them. All items are handled immediately but death visits at the end of the turn, so it is possible to take fatal damage from a spike trap but immediately cure yourself with a fistful of food to avoid death. Gold is added to your inventory. Items can only be used once, so they should be removed from a room once used. It wouldn't be an adventure if you were stuck in the entrance. You'd probably die of boredom before you even died of thirst. - Implement `go [destination]` to move from your current location to any room connected by a hallway. Now that you have the freedom of movement, you've finally come to a great, terrible, disgusting realization. You've remembered the legend of *the Wumpus*. The Wumpus is a large, repulsive, oozing monster from which that putrid smell emanates. The Wumpus will consume your being if you happen upon the room where it makes its lair. However, you are no defenseless peasant; you have prepared with your mighty bow and arrow. If you shoot an arrow toward the room a Wumpus lies in, it will surely kill the Wumpus, because you are a mighty warrior and all of your arrows fly true. Or maybe because the Wumpus is so big you can't possibly miss. - Implement `shoot [destination]` to shoot an arrow into a room and kill the Wumpus if it's there. When you've killed the Wumpus, congratulations! You have freed this castle from its terrible and malodorous curse. All the land rejoices in your honor. You are crowned as the exalted ruler for the people whose lives you have saved. There is no end of riches and luxury for you. Or something. Maybe you just go home and smell the roses. ## Demo If this doesn't make any sense, or you aren't familiar with text adventures, you can play this game on Eniac: ```rust ~cis198/local/bin/hw05 ~cis198/local/share/hw05/castle.json ``` ### How To Play After building your project, you can run it with `cargo run [castle_description.json]`. A sample castle is provided in `data/castle.json`. The available commands are `go [room]`, `shoot [room]`, and `quit`. Quit prints your score, whether or not you've won, and exits the game. Dying, when your health falls below 0, also results in exiting the game. Players have a location (`Rc>`, health (`i32`), gold (`i32`), and an account of whether they won (`bool`). Players can `Go` or `Shoot` a room. Movement between rooms should be implemented by replacing the stored `Rc>` owned by the player. ### Castle Structure A room in this castle contains a name (`String`), a list of contents (`Vec`), a list of halls (`Vec>`), and the possibility of a Wumpus (`bool`). Halls each connect two rooms (two `Option>>`s). See the provided JSON file (`data/castle.json)`: Rooms are defined by name, the number of randomly-generated curios, and whether the wumpus is in that room. Halls are defined as tuples (in as much as JSON allows tuples) between room numbers. Rooms are numbered in the order they are defined. Room names should be unique. Each room contains a set of Curios. The generation of Curios for a given room is provided. The JSON parsing is provided; you will start by finishing room and hallway generation. ## Testing No tests! Yay! We're not providing any tests nor expecting that you write any. We'll grade your game by simply playing it. ## Writeup Edit the README in your project with a (brief!) explanation of how `Rc` and `RefCell` are used and why they are necessary in this game. ## Make your game more game Apply as much creativity as you want to your game. Add your own maps, flavortext, additional curios, miscellaneous player effects. Extremely arbitrary numbers of brownie points (which can be redeemed for real brownies!) will be distributed for making your game more fun. Please do not edit the structure of Rooms and Halls. Otherwise, you're missing the point of this assignment! Highlight any interesting additions that you've made in your README. ## Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. Make sure it is visible on Github! This is your submission. (Work must be in the master branch at the due time.) If you have any comments or feedback on this assignment, include them in the README of your submission. ================================================ FILE: hw06/README.md ================================================ # Homework 6: BB-198 - A Bulletin Board System (BBS) **Due Sunday, 2016-03-06, 11:59pm.** The internet is the beginning of the hyper-connected future. Isn't it great? We're working to create a new _wireless_ social network, where people can communicate with their friends over the internet! We think the next big product in this space will be bulletin board systems, where people in all different places can gather and share information throughout the world. You've recently been hired as an engineer. Welcome to the team! Since we've heard that you have a lot of experience using Rust, you've been given responsibility for this BBS project that we've just started. Good luck! #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/c674a59f072e68a140e54599fcf35e8f ## Background ### [`hyper.rs`][] [`hyper.rs`]: http://hyper.rs > Hyper is a fast, modern HTTP implementation written in and for Rust. It is a low-level typesafe abstraction over raw HTTP, providing an elegant layer over "stringly-typed" HTTP. Hyper is the beginning of the future. Hyper contains both a server, which manually handles requests that it receives, and a client, which can build HTTP requests and parse HTTP responses. You will use both in this assignment. Hyper uses OpenSSL, which means OpenSSL needs to be installed. On OS X, you need to set environment variables telling Cargo where to find OpenSSL headers on your computer. Read [this StackOverflow post][osx_ssl_instructions] and follow the instructions. [osx_ssl_instructions]: http://stackoverflow.com/questions/34612395/openssl-crate-fails-compilation-on-mac-os-x-10-11 On Linux, you may need to install a development package for OpenSSL (this provides OpenSSL's header files, so Hyper can link against them). On Ubuntu, you can install this with `apt-get install libssl-dev`. ## Instructions ### lib We're going to build this BBS in a library crate, so it can be easily deployed to others who want to use it. This crate will contain `lib.rs` as before, which defines common functions and types used in the rest of your project. Additionally, the crate contains several binaries (located in `src/bin`). Each binary is a standalone Rust file, which uses the library as if it were an external crate. Here's what's defined in `lib.rs`: - `Message`: a struct representing a post on the bulletin board, containing the username and their message. - `UserClient`: a struct storing a username, the server address they make posts to, and a `hyper::Client`, which can be used to make multiple requests to a server. - `get_content`: a method which builds a GET request to the stored server address. - Some constants which are shared between binaries. - `SERVER_ADDR`, `BOT_ADDR`: the addresses that the server and bot will listen on. - `HTML_ADDR`: the addresses the BBS is visible at (i.e. the address you'd visit with browser) - `HTML_HEADER`, `HTML_DATA`, `HTML_FOOTER`: the local files which the server uses to generate the page. ### `bin/server` Provided: A basic web server which can respond to GET requests at http://127.0.0.1:1980 and serves a static web page. It generates this static web page by concatenating two files (the header, and footer). * You may need to change the port numbers from 1980 to others if you are working on Eniac, to avoid colliding with other servers. This is the _future_, though. Static webpages are so _nineties_. Add to the existing request handler (`req_handler`) the ability to receive posts through POST requests. The body of a POST request will contain a JSON-encoded `Message` (defined in `lib.rs`). You should append each post to the bulletin board's data page (`HTML_DATA`) when it's received. You should also update the GET request handler to include the posted messages when it generates a page. * For more context on RESTful APIs and the difference between GET and POST requests, you can read this [StackOverflow page][restful]. [restful]: https://stackoverflow.com/questions/671118/what-exactly-is-restful-programming Notice that, since the `bbs` library is external to this server, you need to declare it with `extern crate bbs` at the top of your file, and import parts as needed. ### `bin/client` Provided: A client which initializes a user and makes a GET request to read the current bulletin board. _Boring._ The user of this software should be able to post a message to the bulletin board. Otherwise, this wouldn't be an innovation at all! We'd like to provide our users with a clean, modern command-line interface for making posts to our bulletin board. It's up to you to design this. For example, you may retain the current command line interface (where the user specifies their username), and allow the user to post messages to the BBS by reading from standard input. ### `bin/bot` Oh no! You've put all of this hard work in, and your bulletin board still doesn't have any users. ~~Because you have no friends,~~ To help build activity and user interest, you decide to build a bot service which will automatically respond to certain types of messages on the board. This is exactly the sort of fun, exciting feature that people are looking for on the Web. Add a new binary to your BBS crate which contains a bot. This bot listens for TCP connections on port 1981 (as defined in `lib.rs`). Whenever the server receives a new post, it will make new connection to this bot, relaying the contents of the post. The bot will read the post and determine if it should respond. This is the future, so of course your bot will have to be connected to the internet. When the bot receives a message of the form "choose x y z", it will make a query to random.org's [HTTP API][random-api], asking for a value between 1 and the length of the options. You can use `hyper::client` to receive and parse these queries. Then, the bot should post a message back to the server with the appropriate choice (e.g. for a value of 2, your bot would post the message "y"). Bam. Random.org. _The future_. * You can easily build these query URLs with a format string. A simple example (as given [here][random-api]) will generate ten integers in base 10, between 1 and 6, returned as a plaintext page: https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new You are the head engineer on this project, so you are free to build any sort of bot functionality you want. If you want to do something different, just explain what you've done in your README. [random-api]: https://www.random.org/clients/http/ ### Feature List To recap, here are all the features you should implement: - In `server.rs`: - Handle `POST` requests in `req_handler` - Update the `GET` handler in `req_handler` to generate a page containing the post data - In `client.rs`: - Create a command-line interface to allow users to easily send data to the server - In `bot.rs`: - Listen on port 1981 for incoming TCP connections to determine if new posts have been made to the BBS - Create a function to randomly choose "x", "y", or "z" from posts of the form "choose x y z" using random.org's HTTP API ## Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. Make sure it is visible on Github! This is your submission. (Work must be in the master branch at the deadline.) If you have any comments or feedback on this assignment, include them in the README of your submission. ================================================ FILE: hw07/README.md ================================================ # Homework 7: A Multithreaded Chat System **Due Wednesday, 2016-03-23, 11:59pm.** Okay, you admit it - a BBS isn't exactly the most modern web technology of all time. Inspired by the rising success of this newfangled "AOL Instant Messenger" thing all the kids are using, you've decided to make your own chat service! In this homework, you'll write your own multithreaded, multi-user, IRC-esque chat service! And as a bonus, it'll run in the browser using WebSockets! #### Classroom for GitHub We're using Classroom for GitHub, which manages private homework repositories for students. To create your very own private homework repository (owned by us), click this link: * https://classroom.github.com/assignment-invitations/8380d7698ae30e972cf46d5c80daa2a8 ## Background **This assignment should work on [any modern browser](http://caniuse.com/#feat=websockets); tested on Firefox and Chrome.** ### WebSockets WebSockets are a web technology for allowing JavaScript in webpages to connect back to servers using a bidirectional data stream. WebSockets behave very similarly to TCP streams (but are message-based). We will be using the `rust-websocket` library. You'll definitely need to take a look at some of the documentation and examples: * [GitHub repo](https://github.com/cyderize/rust-websocket) * [Documentation](http://cyderize.github.io/rust-websocket/doc/websocket/) * [Example server](https://github.com/cyderize/rust-websocket/blob/master/examples/server.rs) ### Multithreaded Networking In this assignment, we'll be using threads to easily handle several network clients at once. One way to do this is to create one thread for each client. As clients connect, a thread is spawned to manage that particular connection: ```rust // fn listen for connection in server { // Spawn a client_thread. } ``` In this paradigm, each _client thread_ will _block_ as it waits for data to come in from its respective client. This can be expressed very elegantly using iterators: every time a message comes in, the loop runs once. When the channel closes, the loop terminates. ```rust // fn client_thread for message in client_recv.incoming_messages() { // Handle message; relay via MPSC channel. } ``` In the _relay thread_, each message received via the MPSC channel should be sent to all of the clients (including the originator). Once again, iterators allow us to use the MPSC receiver very nicely: ```rust // fn relay_thread for action in relay_mpsc_recv { // Send message to all clients. } ``` **Aside:** For many applications, the one-thread-per-client model does not work well. For systems which will have many (thousands or more) clients, spawning thousands of threads is very inefficient. In these cases, a more complex system will typically be used; for example, several threads (usually about one per core) might run in a thread pool, where each _asynchronously_ handles many clients. That is, each thread will periodically poll for incoming data from each client (_non-blocking_), rather than waiting for incoming data from a single client (_blocking_). For `rust-websocket`, there is a discussion [here](https://github.com/cyderize/rust-websocket/issues/6). ### IRC Principals IRC (Internet Relay Chat) operates on very simple principals: * Maintain a list of all of the currently connected clients. * For each message that comes in, relay it back out to all of the clients. We won't be implementing IRC precisely, but we will use the "relay" idea. ## Instructions `main.rs` and `webpage.rs` are provided for you; they just serve a static HTML webpage over **port 1980**. To access this, just run the server (`cargo run`) and open [localhost:1980](http://localhost:1980/) in a web browser. The static webpage is also written for you. Your job is to write `chatserver.rs`. In `chatserver::start`, you should spawn a thread to listen for incoming WebSockets connections on **port 1981**. We've also given you a (de)serializable `enum ChatAction` - don't modify this; the JavaScript code depends on it! **Note:** The messages received-from and sent-to the client should be JSON object strings with the same form as `rustc_serialize`'s serialization of the `ChatAction` type. This means you can `json::encode` `ChatAction`s to create text to send to the web browser, and `json::decode` to turn the web browser's messages into `ChatAction`s. The [example server](https://github.com/cyderize/rust-websocket/blob/master/examples/server.rs) will be an important resource - you'll use a lot of the same boilerplate code. (Note: we aren't using a protocol; the protocol response isn't necessary.) Now, go check out the comments left in `chatserver.rs`! ## Submission Commit and push your work to the master branch of your Classroom for Github repository for this HW. Make sure it is visible on Github! This is your submission. (Work must be in the master branch at the deadline.) If you have any comments or feedback on this assignment, include them in the README of your submission or post on Piazza or the Google Group.