Programming Assignment 2: Attack on Tetris

COMP 211-002 | Spring 2023 (Bakita)

Written by Ryan Good

These steps were last updated .

Due Date

Thursday, February 9th, 11:59 PM.

Connecting

You’ll have to connect to the school server for this assignment. All the connection information is the same as in Assignment 1.

Preface

This assignment is intended to get you working at the command-line level and expand the scope of your C programming abilities. As new concepts pop-up throughout the README, you’ll be provided with relevant resources and gently nudged in the direction of a ‘correct’ implementation. Follow along, stay focused, and most importantly, try things out for yourself!

Introduction

As you can see, in this assignment we’ll be working with a terminal-based implementation of Tetris that was coded in C. Although its code isn’t particularly short, it is quite simple! As such, when you use the “quick save” feature included in the game, all the program does is take the whole struct used to represent the game-state and dumps it into a file in binary format. That may sound a tad complex right now, but by the end of this assignment you’ll be working proficiently with this binary file to manipulate the game!

Prep

(First ensure that you’re connected to the school server over SSH.)

Using Git, clone the repository located here with the command git clone https://github.com/goodryananthony/COMP211-SP23-A2.git. This will create a folder named COMP211-SP23-A2 that you can switch into with the cd command. In the TASK1 subfolder, you’ll find the ./tetris binary.

* I accidentally forgot to add the tetris.h file to the TASK2 subfolder. It’s updated now. If you cloned it prior to that change, you can copy it over yourself or run git pull to pull the new changes down.

Run this (./tetris) to play the game! While you’re testing it out, be sure to get a feel for how quick load, quick save, and the other features of the game work. Once you’ve done that, we’ll move on to inspecting the guts of the game a bit more.

Next, we’ll move on to inspecting the header file. It’s only a little over 100 lines long, so be sure to read it in its entirety. What we’re most concerned with is the TetrisGameState struct (and its contents). When we discuss the idea of quick-saving, what’s actually being saved to the tetris_quicksave.bin file are the contents of the TetrisGameState struct that the main game logic keeps track of. Unfortunately, sharing the tetris.c file would make the second half of this assignment a bit too easy, so we’ll leave it to your imagination as to how exactly that works.

Task 1

Now that you’ve got a basic understanding of the TetrisGameState structure, we want to begin our attempts at modifying it. You should see a tetris_quicksave.bin file located in the directory. If there isn’t one, open the game and press s to quick save, it will create the file if one doesn’t already exist.

We’ve now got a binary file representing the game state, but how do we work with it? This is where command-line tools such as xxd come in. If you type in the following command, you should see a similar output depending on the state of your game.

username@comp211-2sp23:~$ xxd -p -c10 tetris_quicksave.bin > hexdump.txt
username@comp211-2sp23:~$ vim hexdump.txt

20202020202020202020
20202020202020202020
20202020202020202020
20202020202020202020
20202020204e4e202020
2020202020204e4e2020
20202020202040202020
20202020202040202020
20202020204040202020
20202020204848202020
20202020204848202020
20202020202040202020
20202020202040202020
20202020204040202020
20202020204f4f4f2020
20202020204f20202020
20202020204848202020
20202020204848202020
20202020204020202020
20202020204040402020
0300000005000000ffff
ffff0f00000050c30000
000000000a

The file hexdump.txt now contains the whole TetrisGameState struct in hexadecimal. The first 20 lines of the file are the game board. For example: the first 2 hexadecimal characters are “20”, which equate to 32 in decimal, which is the “Space” character. You’ll have to rely on an ascii chart for this part!

(You can also just do “xxd -c10 tetris_quicksave.bin” to have it spit out all of its formatting, including an ascii representation. It’s much easier to modify/work with the plain hex though so that’s why we added -p to the command)

If you just ran the xxd command without > hexdump.txt at the end, it would have just output the contents into the terminal. What we did by adding that bit at the end is called output redirection. There is also input redirection, and it’ll be necessary to returning the hexadecimal format back into binary.

Since we’re already handgrading assignments, and since you’re not turning in any C code for this portion, your job is to create a gameboard that contains your first and last initials! I suggest starting with a quicksave of an empty board for ease.

Here’s an example for reference.

After you’ve done that, follow these instructions to turn it back into a binary file:

* I would definitely suggest making a copy of both your hexadecimal file and original quicksave binary file before doing this

  1. Check out the manual page for xxd (read up on -r and look out for the word plain)
  2. When you go to run the command, use input redirection to “input” your modded hex file and use output direction to “output” the binary into a file called tetris_quicksave.bin

You should now be able to run tetris and quickload your modded gameboard!

Task 2

Be sure to have a tetris_quicksave.bin file present before starting this section

For your second task, the training wheels will be coming off somewhat. Now that it’s been proven to you the data we’re working with is really just binary (or hexadecimal) under the hood, lets make use of C’s ability to interpret such data. Modifying binary by hand is a nice skill to have, but one small instance of human error can corrupt a whole save file and certainly takes longer than the alternative we’ll explore now.

Be sure to name the file your code is in modify.c

In simple terms, you will use the tools on pages 160 and 247 (B1.5) of the C Programming book to load the contents of the tetris_quicksave.bin file in and then read from that “data stream” as the books calls it into a locally initialized TetrisGameState struct. You can name the struct whatever you wish. Read the referenced sections carefully and note we’re working with binary files.

Think of it like one of those cheap plastic buckets you might find at the beach, you fill it with wet sand and when you dump it out you’ve got a perfect little sand castle. With the functions included in those two sections we can shove binary data into a TetrisGameState structure and be left with a fully functional instance of a TetrisGameState. This is exactly how the main file for the tetris game actually saves its game state.

Once you’ve got such a local variable working, you can modify it just like any other struct! Want to make your score 50,000? Just modify that property of the struct.

After any modifications have been done, you now have to do the opposite of what we did earlier. Now we’ll be going from an instance of a TetrisGameState struct and outputting its contents into the tetris_quicksave.bin file. If you’ve done it correctly, you should be able to run your program then run ./tetris and see the changes reflected in your savefile.

Once you’ve gotten such a simple implementation working, it will be fairly trivial to turn it into the program the autograder will expect.

Specifications

  1. The program’s name will be modify.c
  2. <stdio.h>, <stdlib.h>, <string.h>, <limits.h>, and “tetris.h” are allowed as included files
  3. Your program will use argc and argv to read in command-line arguments (see section 5.10 of the C Programming book)
  4. Your program will accept and correctly perform the following commands:
    1. ./modify score 100 (or some similarly arbitrary number)
    2. ./modify lines 100 (or some similarly arbitrary number)
    3. ./modify next_piece 2 (or some other number between 0 and 18 since there are 19 listed pieces in the tetris_pieces array in tetris.h)
    *When accepting number inputs, be sure to use the atoi() atol() or strtoul() family of functions to convert strings to numbers as necessary. See man atol or man strtoul for details.

Edit Notice, Feb 8 10:45 AM: The above was updated to remove mention of atoi(). This function is insufficent to allow a user to specify numbers within the full range of a some fields of TetrisGameState. For example, atoi() returns an int (range up to ~2B) but score in TetrisGameState is an unsigned int (range up to ~4B). –Prof. Bakita

Edit Notice, Feb 8 11:15 AM: The above was updated to allow use of other headers, and to recommend use of the limits.h header. When validating that an input number is within the range supported by our TetrisGameState struct, we may want some of the values here. See the section Range of an Integer Type in the manual provided by info libc for details. (Type / once the info page for libc is open to get a search prompt at the bottom.) –Prof. Bakita

When the game save is modified to contain large values for lines, the game speeds up significantly. This is normal behavior.

Collaboration Policy: As stated in the syllabus.

To Submit…

  1. Make sure you can log in to Gradescope through the link on Sakai.
  2. Run submit_a2 <path to your tetris_quicksave.bin file> <path to your modify.c file> on the course server.
  3. Log in (no characters will appear while you enter your password).
  4. After you see File "<your file name>" uploaded. for each file, your submission is uploaded to Gradescope.
  5. [Optional] Log in to Gradescope and verify that your submission for assignment 2 uploaded correctly.

You have unlimited resubmissions before the deadline, and may repeat the above process as many times as is necessary to update your submission.

Autograder is live as of 10 AM on Feb 8.

Note that the autograder very carefully checks that when your modify program is passed invalid input, it doesn’t corrupt the save file. Your score can only be so high! An unsigned can only store numbers up to a certain value, and there are only a handful of allowable piece types.

Honor Code Pledge

The first line in your submission file (modify.c) must be the following:

// I, John Doe (730777777), pledge that I have neither given nor received unauthorized aid on this assignment.

Where John Doe is replaced with your name, and 730777777 is replaced with your PID. This statement constitutes your legally binding signature.

If you collaborated with any students on your assignment submission, you must list them as collaborators on the second line of your submission, as follows:

// Collaborators: onyenA, onyenB

Where onyenA and onyenB would be replaced with the Onyens of your first two collaborators. This comma-separated list may name any number of collaborators. Remember, per the syllabus, any form of code sharing is strictly forbidden. Your “collaborators” listed here are simply those you non-trivially discussed high-level concepts, approaches, and pseudocode ideas with (as defined in the syllabus).