Programming Assignment 5: The Dark Side

COMP 211-002 | Spring 2023 (Bakita)

Author: Prof. Bakita

These steps were last updated .

Due Date

The only and final due date for this assignment is immediately prior to the final exam, at Tuesday, May 2nd, 12 PM.

We have been able to optimize our grading schedule, and can give you all another 25 hours, up until Wednesday, May 3rd, 1 PM.

Introduction

As a frustrated Tetris player, you’ve decided that it’s too much work to manually level-up your Tetris scores to win the tournament. You’ve managed to obtain all the tournament administration tools recently written in COMP 211, and have decided that building an all-in-one tool to forge, check, and rank quicksaves will be more fun than playing any more Tetris.

Teams

To help you overcome the difficulty of crafting such a complex hacking tool, you’re allowed to complete this assignment jointly with one other class member. The collaboration policy still applies in regards to everyone except for your teammate. We always check every assignment submission for similarity, and reserve the option to apply the unoriginality penalty in inconclusive cases (as noted in the syllabus).

Looking for a teammate? Fill out this form and our LA Andrew Byerle will get back to you with a pairing as soon as one is available.

Key Challenges

Non-Challenges

Requirements

In this assignment, you have flexibility to choose what to implement. The available points are divided into two categories:

  1. Core functionality (70 pts)
  2. Suggested components (30+ pts)

By implementing many suggested components, it is possible for your submission to exceed 100 points (100%), potentially supplanting poor scores on tests or other assignments. The number of points available for a feature reflects its expected difficulty, so choose wisely.

Your included documentation will let us know what features you’ve implemented.

Some potential feature combos:

But you’re not constrained by the selection of features in this description! Any features you add (color? text underlining?), can net you points, whether or not it was our idea. Creative solutions are the most fun to grade, so we’ll likely be quite charitable to features you think up yourself.

Provided Binaries

The binary executables modify, rank, check, and recover you’ve obtained are similar to those from earlier assignments, but have been extended by the tournament administrators with new, backwards-compatible features:

You can find these binaries in /playpen/a5/ on the course server.

The leaderboard will begin accepting uploads by 5:00 PM on Friday, April 21st. Up until then, using “uplink” with rank will yield a “Function not implemented” error.

Core Functionality

Naming, Compiling, and Linking

Your program is to be called tetrashell. 1 You may name your source file(s) as you wish, but you must provide a Makefile which compiles the binary tetrashell (any any supporting files) from your submission when make in invoked.

You may use C Standard Library or ncurses library functions. We are happy to entertain reasonable requests for the use of other common libraries.

Basic User Interface

Tetrashell works via a CLI (Command Line Interface) which provides a few key features:

  1. Accepts modify, rank, check or recover commands from the user, launching the associated program to perform the command.
  2. Automatically tracks the current quicksave that the user is attempting to forge, and applies all the modification commands (modify, rank, and check) to this quicksave.

An example sequence of actions during a single run of Tetrashell:

  1. Start Tetrashell and enter the target quicksave path to use with the following commands (we refer to this quicksave as “current”)
  2. modify score to be a higher number
  3. check that the modified save still appears legitimate
  4. modify lines to be slightly higher
  5. check that the modified save still appears legitimate
  6. rank to upload and rank the modified save against the system-wide database
  7. exit

Startup & Initialization

Upon launch of Tetrashell, you should print a welcome message and obtain the path to the target quicksave from the user.

A good implementation will check that the user-specified quicksave exists.

Example:

student@comp211-2sp23:a5$ ./tetrashell
Welcome to...
                              (
  *   )          )            )\ )    )       (   (
` )  /(   (   ( /( (       ) (()/( ( /(    (  )\  )\
 ( )(_)) ))\  )\()))(   ( /(  /(_)))\())  ))\((_)((_)
(_(_()) /((_)(_))/(()\  )(_))(_)) ((_)\  /((_)_   _
|_   _|(_))  | |_  ((_)((_)_ / __|| |(_)(_)) | | | |
  | |  / -_) |  _|| '_|/ _` |\__ \| ' \ / -_)| | | |
  |_|  \___|  \__||_|  \__,_||___/|_||_|\___||_| |_|

the ultimate Tetris quicksave hacking tool!
Enter the path to the quicksave you'd like to begin hacking: ./haxxed_quicksave.bin
'./haxxed_quicksave.bin' set as the current quicksave.
Enter your command below to get started:
tetrashell> 

Want to have some fun with your welcome message? The example intro text is ASCII “Fire Font-k” from Patrick Gillespie’s ASCII text generator.

Core Command modify (15 pts)

The modify command should take two arguments, the field to modify and the value to change it to, just as in Assignment 2. This should be implemented by calling the modify program.

Your shell should automatically pass the path to the current quicksave as the last argument to the modify program.

This should require no special handling of stdin or stdout.

Example:

tetrashell> modify score 1000
Quicksave './haxxed_quicksave.bin' modified.

Core Command rank (25 pts)

The rank command should take two arguments, the metric to score on and the number of top games to display, just as in Assignment 3. It should use the rank program to upload your current quicksave to the central ranking database, and print the top players from the leaderboard.

Your shell will need to write the path of the current quicksave to stdin for rank, and automatically pass “uplink” as the third argument to rank.

This should require no special handling of stdout.

Example:

tetrashell> rank score 5
/priv/cba/tetris_quicklead.bin
/priv/student/haxxed_quicksave.bin
/priv/fed/tetris_master.bin
/priv/ihg/my_game.bin
/priv/kj/better.bin

Core Command check (15 pts)

The check command should take no arguments, and indicate if the current quicksave is legitimate or not by using the check program.

Your shell should automatically pass the path to the current quicksave as the only argument to the check program.

This should require no special handling of stdin or stdout.

Example:

tetrashell> check
The quicksave ./haxxed_quicksave.bin appears legitimate.

Core Command recover (10 pts)

The recover command should take one argument, the path to a disk image file to recover quicksaves from, just as in Assignment 4. It should then use the recover program to recover and print the list of recovered quicksave paths.

This should require no special handling of arguments, stdin, or stdout.

Example:

tetrashell> recover /playpen/a4/a4_disk.img
recovered/out_1.bin
recovered/out_10.bin
recovered/out_11.bin
recovered/out_2.bin
recovered/out_3.bin
recovered/out_4.bin
recovered/out_5.bin
recovered/out_6.bin
recovered/out_7.bin
recovered/out_8.bin
recovered/out_9.bin

2

Core Command exit (5 pts)

The exit command takes no arguments, and causes tetrashell to exit.

Example:

tetrashell> exit
student@comp211-2sp23:a5$ 

Suggested Components

You are free to add whatever features you would like, and we encourage you to be creative! You’re welcome to reach out to gauge how excited we would be about a potential feature. In case you’re not sure where to start, we provide an assortment of suggested features you could implement, as well as estimated score values for a good implementation.

Short Commands (3+2 pts)

Allow commands to be abbreviated to the minimum number of unique distinguishable characters (3 pts). For example, typing c, ch, che, or chec would all run the check command.

The ideal implementation (+2 pts) will continue to reject invalid commands such as cheque.

Improved Prompt (7 pts)

Provide a bit more information, or some style (using ANSI Escape Codes) in your shell’s prompt. Some ideas for information to provide:

One way your prompt could look:

student@TShell[haxx...][Rank #2][1000/172]> 

Where the current quicksave would have a score of 1000, and 172 lines.

Tab-Completion of Commands (10 pts)

Pressing Tab will enable autocompletion of commands if a unique prefix has already been typed. If the prefix is not unique, all possible matching commands should be printed, and the prompt should be restored with the partially-typed text.

Advanced-modify (5+5 pts)

Extend your implementation of modify.c from Assignment 2 to allow for:

  1. Modifying the current piece
  2. Modifying the location of the current piece
  3. Modifying the game board

Example (visualize command also shown):

tetrashell> visualize
Visualizing savefile ./haxxed_quicksave.bin
+---- Gameboard -----+   +--- Next ----+
|                    |   |             |
|                    |   |             |
|                    |   |             |
|                    |   |  HHHH       |
|                    |   |  HHHH       |
|                    |   |             |
|                    |   +-------------+
|                    |
|                    |
|                    |
|                    |
|  NN                |
|NNNN                |
|NNNN                |
|NNNN            ZZ  |
|NNNNZZ  OOOO    ZZZZ|
|NNNNZZZZ==OO      ZZ|
|NN##  ZZ==OO  ######|
|####ZZ  ==ZZ  ZZ##==|
|OOOO  NNNN  ##==HHHH|
+--------------------+
tetrashell> modify board 5 5 #
tetrashell> visualize
Visualizing savefile ./haxxed_quicksave.bin
+---- Gameboard -----+   +--- Next ----+
|                    |   |             |
|                    |   |             |
|                    |   |             |
|                    |   |  HHHH       |
|                    |   |  HHHH       |
|          ##        |   |             |
|                    |   +-------------+
|                    |
|                    |
|                    |
|                    |
|  NN                |
|NNNN                |
|NNNN                |
|NNNN            ZZ  |
|NNNNZZ  OOOO    ZZZZ|
|NNNNZZZZ==OO      ZZ|
|NN##  ZZ==OO  ######|
|####ZZ  ==ZZ  ZZ##==|
|OOOO  NNNN  ##==HHHH|
+--------------------+

Five points for a basic implementation. Only allow moving the current piece to an open location for five more points.

Include your modified version of modify.c in your submission and make sure that your included makefile also builds modify when run with make.

For a diabolical challenge, include a graphical interactive game board editor—this could net many bonus points. 3

Quick-rank (5 pts)

Allow the rank command to be called with one or zero arguments:

Example of the first case, with a 5-game default:

tetrashell> rank score
/priv/cba/tetris_quicklead.bin
/priv/student/haxxed_quicksave.bin
/priv/fed/tetris_master.bin
/priv/ihg/my_game.bin
/priv/kj/better.bin

Pretty-rank (15 pts)

Requires Quick-rank as a prerequisite.

Parse the output of the rank command and, rather than just printing filenames, number each line of the output to reflect leaderboard position.

Furthermore, when the number of lines to print is unspecified, request a large number of games from rank, and display only a few games above/below your current quicksave. A good implementation would also apply some sort of formatting to highlight your quicksave (maybe ANSI escape codes for bold text?).

Example of one way it could look:

tetrashell> rank
11 /priv/abc/tetris_quicklead.bin
12 /priv/def/tetris_master.bin
13 /priv/ghi/my_game.bin
14 /priv/jk/better.bin
15 /priv/bob/beat_josh.bin
>>> 16 /priv/student/haxxed_quicksave.bin <<<
17 /priv/alpha/try_35.bin
18 /priv/in/work.bin
19 /priv/oice/tetris_quicksave.bin
20 /priv/beta/trying.bin
21 /priv/hal9000/ai_attempt.bin

Pretty-recover (15-30 pts)

Provide the user a nice interface for the user to view the recover command output and optionally switch the current quicksave to one of the recovered ones.

Example of a basic implementation (15 pts):

tetrashell> recover /playpen/a4/a4_disk.img
Recovered quicksaves:
1 recovered/out_1.bin
2 recovered/out_10.bin
3 recovered/out_11.bin
4 recovered/out_2.bin
5 recovered/out_3.bin
6 recovered/out_4.bin
7 recovered/out_5.bin
8 recovered/out_6.bin
9 recovered/out_7.bin
10 recovered/out_8.bin
11 recovered/out_9.bin
Switch? Enter num, or 0 to not switch: 1
Switched to recovered save #1.

4

Example of a fantastic solution (30 pts):

tetrashell> recover /playpen/a4/a4_disk.img
Recovered quicksaves:
-- --------------------- ------- -------
#  File path             Score   Lines
-- --------------------- ------- -------
1  recovered/out_1.bin   100     23
2  recovered/out_10.bin  34      2
3  recovered/out_11.bin  2       0
4  recovered/out_2.bin   10      0
5  recovered/out_3.bin   50      1
6  recovered/out_4.bin   10      0
7  recovered/out_5.bin   30      3
8  recovered/out_6.bin   10      0
9  recovered/out_7.bin   14      0
10 recovered/out_8.bin   10      1
11 recovered/out_9.bin   10      0
Would you like to switch to one of these (y/n): y
Which quicksave (enter a #): 1
Done! Current quicksave is now "recovered/out_1.bin"

5

switch: Switch Current Quicksave (5 pts)

Add the switch command to allow the user to change the current quicksave being hacked.

Example:

tetrashell> switch ./haxxed_quicksave.old.bin
Switch current quicksave from `./haxxed_quicksave.bin` to `./haxxed_quicksave.old.bin`.

help: Built-in Help (7 pts)

Add the help command to receive help for a particular command.

Example:

tetrashell> help check
This command calls the `check` program with the current quicksave to verify if it will pass legitimacy checks.

info: Basic Save Info (5 pts)

Print basic information about the current quicksave, such as its path, score, and lines.

Example:

tetrashell> info
Current savefile: ./haxxed_quicksave.bin
Score: 15000
Lines: 12000

visualize: Save Game Visualizer (15 pts)

Add the visualize command to show the current state of the Tetris game board as it would be displayed in-game. Good implementations will include the current and next piece.

Example:

tetrashell> visualize
Visualizing savefile ./haxxed_quicksave.bin
+---- Gameboard -----+   +--- Next ----+
|                    |   |             |
|                    |   |             |
|                    |   |             |
|                    |   |  HHHH       |
|                    |   |  HHHH       |
|                    |   |             |
|                    |   +-------------+
|                    |
|                    |
|                    |
|                    |
|  NN                |
|NNNN                |
|NNNN                |
|NNNN            ZZ  |
|NNNNZZ  OOOO    ZZZZ|
|NNNNZZZZ==OO      ZZ|
|NN##  ZZ==OO  ######|
|####ZZ  ==ZZ  ZZ##==|
|OOOO  NNNN  ##==HHHH|
+--------------------+

undo: Undo Last Modify (15 pts)

Add the undo command which reverts the last modify command executed.

The easiest way to implement this would probably to save a copy of the quicksave in memory or on disk before executing each modify command.

Bonus points if you allow for undo to revert more than one change, particularly if you can do it without cluttering up the filesystem.

If you decide to write backup files to disk, please delete them as part of your shell’s exit command.

train: Hex Conversion Trainer (20 pts)

Not exactly necessary to modify Tetris quicksaves, but what is a hacker without a good understanding of hex?

Implement the command train in tetrashell which generates example hex strings and asks the user to convert them into binary or into integers (or the reverse). Try to provide helpful guidance if the user responds incorrectly, for example, by highlighting which parts of the response were incorrect. A truly excellent implementation of this would support adaptive difficulty, where easier-to-convert numbers are provided after a mistake, or gradually more difficult ones after consistently correct answers.

This provides an opportunity for those of you eager to finally implement the extra credit opportunity mentioned in-class months ago.

Getting Started

Step 1: Preparatory Readings

Learn about how to launch processes from within your program using fork and execve:

Step 2: Basic Prompt & Exit

Create the basic mainloop of your program:

  1. Print prompt
  2. Read input from same line
  3. Perform action on input

Start by simply reading in the line entered by the user, echoing it back, and printing a new prompt.

Expand on this to check if the user input is exit, and exit from your program in that case.

Example:

student@comp211-2sp23:a5$ ./tetrashell
tetrashell> hello my shell!
hello my shell!
tetrashell> exit
student@comp211-2sp23:a5$

Step 3: Stateless Commands

Add support for the recover command. This likely requires:

  1. Check if the first word entered by the user is “recover”
  2. If it is, treat all the following text after the space as the argument
  3. Use fork() and execve() to run recover with that argument
  4. Wait for recover to finish
  5. Print the tetrashell> prompt

Step 4: Acquire State

Add a welcome message which prompts the user for the quicksave they’d like to modify. Read and store that filename for later use.

If you want to have some fun with the styling, here’s another link to Patrick Gillespie’s ASCII art text generator mentioned earlier.

Step 5: Stateful Commands

Implement check and modify, passing the current quicksave (the one the user specified at the start) as the last argument.

Step 6: Stateful Commands with Special stdin Handling

Implement rank, passing “uplink” as the last parameter. As the rank program takes the path to the quicksave to rank over stdin, you’ll need to launch rank with a custom-created pipe configured as stdin for rank. You can then communicate the name of the current save to rank by writing it to the pipe.

Aside: We’ll try to add a bit more detail to this step later, time permitting.

Step 7: Add More Features!

What you choose to implement for the remaining 30 points of the assignment is up to you! Peruse the Suggested Components section for ideas.

Submission

In order to flaunt your fantastic tetris-quicksave-forging shell to your friends, you’ll need to both provide your files and some documentation of what features your shell includes. Both will be submitted via Gradescope.

Files

Upload all the files from your submission to Gradescope using any available method. The submit_a5 command is now available on the course server. This will verify your code compiles and, if so, upload it into Gradescope.

Only one member of the team needs to submit. For the team member that submits: after submitting, please go onto Gradescope, open your submission, click “Group Members”, and add your teammate.

Make sure to include your makefile, and make sure that the default (first) target builds your whole project, including tetrashell.

Documentation

What good is a tool if no one know how to use it? Along with your code, you are expected to provide a brief manual for how to use your tool.

Include a plain-text- or markdown-formatted file named README, README.md, or tetrashell.1.md that you submit along with your code. Here’s an overview of the sorts of things common in modern README files. Alternatively, you can format your write-up as a manpage (your hacker friends would love this!) drafted in tetrashell.1.md. View the rendered manpage in man via the command pandoc tetrashell.1.md -s -t man | man -l -. Here’s an okay guide on how to write a manpage using markdown.

If you find these options too inflexible, you are allowed to create a plain-text README that simply includes a URL to a public Google Docs or Word Online file.

So that we know what to test and give you points for, make sure that your write-up includes the following information:

In your write-up, if you implemented a feature exactly as suggested above, you can say so, and omit repeating information from this page.

You are welcome to include whatever other sort of information you would like! The quality of your write-up for each feature is considered along with the quality of your implementation when grading. What is the point of excellent code if there are no instructions on how to use it? Excellent write-ups can receive extra credit.

Grading

We will test all your submissions by hand on the course server, verifying that each feature claimed in the provided write up works. How many points you get for each feature is a function of how well the feature works, with a good, fully-working implementation getting full points. Our emphasis will be on functionality, but we reserve the right to take off points for particularly slow or unreadable code.

Footnotes


  1. The name “Tetrashell” includes tetra (meaning four) for the four primary subcommands and for its shared prefix with “tetris”; shell as your program implements functionality similar to common shells such as bash.

  2. List of recovered games trimmed for brevity.

  3. This will likely require some very advanced use of escape codes, or ncurses. Doing this nicely would really impress me, and likely net tens of extra points.

  4. List of recovered games trimmed for brevity.

  5. List of recovered games trimmed for brevity.