Programming Project 2

This semester you will be writing a program that will be a very basic air traffic controller system. Rather than modeling air traffic in the sky, you will only have to implement the portion of the air traffic controller that is responsible for managing aircraft on the ground. Though this may seem easy, runway incursions are a dangerous and common occurrence, and their prevention is an active area of research even today.

Rather than write this project at once, we will break the project down into several two-week sub-projects that are due throughout the semester. The rest of this document will detail the first such assignment.

PROJECT UPDATES AND CLARIFICATIONS

This first assignment will ask you to write some code that will help you determine some properties of aircraft somewhere on the grounds of an airport. You will complete two functions available in the template file for project2_template.py. You should save that template file under the name project2.py.

You will have to do three things for this assignment:

Step 1: Writing Test Cases
Ideally, you should write your test cases before you write your code. In doing so, your test cases will all initially fail (because you haven't written any code), but that's fine. So, next we're going to explain what the two functions above are supposed to do, and then we'll explain how to format your test cases so you can get credit for them on Marmoset, and how you can run these test cases on code you will write.

First, we re going to write a function to check when two tiles are next to each other. For example, we might want to know if one aircraft is close to a gate, another aircraft, or the runway. We will assume that all airports are rectangular in shape. The airport is conceptually imagined as a grid. The checkNeighbor function takes as arguments, or already knows the value of, the width (x-axis), height (y-axis), a current tile called me, and another tile called neighbor. In general the tiles of a board are always numbered, starting at one. For example, on a map with a width of 4 and a height of 3, the tiles would be labeled as:
 1   2   3   4 
 5   6   7   8 
 9  10 11 12

Obviously this is a very small airport! For this project, the width and height of the airport can change, depending on what arguments are passed into this function.

Your function should determine if the neighbor tile is directly adjacent to the me tile, and return either True or False (as boolean values), depending on the arguments passed in. For example, a call to the function with checkNeighbor(4,3,1,5) (here 4 is the width, 3 is the height, and 1 is the me tile, and 5 is the neighbor tile argument) would return the value True. A call to checkNeighbor(4,3,1,3) would return the value False. Tiles must be unique to be adjacent; if they are not unique, they are not adjacent. If the map provided is invalid, (i.e. its width or height are not natural numbers), your function should return a string of invalid map!. If any of the tiles provided do not fit on the map (which includes, among other things, them not being natural numbers), your function should return the string Invalid tile!. Two tiles are adjacent even if they share just a single point. Also, be careful of the wording of the strings you return (and they don't need any quotes around them in your test cases, unlike project 1).
You may assume this code will never be tested on floats that are also integers, like 1.0 or 5.0 (no need to write those test cases).
For the second function, we're going to calculate the distance between two points on a map. A function, getDistance, will take as argument the width, height, first, and second tiles. It will return the smallest distance, in terms of the length of tiles, between the two tiles on the map. For example, getDistance(3,3,1,9) will return 1.414. You should use the Pythagorean theorem to calculate this distance. However, you cannot simply use the formula as given; you need to modify it to be able to calculate the shortest distance between two tiles (i.e., for this project, the shortest distance is NOT between the center of the tiles, but along their edges). For example, the distance between two adjacent tiles (and/or tiles that share a single point) is zero. If the distance is not a whole number, the function will return the floating point decimal truncated to five characters total (i.e. 34.5678 becomes 34.56, and 4.34556 becomes 4.345); otherwise it will return the whole number as an integer.

You should assume that the arguments passed into this function will always be valid natural numbers of integer type.

Now you should be ready to write test cases for your code, once you understand what arguments each function is expecting, and what it will return and when. For this project, we will expect your test cases to be written to a file called tests.txt (right mouse click and save the file - do not try to click on it in your browser and copy the contents - you will miss the newline at the end). The example shows you the format for one test case of checkNeighbor and one test case of getDistance. Each test case is two lines long: the first line contains the name of the function, followed by a space, followed by all of the arguments to the function, each separated by a single space, and terminated by a newline. Then, on the next line, you should provide the expected answer for that function call, terminated by a newline. In your tests, make sure you include spaces where they need to be, otherwise they will not diff.

You should write at least 50 cases, and up to 300, for this project (I have 141 test cases that I will use to test your project, but even these are not close to exhaustive. You can write more than 300 but Marmoset will only grade the first 300). This should not take very long, as each test case is just two lines long. Your test cases will be graded on Marmoset and are due a week before the project due date. When you are satisfied you have enough quality test cases, you will have to convert your tests.txt file to something that Marmoset will understand. We have provided a file for you, called DriverBuild.class (right-mouse click and save this file into the same directory as all of your other files for this project). To use this file, from a terminal in that directory, type:

java DriverBuild tests.txt

This will create a file called DriverJava.java that you will submit as your test cases for this project. Note that you do not need to know or worry about how DriverBuild.class works (you will be learning about Java in CS211 if you take it), except each time you type the commands above, it will overwrite any older version of DriverJava.java in that directory. Note that this is a delicate process, and if your test cases are malformed in any way, the DriverBuild program will not be able to correctly convert them to something that Marmoset understands. If you see any error messages from Marmoset regarding "compilation", this means that your test file was not properly formatted, so you need to go back, fix the format, re-run DriverBuild, and resubmit to Marmoset. Marmoset will grade the quality of your test suite.

See the submission instructions at the bottom of this page for how to submit your test cases. Because each test (public and private) must run your entire test suite, this will be a slow process (it took over a minute for me to grade my test suite on Marmoset, when no one else was using the system). Do not wait until the last minute to do this part of the assignment!
Step 2: Writing Code
Once you have written your test cases (and submitted just the test cases to Marmoset), you can begin to write your code for the project in the file project2.py (and remember the link to the template for this file above). You should get started writing your python code as soon as all of you finish your test suite and it passes both the public and release tests on Marmoset -- do not wait!

You should avoid writing print statements in your code, as Marmoset cannot handle them. Instead of print statements, the template file comes with a debug function (see the two examples) that you can use just like a print statement. In the template file, you will have to choose which version of python you want, and comment out the other version on lines 13 and 14 (the default right now has the newer version of python enabled, but you can change it to the older by commenting out line 13 and uncommenting line 14). When you are ready to submit your code to Marmoset, set the variable MARMOSET on line 7 to the value True to disable debug printing. If you need to re-enable it, just reset the value to False. uncomment that print statement inside the debug function. When you're ready to submit to Marmoset, re-comment that line you changed (so the debug code looks like the original template), otherwise you will get the error in Piazza post 281.

You must come up with your own formula for the two functions. This problem is mathematically very simple, and can be solved with just addition/subtraction, multiplication/division, modulus, and raising a number to a half power (the power operator in Python is **, so 10**2 is equal to 100. Use 0.5 and not 1/2 in your code. You will need to derive this formula on your own; discussing it with other students (including Piazza) is considered an Honor Code violation.

Where do you start writing your code? Pseudocode! First, you will want to think of some mathematical formulas or rules for determining when two tiles are neighbors. You shouldn't need anything more complicated than addition, subtraction, division, and/or multiplication operations. For example, it's very easy to determine if two tiles on the same row are next to each other; it gets harder when they are on different rows. Think up some simple mathematical formulas to describe when two tiles are adjacent if they are on the same row; then expand upon these formulas to account for cases where the tiles are on different rows.

How about the distance formula? The Pythagorean theorem is simple; figure out how to calculate the X and Y coordinates of both tiles. Be careful though; we're looking for the shortest distance, and since each tile has a width and height of 1, it matters which edge-point you're trying to measure from. Draw some examples!

To get the first five characters of a string, you can place [0:5] after the string (we will cover this in more detail later). For example, if city holds the string "Washington", then city[0:5] would evaluate to the substring Washi.

You may find the and, or, is, is not, in, numeric comparison operators (<,>,<=,>=), type(x), int(x), str(x), and the abs(x) function useful for this project. You will also have to use if-else statements. You will need the return statement to get your functions to return a value - DO NOT use the print statement for this! Remember not to use print statements in this project, in general - they should either be return statements or calls to the debug function, as described above.


Step 3: Testing Your Code on Your Test Suite
Once you have finished writing your python code, you will first want to test your python code with the test suite you wrote, before testing your python code on Marmoset. Testing your python code at home will give you instant results, while you'll have to wait a bit to test it on Marmoset. Ideally, your test suite is well written, and it's testing for almost everything (or even everything!) the release tests for the python code on Marmoset are checking too. To help you use your test cases, I have included two driver files, driver_v2.py and driver_v3.py, that will use your tests.txt file and print out whether or not the tests you wrote passed or failed, on the python code you wrote. First, decide which version of python you're using; for the older version of Python, you'll want driver_v2.py, and for the newer version of python you'll want driver_v3.py. To use this driver, make sure your project2.py, tests.txt, and driver_v2.py or driver_v3.py are all in the same directory, and from a terminal in that directory, type either:

python driver_v2.py

or, if you're using the new version,

python3 driver_v3.py

If you want this driver to stop running after a certain test (so you don't have to scroll through everything when you debug), simply put the word stop on a single line after the last test you want run in your tests.txt.


Step 4: Submitting Your Code to Marmoset.
Once your code passes all your tests at home, you're ready to submit to Marmoset. See the instructions at the bottom of this page for Marmoset submission.

In an effort to get students to do their testing at home using their test suite, rather than on Marmoset, you will need to disable all of your debug/print statements from your code before submitting to Marmoset. Do not delete your debugging statements (you may need them later); just disable them using the instructions above. Note that you DO NOT have to disable your debug statements to run your test suite at home using driver_vX.py. Marmoset submission should be a last and rare step! If you have any print statements in your code, Marmoset will give you a zero - use the debug function instead!


Sample Input and Output

Your python code will be tested on Marmoset in the same manner as your test cases. Two sample tests have been provided in the tests.txt above: these tests are the Public Tests on Marmoset.
Project Hints and Guidelines

Remember, when designing your own test cases, try to do so in a thoughtful and structured approach, as we have done in class. What is the smallest possible board you could call your functions on? What is the next smallest one? What are all the corner cases?

Other hints and guidelines:
Project Grading

The project will be worth 100 points:

Project Submission

Do not submit the same files multiple times to try and get Marmoset to grade them faster (because it works like this). You will only slow down the results for yourself and the rest of the class. All requests are handled in order.

There are two due dates for the two different parts of the project (test cases and code). Once a due date passes you cannot resubmit that part of the assignment. However, you may submit either or part of both assignments before the first due date, if you are done early.

DUE DATE 1: Friday 2/14/2013 at 4:55pm: Test cases due. Submit ONLY your DriverJava.java file (see above for how to convert your tests.txt file into this DriverJava.java file) on Marmoset following the link to CS112-2T. (The T stands for TEST)

DUE DATE 2: Friday 2/21/2013 at 4:55pm: Code is due. In order to have your code work with Marmoset, you must also submit two files code.py and SystemCall.java. Again, you do not need to know how these files work (or even open them); just make sure not to change them, and submit them with your project2.py. Normally, Marmoset is set up to work with Java files, which is why we have this workaround. Submit ONLY your project2.py, code.py and SystemCall.java files on Marmoset following the link to CS112-2C. (the C stands for CODE). Find the link on Marmoset to submit Project 2. Once you pass all the public tests, use your tokens wisely to start examining the release tests. Do not change the name of the files.

You may make as many submissions to Marmoset as you like, before the due date, but we will only grade the highest score. Remember to read and adhere to all of the information regarding projects and their submission and grading on the course syllabus. Remember that Marmoset can be slow due to heavy load, and under no circumstances will project due dates be extended because of this: get stared early.
Allowable resources: Class textbook, Python standard documentation Lecture & Lab Instructors. You may not look at or share other students' code in any manner. You may not look at or share test cases with other students. You may NOT work together or talk to other people (including outside sources besides the professor and GTA/UTAs) about the project. All work must be your own.