6.005 Software Construction
|
Before you start coding
- JDK 6 (for Windows or Linux; Mac users probably already have Java): From this web page, download JDK 6 Update 30. (You don't need NetBeans or Java EE.)
Note: This is not the latest version of Java. During grading, we will compile your code using Java 6, so be sure to always use Java 6 while working on your problem sets. - Eclipse 4.2: Choose "Eclipse IDE for Java Developers," which will download a ZIP file. Unpack the ZIP file, go inside the resulting folder, and run Eclipse. Make sure Eclipse is configured to use Java 6 by doing the following:
- Click Window → Preferences → Java → Installed JREs and ensure that jre6 is the only one checked (on a Mac, the menu is Eclipse → Preferences → Java → Installed JREs).
- Also in Preferences, click Java → Compiler and set "Compiler compliance level" to 1.6. Click OK and Yes on any prompts.
- Git: We will be using the command-line version of Git. The page should prompt you to download the appropriate version. Follow the instructions in the README file in the downloaded ZIP or DMG file.
Note for Windows users: we recommend checking the option to enable Git from the windows command prompt during installation.
Athena already has Eclipse, the JDK, and Git installed. To run Eclipse on Athena, follow these steps:
- From the terminal, run
add eclipse-sdk
to add the eclipse locker to your Athena environment. - From the terminal, run
eclipse
to start Eclipse. - Make sure you configure Eclipse to use Java 6 as above.
Eclipse
The Eclipse integrated development environment (IDE) is a powerful, flexible, complicated, and occasionally frustrating set of tools for writing, modifying, and debugging programs. It is especially useful for working in Java.
When you run Eclipse, you will be prompted for a "workspace" directory, which is where Eclipse will store its configuration and metadata. On Athena, for example, the default location is a directory called workspace in your home directory. You should not run more than one copy of Eclipse at the same time with the same workspace, or the metadata will become corrupted.
The first time you run Eclipse, it will show you a welcome screen. Click the button to go directly to the "Workbench" and you're ready to begin.
You may also store your code in the workspace directory, but this is not required, and is occasionally confusing. On the left side of your Eclipse window is the Package Explorer, which shows you all the projects in your workspace. The Package Explorer looks like a file browser, but it is not. Don’t be fooled — it rearranges files and folders, includes things that are not files or folders, and can include projects stored anywhere on disk that have been added (but not copied into) to the workspace.
Tab policy
At this time you should also go through and change tabs to spaces. This will ensure that your code looks the same in all editors, regardless of the configured width of a tab character in each editor. Go to Window (Eclipse if you're running a Mac) → Preferences... In the toolbar go to Java → Code Style → Formatter. Click the "Edit..." button next to the active profile. In the new window you should change the Tab policy to "Spaces only." Keep the Indentation size and Tab size at 4. Enter a new name for the policy and press OK.
Git
To turn in this problem set, you will commit and push your code to a Git repository. The last code committed and pushed before the problem set due date will be used for grading.
Git is a Version Control System (VCS). If you have used other version control software before, like SVN or CVS, many of the concepts and procedures of git will be familiar to you. The "Pro Git" book is available from the git documentation page; it describes what git is and how to use it. Quote:
What is version control, and why should you care? Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. [...] It allows you to revert files back to a previous state, revert the entire project back to a previous state, compare changes over time, see who last modified something that might be causing a problem, who introduced an issue and when, and more. Using a VCS also generally means that if you screw things up or lose files, you can easily recover.
Quite simplified, below are some important git concepts to know about:
- repository: A folder containing all the files associated with a project (e.g. a 6.005 problem set or team project), as well as the entire history of commits to those files.
- commit (or "revision"): A snapshot of the files in a repository at a given point in time.
- add (or "stage"): Before changes to a file can be committed to a repository, the files in question must be added or staged (before each commit). This lets you commit changes to only certain files of your choosing at a time, but can also be a bit of a pain if you accidentally forget to add all the files you wanted to commit before committing. If you are committing to git using Eclipse, Eclipse will help you select which files to add before committing.
- clone: Since git is a "distributed" version control system, there is no concept of a centralized git "server" that holds the latest official version of your code. Instead, developers "clone" remote repositories that contain files they want access to, and then commit to their local clones. Only when they push their local commits to the original remote repository are other developers able to see their changes.
- push: The act of writing your local commits to a remote repository. Again, until you add, commit and push your changes, no one else can see them.
- pull: The act of retrieving commits made to a remote repository and writing them into your local repository. This is how you are able to see commits made by others after the time at which you made an initial clone.
We will explain Git more deeply in a later lecture. For now, you can simply follow the instructions in this handout. If you want to go through an interactive tutorial, Code School's "Try Git" will show you these commands with a remote repository on GitHub.
Git in 6.005 Problem Sets
For each problem set in 6.005, we will create a git repository for you at "/afs/athena.mit.edu/course/6/6.005/git/sp13/ps[problem set number]/[your athena name].git".
Initially this "remote" repository will contain some template code that we provide. To work on a problem set, you need to clone this remote repository into a local repository in your Eclipse workspace. To hand in your work, you need to both commit your changes to the local repository and push them to the remote repository. When the time comes for grading your assignments, we will clone the remote repository (at the location above), and look at the last commit you made and pushed there before the deadline.
We may also occasionally publish changes to the initial starting code for a problem set. In this case we will tell you to pull from a repository at "/afs/athena.mit.edu/course/6/6.005/git/sp13/ps[problem set number]/errata.git".
Cloning your repository
Open the terminal and change to the directory where you would like to store your code; for example, your Eclipse workspace directory.
If you are working on Athena, you have direct access to your remote repository over AFS. In the terminal, run
git clone file:///afs/athena.mit.edu/course/6/6.005/git/sp13/psets/ps0/[username].git ps0
Otherwise, you will access the remote repository over SSH. In the terminal, run (all on one line)
git clone ssh://[username]@athena.dialup.mit.edu/afs/athena.mit.edu/course/6/6.005/git/sp13/psets/ps0/[username].git ps0
The result should be a directory named ps0. This directory contains the starting code for the problem set. Keep the terminal open—you will need it to commit your code (you can always close your terminal and come back to the same directory to commit, of course).
Configuring Git
Now that you have set up your first Git repository, there are a few ways in which you should configure Git on your computer before continuing. Please follow the instructions described here to configure Git.
Editing in Eclipse
After cloning your repository, you need to add the project to Eclipse so you can work on it.
- In Eclipse, go to File → Import... → Git → Projects from Git
- On the "Select Repository Source" page, select Local
- On "Select a Git Repository," click Add..., and Browse... to the directory of your clone
- The "Search results" list should show your clone, with ".git" at the end; click "Finish"
- On "Select a wizard" for importing, choose "Import existing projects"
- Finally, on "Import projects," make sure ps0 is checked, and click "Finish"
Note: if you encounter issues with Git support in Eclipse, you can import without telling Eclipse that your project is a Git repository:
- Go to File → Import... → General → Existing Projects into Workspace
- Browse... to your clone
- Make sure the project is checked, make sure "Copy projects into workspace" is not checked, and click "Finish"
Turtle Graphics and the Logo Language
Logo is a programming language created at MIT that originally was used to move a robot around in space. Turtle graphics, added to the Logo language, allows programmers to issue a series of commands to an on-screen "turtle" that moves, drawing a line as it goes. Turtle graphics have also been added to many different programming languages, including Python, where it is part of the standard library.
We will be playing with a simple version of turtle graphics for Java that contains a restricted subset of the Logo language:
forward(units)
Moves the turtle in the current direction by units pixels, where units is an integer. Following the original Logo convention, the turtle starts out facing up.turn(angle)
Rotates the turtle by angle degrees to the right (clockwise), where angle is a double precision floating point number.
Don’t worry about the commands to create a new window or to draw the window; those are in the actual testcases themselves, in case you’re curious. Do NOT use any turtle commands other than those listed above in your code.
Warmup: drawSquare(), Staging, Commits, and Pushing
Look at the source code contained in TurtleSoup.java. The first task is to implement drawSquare(Turtle turtle, int sideLength), using the two methods introduced above: forward() and turn().
Once you've implemented the method, run the main() method in TurtleSoup.java. main() is the entry point for Java programs, and if you take a look at the source, the method simply creates a new turtle, calls your drawSquare() method, and instructs the turtle to draw. Run the method by going to Run → Run As... → Java Application in Eclipse. A window will pop up, and, once you click the "Run!" button, you should see a square get drawn.
Committing & Pushing
After you've finished implementing the function and verified it is correct, let's do a first commit.
First, in the terminal, chance directory to your clone, and take a look around with
git status
which shows you files that have been created, deleted, and modified in the project directory. You should see TurtleSoup.java listed under "Changes not staged for commit." This means Git sees the change, but you have not (yet) asked Git to include the change as part of your next commit.
You can run the command
git diff
to see your changes.
Before committing, files must be staged for commit. Staging a file is as simple as:
git add <filename>
so use
git add src/turtle/TurtleSoup.java
to stage the file.
In addition, it’s always a good idea to review your commits before committing to them. Run
git status
again to see that your changes are now listed under "Changes to be committed." If you run
git diff
those changes are no longer shown! Use
git diff --staged
to see exactly what Git will record if you commit now.
Ready? To perform the commit,
git commit
will actually commit the changes locally, after opening your default editor to allow you to write a commit message (which you should always do!). Your message should be formatted according to the Git standard: a short summary that fits on one line, followed by a blank line and a longer description if necessary.
You can use the command
git log
to see the history of commits in your project. Right now, you should see two of them: the initial commit to create your problem set repository with the starting code, and the commit you made just now.
Important: only the local history has the new commit at this point; it is not stored in your remote repository. This is one important aspect where Git is different from centralized systems such as Subversion and CVS.
In order to share the changes with your remote repository (which is the one we will be using for grading), you need to push to the remote repository, with
git push origin
At this point, the remote repository now has the same history as your local repository. It is important to remember that we will be grading the history in the repository on Athena; if you forget to push, we won't see your commits.
One thing you will notice is that our infrastructure will, on commit, run a subset of the autograder on your code. Most everything will fail since you've only implemented one method, but, as the deadline approaches, this should give you feedback to make sure we can correctly compile and run your code.
Unit Testing
For the drawSquare() method, we used the main() method plus some visual inspection to verify that the implementation was correct. More generally, programs will have many dozens of methods that need to be tested; visually inspecting output for each one is fragile, time-consuming, and inherently non-scalable.
Instead, we will use automated unit testing, which runs a suite of tests to automatically test whether the implementations are correct. In PS0, methods that do not draw to screen will use unit tests; we'll talk more about unit testing GUIs later in the course.
Automated Unit Testing with JUnit
JUnit is a widely-adopted Java unit testing library, and we will use it heavily in 6.005. A major component of the 6.005 design philosophy is to decompose problems into minimal, orthogonal units, which can be assembled into the larger modules that form the finished program. One benefit of this approach is that each unit can be tested thoroughly, independently of others, so that faults can be quickly isolated and corrected, as code is rewritten and modules are configured. Unit testing is the technique of writing tests for the smallest testable pieces of functionality, to allow for the flexible and organic evolution of complex, correct systems.
By writing thoughtful unit tests, it is possible to verify the correctness of one's code, and to be confident that the resulting programs behave as expected. In 6.005, we will use JUnit version 4.
The Anatomy of JUnit
JUnit unit tests are written method by method. There is nothing special a class has to do to be used by JUnit; it only need contain methods that JUnit knows to call, which will be referred to as test methods for the remainder of the problem set. Test methods are specified entirely through annotations, which may be thought of as keywords (more specifically, they are a type of metadata), that can be attached to individual methods and classes. Though they do not themselves change the meaning of a Java program, at run-time other Java code can detect the annotations of methods and classes, and make decisions accordingly. The Java annotation system, judiciously used, can create dynamic and powerful code. Though we will not deeply explore annotations in 6.005, you will see how other libraries, such as JUnit, make effective use of them.
Look closely at TurtleSoupTest.java, and note the @Test that precede method definitions. These are examples of annotations. The JUnit library uses these particular annotations to determine which methods to call when running unit tests. The @Test annotation denotes a test method; there can be any number in a single class. Even if one test method fails, the others will be run. The test methods can contain calls to assertEquals, which is an assertion that compares two objects against each other and fails if they are not equal, assertTrue which checks if the condition is true, and assertFalse which checks if the condition is false. Here is a list of the other assertions supported by JUnit. If an assertion in a test method fails, that test method returns immediately, and JUnit records a failure for that test.
Running Existing Tests
To run the tests in TurtleSoupTest, simply right click on the TurtleSoupTest.java file in either your Package Explorer, Project View, or Navigator View, and mouse-over the "Run As" option. Click on the "JUnit Test" option, and you should see the JUnit view appear, with a green bar indicating that all test methods ran successfully.
To see what a test failure looks like, try running TurtleSoupTest.java before making any changes to TurtleSoup. You should now see a red bar in the JUnit view, and if you click on a test in the view, you will see a stack trace in the bottom box, which provides a brief explanation of what went wrong. In this case, TurtleSoup threw a RuntimeException. Double clicking on lines in the Failure Trace will bring up the code for the test that failed.
When a test succeeds, you will see that a checkmark next to that test. Passing the public JUnit tests does not necessarily mean that your code is perfect. You should still look over the function specifications carefully and feel free to write your own JUnit tests to verify your code.
For a more thorough introduction, O'Reilly has a JUnit and Eclipse tutorial, with screenshots to help you get acquainted with using JUnit from within Eclipse. The guide was written for JUnit 3, so the code samples use the older (but still supported) JUnit API.
Implementing other methods
For detailed requirements, read the specifications of each function to be implemented above its function signature in TurtleSoup.java. Be careful when dealing with mixed integer and floating point calculations.
You should not change any of the method signatures (what's a signature?) below.
If you do so, you risk receiving zero points on the problem set.
-
Implement
calculateRegularPolygonAngle()
There’s a simple formula for what the inside angles of a regular polygon should be; try to derive it before googling/binging/duckduckgoing.Run the unit tests again; the method that tests
calculateRegularPolygonAngle()should now pass and show green instead of red. Implement
calculatePolygonSidesFromAngle()
This does the inverse of the last function; again, use the simple formula. However, make sure you correctly round to the nearest integer. Instead of implementing your own rounding, look at Java'sjava.lang.Math libraryfor the proper function to use.Implement
drawRegularPolygon()
Use your implementation ofcalculateRegularPolygonAngle(). To test this, change themain()method to calldrawRegularPolygon()and verify that you see what you expect.Implement
calculateHeadingToPoint()
This function calculates the parameter toturn()required to get from a current point to a target point, with the current direction as an additional parameter. For example, if the turtle is at (0,1) facing 30 degrees, and must get to (0,0), it must turn an additional 150 degrees, socalculateHeadingToPoint(30, 0, 1, 0, 0)would return150.0.Implement
calculateHeadings()
Make sure to use yourcalculateHeadingToPoint()implementation here. For information on how to use Java'sListinterface and classes implementing it, look upjava.util.Listin the Java library documentation. Note that for a list of n points, you will return n-1 heading adjustments; this list of adjustments could be used to guide the turtle to each point in the list. For example, if the input lists consisted ofxCoords=[0,0,1,1]andyCoords=[1,0,0,1](representing points (0,1), (0,0), (1,0), and (1,1)), the returned list would consist of[180.0, 270.0, 270.0].Implement
drawPersonalArt()
In this function, you have the freedom to draw any piece of art you wish; we will run a poll where your fellow students will rate your art and the winner will win a prize: a gift certificate to Toscanini's. Here are some examples of the kinds of images you can generate procedurally with turtle graphics, though note that many of them use more commands than what we've provided here. Modify themain()method to see the results of your function.
Optional Challenge Problem: Implement drawSierpinskiTriangle().
As outlined in the General Information handout, these challenge problems are completely optional and are not counted as part of the PS0 grade; however, solving enough of these challenging problems will enable you to earn an A+ grade in the course. As such, the 6.005 staff will not help or answer many questions about the challenge problems.

(Image source: Scott Sutherland, SUNY Stony Brook)
A Sierpinski triangle is a fractal structure that can be approximated by recursively drawing triangles, among other methods. Your task is to implement a function to draw a Sierpinski triangle given the recursion depth; you should use one of the recursive algorithms, not one of the randomized ones. Furthermore, do not use algorithms that require filled/unfilled triangles; you just need to draw the lines. Finally, the length of a side is not a parameter to the algorithm; instead, implement your algorithm so that the minimum side length (in other words, the length of a side at the smallest recursion level) is distance 4. Don’t worry if, at large recursion depths, part of the triangle is off screen.
drawSierpinskiTriangle(0) should draw a single triangle, and drawSierpinksiTriangle(1) should draw a larger triangle with 4 triangles inside. At these small sizes, you won't be able to see much if you try to draw. We will run your examples with higher recursion depths.
Super Bonus: draw the Sierpinski triangle without retracing a single line (you may use an auxillary function).
Submitting
Make sure you commit AND push your changes to the repository!
We will use the state of your repository on Athena as of 5:00pm on the deadline date.
When you git push, the continuous build system attempts to compile your code and run a subset of the autograder tests.
You can always review your build results at didit.csail.mit.edu.
This feedback is provided on a best-effort basis:
- There is no guarantee that Didit tests will run within any particular timeframe, or at all. If you push code close to the deadline, the large number of submissions will slow the turnaround time before your code is examined.
- You do not need to have a successful Didit run before the deadline in order to be properly graded.
- Passing some or all of the public tests on Didit is no guarantee that you will pass the full battery of autograding tests — but failing them is almost sure to mean lost points on the problem set.