Course:CPSC312-2016-Project2-Air-Traffic-Control

From UBC Wiki

Title

Authors: Maggie Chen, Brandon Sanderson

What is the problem?

We want to implement ATC (air traffic control) game from bsd games: http://manpages.ubuntu.com/manpages/trusty/man6/atc.6.html This is a simple (and surprisingly hard) game where you're air traffic control for a couple of airports. The original game is ncurses/terminal based and so it's annoying to use.

What is the something extra?

The "something extra" we want to do is implementing a GUI for the game, since it was originally a terminal game

What did we learn from doing this?

We learned that programs with a lot of changing state seem to get complicated really fast in Haskell, because you can't encapsulate state like you would in Java/C++. Our game state structure quickly filled up with lots of information (airports, exits, time ticker, error message, current input command, etc.), and this needed to be taken into account every time we referenced the state. We can't just use state.getExits(). Instead we need to say that the state structure has a list of exits in the input parameter of our function, and reference that variable when we need to use it.

Doing imperative-style programming in Haskell also got confusing at times, because we would get errors that complained that IO <Type> didn't match with <Type>. We also managed to get a memory leak when we forgot to free the memory used for loading textures (which we used to display error messages and plane/exit labels).

Updating the state of every plane every couple of seconds was also difficult, because Haskell is side effect free, so doing a map on all the planes and returning an IO for each does not actually execute the code in the IO. Because we don't use the result of the function returning the IO, we don't actually execute the code inside of it. We had to use Haskell's sequence function to force execution.

Parsing the plane commands was also pretty tricky. We learned that if you don't account for every case (e.g. one character, no character, the wrong character, etc), Haskell throws an error because it fails to unify. Also, if you want informative error messages, you basically have to analyze the value of each letter, one at a time, validating the command, and then executing the command (which may require another round of unification) if it is valid, or updating the error message in the game state with something useful.

Rotational moving was very interesting. The "<planeId>t<direction>" command allows you to tell the plane to turn to an absolute direction, and the plane is supposed to take the shortest rotational path (clockwise/counterclockwise) to get to that direction. What made this difficult was that unlike the altitude field, angles go back to value 0 after 359. We had to make sure we were getting the right direction, make sure the new angle that we updated to would be between 0 and 359 (and not, for example, -90), and prevent the plane from rotating too far (clamping it to minimum of the goal direction and the stepsize).