<style>
details {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
summary {
//font-weight: bolder;
}
summary:hover {
text-decoration: underline;
}
blockquote {
font-size: 16px;
}
.todo {
color: #ff00ff;
border: 2px dashed #ff00ff;
padding: 0em 1em;
border-radius: 5px;
margin-top: 1em;
margin-bottom: 1em;
//display: none; // UNCOMMENT TO HIDE TODOs
}
</style>
**[« Back to the main CS 300 website](https://csci0300.github.io/)**
# Project 1: Snake :snake:
**Part 1A checkin due by Friday, January 31**<br />
**Parts 1 and 2 due Friday, February 7 at 8PM EDT**
### All parts (1-3) due Friday, February 14th at 8:00pm (EST)
---
<!-- [TOC] -->
# Introduction
Welcome to the first project of CS 300! We're excited that you're here! :smiley:
In this project, you will develop a version of [Snake](https://en.wikipedia.org/wiki/Snake_(video_game_genre)), a famous early computer game widely cloned and reproduced. (Fun fact: [Snake in QBASIC](https://en.wikipedia.org/wiki/Nibbles_(video_game)) was the first computer game Malte played as a child, and Snake was also on his first mobile phone, a [Nokia 3310](https://en.wikipedia.org/wiki/Nokia_3310).)
Since we're writing the Snake game in the C programming language, this assignment requires you to work directly with memory and data representations. In the first and second portions of the project, you will set up a basic playable version of the game with customizable levels. In the third part, you will bring your implementation closer to the original snake game by supporting a growing snake (using your own linked list data structure), and explore different character representations that allow for player names from different cultures by supporting their alphabets.
## Learning Objectives
In this assignment, you will:
* Practice writing code in the C programming language.
* Understand how data is laid out in memory.
* Learn how to work with pointers.
* Become familiar with C strings, bitwise operations, and dynamic memory allocation (`malloc` and `free`).
* Understand the difference between ASCII and Unicode character representations.
# Assignment
:::danger
**⚠️ Warning:** It is important to get started early! This project is not trivial. It's very doable (even the hard parts), but you want to start early to have enough time to debug your code.
:::
## Assignment Installation
If you have completed [Lab 0](https://csci0300.github.io/assign/labs/lab0.html), you will already have created a private GitHub repository for your project work under the `csci0300` organization.
:::warning
:warning: **_If you have not completed [Lab 0](https://csci0300.github.io/assign/labs/lab0.html), we suggest that you do it now!_** :warning:
:::
To get started on this project, you will need to pull some additional stencil code into your projects repo, as follows:
1. Open a terminal in your container environment and `cd` into the directory you setup as your project repo from Lab 0 (eg. `cd projects`).
2. Run the following command to pull the latest code from the `handout` remote:
```
cs300-user@9899143429a2:~/projects$ git pull handout main
```
If this reports an error, run:
```
git remote add handout git@github.com:csci0300/cs300-s25-projects.git
```
followed by:
```
git pull
git pull handout main
```
This will merge our Project 1 stencil code with your repository.
Once you have a local working copy of the repository that is up to date with our stencils, you are good to proceed. You'll be doing all your work for this project inside the `snake` directory in the working copy of your project's repository.
# Stencil Overview
Below is an outline of all the files we provide you with an a brief description of what they do:
In `snake/src/`:
| File | Purpose |
| -------- | -------- |
| `common.h` | Contains definitions for the `input_key` enum and `snake` struct, declarations for global variables defined in `common.c`, and function headers for `common.c` |
| `game_over.h` | Contains function headers for `game_over.c` |
| `game_setup.h` | Contains the definition for the `board_init_status` enum and function headers for `game_setup.c` |
| `game.h` | Contains function headers for `game.c` |
| `linked_list.h` | Contains the definition for the `node` struct and function headers for `linked_list.h` |
| `mbstrings.h` | Contains function headers for `mbstrings.c` |
| `render.h` | Contains function headers for `render.c` |
| `snake.c` | Contains support functions and skeleton version of `main` |
In `snake/test/`:
| File | Purpose |
| -------- | -------- |
| `traces.json` | Contains all of the traces (i.e. test cases) that a working implementation will pass |
| `autograder.c` & `autograder.py` | Autograder program/scripting |
**The files that you will have to modify in the project are:**
- `game.c`
- `game_setup.c`
- `snake.c`
- `linked_list.c`
- `mbstrings.c`
- `common.h`
- `common.c` <!-- since they need to add globals now -->
- `Makefile`
- `game_over.c`
:::info
**Important Note:** The following **[Testing](#Testing)** and **[Debugging](#Debugging)** sections explain how to test and debug your code. Make sure you read through these **before** attempting to implement any part of the project, so that you can run tests and productively debug your code. You should also take a look at our [**C primer on debugging**](https://csci0300.github.io/assets/c-primer3.html). (Note: these sections have a lot of content and it is okay if you don't understand it all right away! You will become more familiar with these concepts as time goes on.)
:::
# Testing
:::danger
**⚠️ Make sure that you run all tests inside the course Docker container!**
Even though this project may compile on your host OS, you may spuriously fail tests if your C standard library generates a different sequence of random numbers than the library version in the course container. For example, the C standard library that comes with macOS uses a different random number generator than the standard library in Linux, and consequently the food will appear in the wrong places (and you'll fail the tests) on macOS.
:::
We've provided an automated test suite for you to check your implementation against. As you progress through the project, you will pass more and more of the tests until you pass them all! **Once you pass all your tests locally, make sure that they also pass on the grading server and that the game is playable!**
To run all the tests, you can use
```bash
$ make check
```
To run a specific test you can use
```bash
$ make check-<n>
```
For instance `make check-5` to run test 5. To run a range of tests, use
```bash
$ make check-<n>-<m>
```
For instance, `make check-3-10`.
A single test contains a compressed board string, a random seed that determines the order of food generation, a flag that says whether or not the snake should grow (the snake doesn't grow in part 1 or 2, but does in part 2), and a series of keyboard inputs. The autograder will use those inputs to pretend to play your snake game. The game runs for as many steps as there are key inputs. When the inputs run out, the autograder takes a snapshot of your game data and compares it against an expected output, notifying you of any discrepancies.
By default, if your board doesn't match the expected output the autograder will print your board and the expected board so you can see the errors. If you also want to see all the intermediate boards as the autograder plays snake, you can run in verbose mode:
```bash
$ make -B check V=1 # `-B` forces a rebuild and `V=1` sets verbose mode
```
To switch back to normal output, you can use
```bash
$ make -B check V=0
```
:::info
**Note:** The tests do not run your `main` function (from `snake.c`), they instead call your `update` function (from `game.c`) directly.
:::
::::info
<details>
<summary><b>More Info on Boards and Rendering</b></summary>
<br />
You probably noticed that the board strings (only in part 2 and 3, e.g. "B7x10|..."), the visual rendering (from running `main`), and the autograder output use slightly different representations for different cells on the board.
Here's a key to understand what each character represents in different board formats:
| | Board String | Visual Rendering | Autograder Output |
| :--------: | :--------:| :--------: | :--------: |
| Wall | "W" | "<span style="color:blue">█</span>" | "X" |
| Empty | "E" | " " | "." |
| Snake | "S" | "<span style="color:gold">S</span>" | "S" |
| Food | _N/A_ | "<span style="color:red">O</span>" | "O" |
| Grass Only | "G" | "<span style="color:green">·</span>" | "G" |
| Grass and Snake | _N/A_ | "<span style="color:green">S</span>" | "s" |
| Grass and Food | _N/A_ | "<span style="color:green">0</span>" | "o" |
</details>
::::
# Debugging
*==**NOTE**==: Compile with the `ASAN=0` flag when using GDB (e.g `make -B all ASAN=0`) to disable the address sanitizer! Sanitizers should not be used on binaries that are attached using GDB and may result in some unexpected errors! Make sure to remove `ASAN=0` from your make command when you are done debugging.*
*==**MORE DEBUGGING INFO**==: For more detailed info on debugging and **the address sanitizer** see our [C primer on debugging](https://csci0300.github.io/assets/c-primer3.html).*
As you'll discover (or have discovered!), `gdb` is an invaluable tool for debugging your C programs. However, our snake program represents a challenging debugging problem: how can we run our program in `gdb` when we also need to see the graphical output?
It turns out `gdb` has an answer for this -- we can run the program in one terminal and attach to it with `gdb` from another terminal. We've provided small scripts to make this easy for you. You can run them as follows:
In one terminal, run the listen script. It takes any arguments you would normally pass to the snake program. Here, we pass `0` to indicate that the snake shouldn't grow.
```bash
./gdb-listen.sh 0
```
In another terminal, run the attach script
```bash
./gdb-attach.sh
```
The first terminal will have your snake game, and the second will have `gdb`. However, you won't see much in the first terminal, as the game window hasn't been set up yet. Try typing `continue` in `gdb` to start the game.
With this setup, you can use `gdb` how you normally would. For instance, you can set breakpoints on `initialize_game`, `update` or `decompress_board_string`.
Another question you may have is how to debug test cases. These are simpler since they don't require graphical output. We've included an extra `make` rule to run tests under `gdb`. You can use it like so:
```bash
make check-gdb-<n> # for instance, make check-gdb-3
```
By default, this will pause program execution in `autograder.c:main()`, which you don't need to understand. All that's important to know is that `autograder.c` calls many of the same functions as `snake.c`, so if you set breakpoints on `initialize_game`, `update`, `decompress_board_string`, etc. you can get to your code to start debugging.
:::info
<details><summary><b>GDB looks weird?</b></summary>
If you run into issues where the terminal doesn't start new lines in the correct place, try a new terminal and do not change its default size. Your terminal may sometimes report the wrong dimensions to GDB.
</details>
:::
:::info
<details><summary><b>Help!</b> What are the exact steps for running <code>make check-gdb-<n></code>?</summary>
As mentioned above, if you want to debug a specific test, our infrastructure automates this for you. However, running <code>make check-gdb-<n></code> is a bit different from running GDB elsewhere.
Let's say you want to debug the `update` function in test 3. Then start with opening your `snake` directory in your terminal, and run our script to debug a test with:
```shell
$ make check-gdb-3
```
You should now be in a GDB window that is set up to automatically run test 3. First, to make it easier to see what's going on, let's show the source code alongside the GDB shell:
```shell
(gdb) layout src
```
Next, let's set a breakpoint at <code>update</code>:
```shell
(gdb) b update
```
Next, let's actually start running! <b>Note</b>: due to how our infrastructure works, when running <code>make check-gdb-<n></code>, you should use <code>c</code> (for <code>continue</code>), rather than <code>r</code> (for <code>run</code>) like you would in other GDB instances.
```shell
(gdb) c
```
Now, you should be set to begin debugging <code>update</code>! Try <code>n</code> to step through a line of code, and `print` to print out the value of a variable. See our C primer on debugging (linked at the top of this section) for more info on this!
</details>
:::
# Part 1: Snake Movement
The rules of the Snake game are as follows:
1. The snake moves around a rectangular playing field bordered by walls, and with some optional internal walls.
2. The player uses arrow keys to change the snake's direction.
3. Food appears at random locations on the playing field, and if the snake's head hits the food, the snake eats it. The player's score increases and new food appears elsewhere.
4. When the snake runs into a wall (or into its own body), it dies and the game is over.
5. Some cells may also contain grass, which the snake or food can hide in, causing it to appear a different color.
Your task is to take the stencil -- which starts out doing nothing at all -- and implement a game with these rules. Don't worry, we'll break this down for you!
We provide several helpful helper functions in the stencil, and will discuss these as we go along.
::::success
**Task:** Compile the stencil code and run it!
<details>
<summary><b>Hints on compiling</b></summary>
<br />
[Make](https://www.gnu.org/software/make/) is a tool that enables us to generate executable files from source code. The Makefile defines rules and behaviors for compilation. You'll learn more about Makefiles in [Lab 1](https://csci0300.github.io/assign/labs/lab1.html).
Navigate to your project directory and run `make` to create an executable file (called `snake`). You can also run tests with `make` — read through the rest of the handout!
</details>
<!-- it's a snek :) -->
You will see that it simply prints a message and exits:
```shell
$ make
$ ./snake
usage: snake <GROWS: 0|1> [BOARD STRING]
$ ./snake 0
____
Hello / . .\
CS 300 \ ---<
student! \ /
__________/ /
-=:___________/
```
::::
### Part 1A: Board and Game Setup
Like any computer program, the Snake game consists of _machine code_ (in the "code" or "text" segment of memory) and _data_. The data represents the state of the game, as well as its visual representation to the player. All of this is stored in the computer's memory in the form of bytes.
Your first task is to extend the stencil with the necessary code to represent and update the game state in memory!
We have already created the local variables you need to store information about the game board in the `main` function of `snake.c`. We have also already created the global variables you need to represent high-level game information in `common.<c|h>`. You will need to fill in the variables necessary to store information about the snake.
First, we'll give you an overview of the information stored on the board and game state. Then, you will come up with a representation for storing information about the snake.
#### Board Data
We know that the game board will have certain dimensions, may contain walls, and will have a snake. Our snake game will support user-created levels whose board dimensions may differ. We will also need to access and update the snake's position on the board based on arrow key inputs from the user.
So, what are some important details about the game board that we need to store? Additionally, what data do we need to represent the board itself? Think on these questions before reading through our explanation of the variables that we give you in the stencil!
::::info
<details>
<summary><b>Board Data</b></summary>
<br />
Here are the board variables that we give you in the stencil (located in `main` in `snake.c`):
```c
// Board data
size_t width;
size_t height;
int* cells; // array of integers containing data for each cell on the board
```
We have included both a `width` variable and a `height` variable to store the dimensions of the board. Notice that both of these variables have type `size_t`. This particular type actually represents values of type `unsigned long`, but we use the type definition `size_t` to indicate that the corresponding value represents a "size" of some other kind of data. In this case, we use `size_t` for our `width` and `height` to indicate that we are storing two size measurements of our board. To disambiguate between width and height, we will consider `width` to be the number of **columns** in a board and `height` to be the number of **rows**.
`cells` is what we use to represent the actual game board. For this representation, we use an array of integers, i.e., an `int[]`. You may observe that we don't use an array type, though! Recall from lectures the duality between arrays and pointers: even though `int*` is the type of a pointer to an integer, we can set aside a region of memory large enough to represent all individual cells in the board, put its address into the `int*` and treat the memory as an array. Then, we can set the memory address of the starting cell of the board as the pointer, and use pointer arithmetic to access, iterate through, or update the other cells in the board. This will become clearer as you work your way through the project!
</details>
::::
::::info
You will notice that these variables are *local variables* in the `main` function of `snake.c`. Read on for more details about what this means for our program.
<details>
<summary><b>Local Variables & Passing Pointers</b>
</summary>
<br />
In a naive approach, the function signature of `initialize_default_board` might look like
```c
enum board_init_status
initialize_default_board(int* cells, size_t width, size_t height)
```
The problem with this is that any changes to these parameters are not reflected in the original variables passed into the function. The solution to this is to pass pointers to those values instead.
```c
enum board_init_status
initialize_default_board(int** cells_p, size_t* width_p, size_t* height_p)
```
(The \_p suffix is a convention used in this project to indicate that these variables are pointers to `cells`, `width`, and `height`.)
Pointers hold the addresses of these variables, so if we pass in the address of `cells`, `width`, and `height` to this new version of the function, the addresses will be copied over, not the actual values themselves. Once we dereference these addresses, we recover their original values in memory.
```c
/* inside the initialize_default_board function */
*width_p = 20;
...
```
Then once the function terminates, it doesn't matter that the copies of `cells_p`, `width_p`, and `height_p` are deleted, because we've already made the changes to the values they point to.
<br />
</details>
::::
#### Game Data
Now, we need to consider what information we might need to store about the state of the game.
::::info
<details>
<summary><b>Game Data</b></summary>
<br />
Here are the game state variables we have given you in the stencil (declared in `common.h` and defined in `common.c`):
```c
int g_game_over; // 1 if game is over, 0 otherwise
int g_score; // game score: 1 point for every food eaten
```
</details>
::::
::::info
You will notice that these are *global variables* that are defined at the top level of `common.c`. Read on for more information about how to use global variables in this project.
<details>
<summary><b>Global Variables and <code>extern</code></b></summary>
<br />
Recall from lecture that variables declared at the top level of a file, outside of any curly braces (`{ }`), are *global* variables. You can access these variables from any function in that file and throughout the entire lifetime of the program. We want global variables like `g_game_over` and `g_score` to have these properties for *every* file in the project. How can we achieve this?
We can't just declare `g_game_over` and `g_score` in `common.h` and have other files include it. When we give a definition of a variable in C, e.g. `int g_score;`, *two* things happen: the name is made accessible *and* space is reserved for that variable. So *every* time that a file includes `common.h`, space will be reserved for it!
We don't want every file to have their own copy of `g_game_over` and `g_score`, we want them to be able to *see* the same `g_game_over` and `g_score`. C lets us do this with the `extern` keyword:
```c
/* common.h */
extern int g_game_over;
extern int g_score;
```
The `extern` keyword tells the compiler not to reserve space, and to trust that these variables exist in memory *somewhere*, while also allowing us to use them. We can actually reserve the space for these variables once in `common.c`:
```c
/* common.c */
int g_game_over;
int g_score;
```
Now every file which includes `common.h` can access the same `g_game_over` and `g_score` variables which live in `common.c`.
</details>
::::
#### Snake Data
At some point, we will need to update the board based on the current direction of the snake's movement. Consider how you will represent the snake's position and its current direction of movement, so you can then use your representation to update the overall game state.
:::danger
**Important Note:** In this part of the project, **you only need to represent the head of the snake (i.e., your snake is always of length 1)**; in Part 3, you will need to implement snake growth.
:::
::::success
**Task:** In `common.h` and `common.c`, declare the global variables you need for your representation of the snake.
::::
::::info
<details>
<summary><b> Hints! </b></summary>
<br />
- You'll need to keep track of the snake’s position on the board. How can you do so? (Remember that you need to be able to find the snake's cell in the cell array contained in the board.)
- How can you represent the snake's direction of movement? (**Hint**: take a look at the `input_key` enum defined in `common.h`.)
- Why do we tell you to modify both `common.c` and `common.h`? How can you use the `extern` keyword to define global variables in a multi-file project?
</details>
::::
#### Board and Game Initialization
Now that we have our representation of game data defined, we need to initialize all of our data before we can run our game. First, take a look at the function `initialize_default_board` (in `game_setup.c`). **You do not need to modify this function in this project.**
::::info
`enum board_init_status initialize_default_board(int** cells_p, size_t* width, size_t* height)`
<details>
<summary><b>Function Description</b></summary>
<br />
**Purpose:** Initializes the `cells` array to a default board with 10 rows and 20 columns. The snake is initialized at the 3rd row down and 3rd column from the left within the cells array. This default board has a ring of walls around the outermost edge, and a ring of grass inside of that. You do not need to modify this function.
**Arguments:**
* `cells_p` a pointer to the array of board cells
* `width_p` a pointer to the value describing the width of the board
* `height_p` a pointer to the value describing the height of the board
**Output:** `INIT_SUCCESS` (denoting that board initialization was successful)
</details>
::::
This helper function that we've given you initializes the given board variables to a default board, upon which you can implement basic snake motions. This board has no internal walls.
:::info
**What is `enum board_init_status`?**
You may notice that the return value of `initialize_default_board` is a value of type `enum board_init_status`. This type represents possible error codes for errors that could occur during board initialization.
You will need to use this enum when you support loading custom levels in part 2. For now however, we are only initializing the default board, so the return value will always be `INIT_SUCCESS`.
You can find the full description of the `board_init_status` enum in `game_setup.h`.
:::
Now take a look at the function `initialize_game` (also in `game_setup.c`):
:::info
`enum board_init_status initialize_game(int** cells_p, size_t* width_p,
size_t* height_p, snake_t* snake_p,
char* board_rep);`
:::
::::success
**Task:** Your task is to implement `initialize_game`, which should initialize all board data.
Here's a roadmap:
1. Call `initialize_default_board` to initialize `cells_p`, `width_p`, and `height_p`. (You should not touch the `snake_p` parameter until part 3!)
2. Initialize all global variables that you declared in `common.h`, including general game data and the snake data you defined. Depending on how you did this, you may need to use information you obtain by reading `initialize_default_board` to set these values. It's okay to hard-code this information for now; later, you will customize it for custom boards.
3. Set the return value of `initialize_game` to the value returned from `initialize_default_board`.
Notice that `initialize_game` is called in the `main` function in `snake.c` in the stencil. You do not need to add this call yourself.
::::
#### Cleaning Up Resources
Now that we have initialized our game and board data, let's run the program as it currently stands!
:::success
**Task:** Compile your code using `make clean all`, and run the program using the following command:
```shell
$ ./snake 0
```
When you run your program, no board will show up because we haven't written the code to display it on the screen yet. Instead, you should see our friendly Snake with the welcome message, but also something else...
![](https://cs300-beta.cs.brown.edu/uploads/86bd8670-3a54-4863-b5f7-c1878eee4360.png)
This message is an example of a *sanitizer error*--it may look scary at first, but we'll learn how to break it down over the next few lectures. Sanitizer errors are designed to help us make sure we're using our program memory correctly. If you want to read more about sanitizers now, expand the drop-downs below for additional details regarding the two types of sanitizers you'll be working with for this assignment.
<details><summary><b>Leak Sanitizer</b></summary>
<br />
The leak sanitizer detects *memory leaks* upon the completion of a program. A memory leak occurs when memory allocated during our program is not freed before exiting.
</details>
<details><summary><b>Address Sanitizer</b></summary>
<br />
The address sanitizer detects invalid memory accesses when a program runs. There are several types of invalid memory accesses (which you'll be exploring in detail in a future project), but they can all be broadly classified as either reading from or writing to memory that was not currently allocated to the program.
Note that by default, the address sanitizer comes packaged with the leak sanitizer, so compiling with `-fsanitize=address` (enabled by default in this project) is sufficient :-).
</details>
Also see our [C primer on debugging](https://csci0300.github.io/assets/c-primer3.html) for more information on sanitizers!
:::
The error you should just have encountered is from the **Leak Sanitizer**. Let's figure out why that is!
Memory is a critical resource that we must clean up when we no longer need it. This is particularly true for _dynamically-allocated memory_ (memory with a manual lifetime), which we'll learn more about in lecture 5. However, our Snake project needs to use dynamically-allocated memory early, so here's a brief preview: dynamically-allocated memory doesn't get automatically cleaned up: a long running program could eventually use all the memory in the computer! The LeakSanitizer looks for dynamically-allocated memory that is still live when your program exists, and warns you about this "leaked" memory. In other words, once our game ends, we need to make sure that we have freed all the data we allocated to play the game! As it currently stands, we seem to be forgetting to free some memory.
Luckily, the sanitizer error gives us a good idea of where to look. Look at the backtrace from the sanitizer error—you should see a call to a function called **`malloc`**. `malloc` dynamically allocates memory, meaning that we as programmers are responsible for freeing that memory with a call to `free`. *The Leak Sanitizer's message is a reminder that we have forgotten to free the memory that we had previously allocated through `malloc`!*
:::info
**What to expect**: we will will learn more about `malloc` and `free` in lecture 5, so it's okay if the idea of dynamically-allocated memory doesn't make too much sense yet. However, at this stage, we just want you to see the idea of allocating memory (`malloc`), and freeing it (with `free`), and to know that memory that is allocated with `malloc` must be freed. You'll implement this now by fixing the error, and then we'll learn more about the underlying concepts in class soon!
:::
:::success
**Task**: To start resolving the sanitizer error, go to the end of the `main` function in `snake.c` and **uncomment the line** that calls `end_game(cells, width, height, &snake)`.
The `end_game` function is responsible for freeing all the resources used up by our program, as well as stopping game. Click on the dropdown for more information on `end_game`.
:::
::::info
`void end_game(int* cells, size_t width, size_t height, snake_t* snake_p)`
<details>
<summary><b>Function Summary</b></summary>
<br />
**Purpose:** Responsible for cleaning up all the memory used by the game and stopping the renderer.
**Arguments:**
* `cells`: the array of board cells
* `width`: the width of the board
* `height`: the height of the board
* `snake_p`: a pointer to the snake struct (not used until part 3!)
</details>
::::
:::success
**Task:** You will now add a call to `free` such that the memory that your program allocated through `malloc` is freed when it calls `end_game`. To do this, you should:
1. Use the backtrace from the Leak Sanitizer to find out where the program calls `malloc`, and where the program stores the pointer it returns.
2. Look at the definition of `end_game` to find out where you need to put a call to `free`. (Does `end_game` call any relevant functions that have access to `cells`?)
3. Insert an appropriate call to `free`! (Note that `free` takes in one argument: the `cells` array (a pointer)).
<details>
<summary><b>Example of using <code>free</code></b></summary>
<br />
<pre><code>int* num = malloc(4); // allocates 4 bytes of memory
free(num); // frees up the 4 bytes
</code></pre>
</details>
After you add your call to `free`, compile and run your program again. You should now no longer see the sanitizer error.
:::
::::warning
Once you have correctly implemented this section, you should not get any Leak Sanitizer errors when running the game until Part 3.
::::
Congrats! You have now ensured that once the game ends, all allocated memory is freed before `main` exits (for now).
#### The Game Loop
Let's get some visual output.
:::success
**Task**: Now, at the end of the `main` function in `snake.c`, **uncomment the line** that calls `initialize_window(width, height)`.
The `initialize_window` function is responsible for setting up the game renderer.
:::
Many computer games are designed around the idea of a core "game loop" that repeats over and over until the game reaches a "game over" state (indicated by the `g_game_over` global variable in this project). For our version of Snake, the core game loop is as follows:
1. wait (for some specified amount of time) for user input;
2. retrieve user input;
3. update the game state based on user input; and
4. render the new game state to the screen.
To begin with, you will implement steps 1, 3, and 4 of the core game loop in the `main` function of `snake.c` (look for the `TODO` for Part 1A).
:::info
**Note:** You will initially update the game state without handling user input, so you will see your snake appear and move but not respond to arrow keys. *This is okay!* Once you have an `update` function that successfully updates the snake's movement, overall game state, and checks for wall collisions, you will handle user input.
:::
For now, there are three function calls you need to make in your game loop:
1. [**`usleep(useconds_t usec)`**](https://man7.org/linux/man-pages/man3/usleep.3.html): a library function that suspends execution for a some amount of time, measured in microseconds (we recommend 100 milliseconds, i.e., 100,000 microseconds, but you can adjust this to speed up or slow down your snake!).
2. **`update(int* cells, size_t width, size_t height, snake_t* snake_p,
enum input_key input, int growing)`**: the update function you will need to implement in Part 1B that updates the game state based on user input (see the stencil for more details).
3. **`render_game(int* cells, size_t width, size_t height)`**: a function we give you as part of the stencil that renders the given game state into a visual game! You will not need to modify this function.
Since we're not handling user input yet, you will pass in `INPUT_NONE` as the `input` argument to `update`. Additionally, since our snake does not yet grow, you will pass in `0` as the last argument to `update`. Furthermore, since we are not using the `snake_p` argument yet, you will pass `NULL` as this argument. In other words, your call to `update` will look like this: `update(???, ???, ???, NULL, INPUT_NONE, 0)`, where you'll have to figure out what to pass for each `???`.
Read through the `main` function in `snake.c` to make sure you understand what it's doing! We don't expect you to know what every single function does, but you should understand the general structure.
::::success
**Task:** Implement the core game **loop** in the `main` function of `snake.c`!
::::
:::warning
**☑️ Testing Point A:**
_Now go and read the testing section below if you haven't already. It tells you how to run the tests!_
After running `make check`, you should pass tests 1 and 2 at this point! This means that you should be able to see a board with 10 rows and 20 columns, with walls around the edges, and a snake initialised at the third row down and third column from the left. The snake should not move.
In other words, when you run `./snake 0`, you should see the following:
<center>
<img src="https://csci0300.github.io/assign/projects/assets/snake-testpoint1.png">
</center>
:::
:::success
⛳ **Checkpoint:** _Congratulations! You have completed the section required for the Part 1A checkin._ Please be sure to push your code to Github click the "Test Part 1A checkoff" button on the grading server so that we can check off your work.
If you don't reach this point by the checkin deadline, please come to TA hours so that we can help you get off to a successful start!
:::
### Part 1B: Updating and Running the Game
#### Updating the Snake's Position
Now that you have defined a way to represent the snake in our game, you can use your representation (as well as the variables storing the game and board data) to begin implementing the `update` function in `game.c`. First, you will need to write the functionality that allows your snake to move while on the board.
:::info
**Note:** In most versions of Snake, **the snake initially moves to the right**. Make sure that you replicate this convention and that your snake starts moving right when the game is initialized!
:::
The full description of the `update` function can be found in the stencil, but below is a short summary for reference.
::::info
`void update(int* cells, size_t width, size_t height, snake_t* snake_p, enum input_key input, int growing)`
<details>
<summary><b>Function Summary</b></summary>
<br />
**Purpose:** updates the `cells` array and other game data according to `input`, including the `g_game_over` and `g_score` global variables when needed, and the snake's position if the game is not over.
**Arguments**:
* `cells`: an array of integers representing each board cell
* `width`: the width of the board
* `height`: the height of the board
* `snake_p`: a pointer to your snake struct (not used until part 3!)
* `input`: the next input
* `growing`: an `int` representing whether we want the snake to grow upon eating food (`growing = 1`), or not (`growing = 0`).
**Returns:** Nothing! But the function should modify the game, board, and snake data appropriately to reflect the user's input and the snake's behavior.
</details>
::::
For now, we will only consider the case where the user input is `INPUT_NONE` (i.e. there is no user input). This means that your update function should shift the snake's position by one cell to the right, because the snake's default initial direction is to the right and we aren't taking any user input yet.
::::success
**Task:** Implement `update` so that your snake's position shifts by one cell to the right whenever `update` is called, and the game board is updated to reflect the new snake position.
<details>
<summary><b>Hints!</b></summary>
<br />
You will need to update your variables storing snake data *and* the board (from the cells array passed into `update`) to reflect the snake's new position.
* This is an instance of a common pattern in computer systems: metadata about the system state and the actual system state must always be updated together. This maintains an _invariant_ that system state and metadata always match (the metadata would be rather unhelpful if this were not the case!).
When should you initialize your snake? Where on the board should you initialize your snake?
* Hint: The board data should be initialized -- with bit flags set -- before the snake is initialized.
</details>
::::
#### Bit Flags
In the stencil, the `cells` array is defined as an array of integers. This means that each cell on the board has an integer representing what type of cell it is (i.e., a wall, an empty cell, a food cell, a snake cell, or a grass cell). We would like our game implementation to support cells that have multiple properties, such as a snake hiding in grass, or food hidden in grass. To use a single integer to represent these properties of a cell, we use the different **bits** within the integer value to represent the different states a cell can be in.
A 4-byte (32-bit) integer can hold up to 32 bit flags, but we only need four for the basic game (defined in `common.h` and shown below). Additionally, we provide `PLAIN_CELL`, which is the integer corresponding to no bit flags being set (i.e., a plain cell).
```c++
// Bit flags enable us to store cell data in integers!
// The "0b" notation indicates binary numbers.
#define PLAIN_CELL 0b0000 // equals 0
#define FLAG_SNAKE 0b0001 // equals 1
#define FLAG_WALL 0b0010 // equals 2
#define FLAG_FOOD 0b0100 // equals 4
#define FLAG_GRASS 0b1000 // equals 8
```
In the game variant you will implement in this project, `FLAG_GRASS` can overlap with `FLAG_FOOD` or `FLAG_SNAKE`, but no other overlapping flags are allowed. For example, if a cell had `FLAG_WALL` and `FLAG_SNAKE` set, the snake would have walked into a wall. We will further discuss what overlapping cells look like in terms of bit flags below. Additionally, you may further exploit the fact that cells can be in multiple states at the same time in our exciting extra credit exercises ✨.
#### Overlapping Snake and Grass
Our version of the game allows for `FLAG_GRASS` to overlap with `FLAG_SNAKE` in a cell on the board. But how can we manipulate the individual binary bits within a cell? Enter: bitwise operators!
Bitwise operators allow manipulating the individual bits within a variable. C supports several bitwise operators, including but not limited to:
* `&` is AND, which puts a 1 bit in the result if both input numbers have a 1 in the same position, otherwise it puts 0. For example, `0b0110 & 0b1100` results in `0b0100`.
* `|` is OR, which puts a 1 bit in the result if either one or both input numbers have a 1 in a position, otherwise it puts 0. For example, `0b0110 | 0b1100` results in `0b1110`.
* `^` is XOR, which puts a 1 bit in the result if **exactly one** of the two input numbers has 1 in a specific position (but not if both do), otherwise it puts 0. For example, `0b0110 ^ 0b1100` results in `0b1010`.
For the full list of the bitwise operators in C, see <u>[this section](#src)</u> in part 3!
::::warning
<details>
<summary><b>⚠️ IMPORTANT READ: Update board with bitwise operators</b></summary>
Let's imagine that a snake moves onto a block of grass. The grass is still on that block, but so is the snake. So, we need to represent the cell as having both states.
```
int cell = FLAG_SNAKE | FLAG_GRASS;
```
We know that `FLAG_SNAKE = 0b0001` and `FLAG_GRASS = 0b1000`, so `FLAG_SNAKE | FLAG_GRASS = 0b1001`.
<br/>
<b>Checking for a snake in the cell</b>
With this `cell` that is `0b1001`, how can we check whether there is a snake on this block? If we check `cell == FLAG_SNAKE`, the result will be **false**, because `0b1001 != 0b0001`. Luckily, we have the `&` operator!
```
bool has_snake = cell & FLAG_SNAKE;
```
Because `cell = 0b1001` and `FLAG_SNAKE = 0b0001`, `cell & FLAG_SNAKE = 0b0001`. Remember that in C, 0 represents **false**, and any non-zero number is considered **true**. So, we get **true**, meaning that this block has a snake on it!
<br/>
<b>Modifying cell state</b>
Now, let's imagine that the snake moves off this block. The block will still have grass on it, but will no longer have the snake. How can we update the cell state?
```
int new_cell = cell_state ^ FLAG_SNAKE;
```
Again, `cell = 0b1001` and `FLAG_SNAKE = 0b0001`, so `cell ^ FLAG_SNAKE = 0b1000`. Remember that `FLAG_GRASS = 0b1000` as well! We have successfully removed the snake from the cell but kept the grass.
Note: Visually, your snake symbol should turn green when you enter the grass. If it's not, double check your bitwise operator usage to make sure both flags are being set simultaneously!
<br />
</details>
::::
:::info
*What advantages do representing our states with bit flags give us?*
Think about the alternative where we don't use bit flags:
PLAIN_CELL = 0
SNAKE = 1
WALL = 2
FOOD = 3
GRASS = 4
In this scenario, how would you represent a cell that contains both snake and grass? One solution might be to create another representation for SNAKE_AND_GRASS. Do you see how this could complicate the logic as the game grows in complexity? Contrast that with our representation that allows us to represent every combination of state with just the original flags...
:::
::::success
**Task:** Ensure your `update` properly allows the snake to move into a grass cell (causing the snake to turn green).
::::
#### Collisions with Walls
Now, your snake can move across the board and hide in grass! But, as you should notice, your snake doesn't stop when it hits a wall, and passes right through instead!
We don't want this to happen; if your snake hits a wall, it should stop moving and the game should end immediately (think back to the game loop you wrote in `snake.c`: when does your loop break?). Your next task is to modify your `update` function so that if your snake's new position has a wall there, then the game ends. Make sure your game ends **before** your snake actually moves into the wall! We don't want your snake to appear to move through walls. Make sure to think about what `update` should do if `g_game_over` is set to 1.
:::info
**Note:** So far, we have been using the default board to run your game, but later on, you will need to handle custom boards, which may or may not have internal walls that your snake can crash into.
:::
::::success
**Task:** Modify your `update` function so that your snake collides with walls and the game ends if any such collision happens.
::::
:::warning
**☑️ Testing Point B:** You should pass tests 3-5 at this point. Your moving snake should hit the wall to the right of the board, which should then cause the game to end.
:::
#### Getting User Input
You've probably watched your snake crash into the right wall of the board many times now :(
Let's give your snake a way to avoid crashing! Your next task will be to modify your game loop in `snake.c` and your `update` function so that you can get user input, and update your snake's position based on the direction specified by the user.
We've given you a function in `snake.c`, called `get_input`, that gets arrow key input from your keyboard and converts it into an `enum input_key` - one of the enums representing a key input. Currently, when you call `update` in your game loop, you pass in `INPUT_NONE` as `input`, and `update` defaults to shifting the snake to the right. Now, you will now need to change this so that you pass in the output of `get_input` as the `input` parameter to `update`.
:::success
**Task:** Modify your game loop in `snake.c` so that you can get input from the user, and pass this input into the call to `update`.
:::
Now that your `update` function will take in different inputs (as its second to last argument), you will need to modify it once again to handle each of these different inputs, so that your snake's position is updated according to the last input by the user. Note that it **is** valid for a snake of length 1 to double back on itself (e.g. to go from moving right to moving left.)
:::info
**Aside**: For all three parts of this project, try playing the [Google Maps version](https://snake.googlemaps.com/) of Snake to figure out how to handle any edge cases you come up with! This will also be useful when trying to pass any of our tests that test for specific edge cases.
:::
::::success
**Task:** Modify your `update` function so that it can handle every type of user input, and update the snake data and board accordingly.
::::
:::warning
**☑️ Testing Point C:** You should pass tests 6-10 at this point. You should now be able to control your snake's movement using the arrow keys! Your snake should still be able to crash into the wall and end the game.
:::
#### Food
You almost have a fully playable game! The last component that we're missing is the food (without which, of course, you can't score). Next, you will need to modify your `update` function further so that if your snake "collides" with a cell on the board that contains food, `update` does the following:
- Your snake eats the food (so the food disappears)
- The snake's position is updated
- The game's score is updated
- New food is placed on the board
We have provided you with the function `place_food` in`game.c` to randomly place food on the game board. It's up to you to determine the correct place to call `place_food`!
**For now, your snake does not need to grow when it eats food. You will implement this functionality in Part 3 of this project.**
::::success
**Task:** Modify your `update` and `initialize_game` functions so that your snake can eat food, and new food is placed on the board whenever the snake eats.
<details>
<summary><b>Hint!</b></summary>
<br />
Why did we include `initialize_game` in the task? What does that function have to do with food placement?
</details>
::::
:::warning
**☑️ Testing Point D:** You should pass tests 11-15 at this point. Your snake should now be able to move around the board and eat food, as long as you don't crash into a wall!
:::
#### Yay!!! All done with Part 1.
# Part 2: Board Decompression (`game_setup.c`)
#### Introduction to Run-Length Encoding
Now that we have basic game play set up for our default board, we can now implement the functionality for setting up and running custom boards.
Because the Snake game is more fun when you can play custom levels, your game will have support for loading user-specified board layouts as a command-line argument. Let's think about how you might tell the game what board you want to run!
Humans are good at dealing with readable strings, so our first plan is to encode the state of each square of the board (wall, empty, contains snake, etc.) as a letter. For example, "E" denotes an empty square, "W" denotes a square that contains a wall, etc. If we teach our program how to read these strings, we can give it any board we want.
In this representation of a custom board, the board is represented by a looooong string that contains exactly one character for every cell on the board. For example, an 80x24 board (a typical terminal size, [which goes back to the 1970s](http://www.righto.com/2019/11/ibm-sonic-delay-lines-and-history-of.html)) would look like this:
```
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
```
This is a 1,920 character string! It would be **very** tedious to type in if you found an exciting new Snake level posted in a retro gaming magazine. How can we make this representation more efficient?
Clearly, there is a lot of redundancy in this representation: many lines just consist of repeated "W" or "E" characters. To avoid this repetition, we can specify the number of instances of a character, such as "E78" to mean "78 instances of E". This is a common compression technique called a **Run-Length Encoding** (RLE).
A run-length encoded version of the above board (going row-by-row) yields this *much* shorter string:
```
W80W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1
W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1W1E78W1
W1E78W1W80
````
To make this more readable, let's introduce a delimiter between adjacent rows:
```
W80|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|
W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|W1E78W1|
W1E78W1|W1E78W1|W80
````
As a sanity check against incorrect transcription, we can add the board size at the beginning, encoded as "B" followed by the height (number of **rows**) and width (number of **columns**), and we can use the "S" character to encode the starting position of the snake. **For this project, we will use "S1" to encode a single-cell snake.** While it would be more efficient to encode the snake as a single S, we'll opt to keep things consistent with the encoding scheme, which will hopefully make things easier! *You should never start off with more than one snake cell.*
Of course, **don't forget about grass!** In the board string, we use "G" to represent a block with grass. Although a snake will never _start_ on a block with grass, remember that once the game begins, the snake or food can be on the same cell as grass.
The following example represents a 7-row, 10-column board with some internal walls and a snake at the bottom center:
```
B7x10|W10|W1E4W5|W2E5G2W1|W1E8W1|W1E4W1E3W1|W1E2S1E1W1E3W1|W10
```
On the screen, this board will look something like this:
![example board](https://csci0300.github.io/assign/projects/assets/snake-testpoint4.png)
:::info
<details>
<summary><b>Reminder on how to run the game with a custom board</b></summary>
<br />
Below is an example of how to run the game with a custom board. (The `0` is because the snake should not grow yet, since you will be implementing snake growth in Part 3.)
```shell
$ ./snake 0 "B5x10|W10|W1E8W1|W1E8W1|W1E3S1E4W1|W10"
```
</details>
:::
#### Handling Errors
As you may have thought about, there are countless ways that a RLE-encoded board representation can be incorrect. These can range from problems with the actual board, to errors when writing the string.
As a general design principle, we prefer that our programs be able to explicitly handle expected errors, rather than leaving it up to undefined behavior to dictate what our code will do. However, because there are *many* different errors, our tests only require that you handle a subset of them. (You are welcome to still handle other errors if you wish!)
:::info
**Errors you are required to handle:**
In `game_setup.h`, you are provided the following enum:
```c
enum board_init_status {
INIT_SUCCESS, // no errors were thrown
INIT_ERR_INCORRECT_DIMENSIONS, // dimensions of board do not match dimensions specified by the string
INIT_ERR_WRONG_SNAKE_NUM, // no snake or multiple snakes are on the board
INIT_ERR_BAD_CHAR // string contains a character that was unexpected
};
```
We guarantee the following about the strings that we will test your implementation against (although once again, you are more than welcome to cover other error cases):
* Letters will always be followed by positive, non-zero numbers (staying true to RLE)
* The dimensions will always be specified at the beginning of the string, and will be followed by the correct delimiter
* Dimensions will be larger than 0
<details><summary><b><code>INIT_ERR_INCORRECT_DIMENSIONS</code></b></summary>
<br />
<b>Definition:</b> <code>INIT_ERR_INCORRECT_DIMENSIONS</code> should be returned if the dimensions specified at the start of the encoded string do not match the dimensions of the board specified by the string.
<br />
<br />
<b>Example:</b>
```
B3x5|W5|W1E1S1E1W1|W1E3W1|W5
```
The above string is wrong, because the dimensions at the front specify that the board should have 3 rows, but rest of the string encodes 4 rows worth of cells.
This error also applies to mismatches between the dimensions specified and the number of columns. Think about what a string that is incorrect in this way would look like!
</details>
<details><summary><b><code>INIT_ERR_WRONG_SNAKE_NUM</code></b></summary>
<br />
<b>Definition:</b> <code>INIT_ERR_WRONG_SNAKE_NUM</code> should be returned if there does not exist <b>exactly</b> one snake cell on the board.
<br />
<br />
<b>Example:</b>
```
B3x3|W3|W1E1W1|W3
```
In the above example, there are no snake cells. This error also applies to boards that start with too many snake cells: try and come up with example strings that demonstrate this!
</details>
<details><summary><b><code>INIT_ERR_BAD_CHAR</code></b></summary>
<br />
<b>Definition:</b> <code>INIT_ERR_BAD_CHAR</code> should be returned if you encounter an "alien" character. There are many ways to define what an "alien" character is, but we will define it as <b>characters that are not expected to be found in a correctly formatted RLE-encoded board string</b>.
<br />
<br />
<b>Examples:</b>
```
B3x3|W3|W1S1Z1|W3
```
```
A1-4|W2S1W1
```
In the first example, we see an unexpected character, `"Z"` that does represent any valid cell type on our board. In the second example, `"-"` is used as a delimiter between the height and the width of the board, where we expect to see `"x"`. Additionally, the string starts with "A" instead of "B".
</details>
:::
::::success
**Task:** Implement `decompress_board_str()` with proper error handling. Look at the dropdown for more details (and a helpful hint)!
<details><summary><b>Function Summary</b></summary>
<br />
```c
enum board_init_status decompress_board_str(int** cells_p, size_t* width_p, size_t* height_p, snake_t* snake_p, char* compressed)
```
Takes in a pointers to board data (<code>cells_p</code>, <code>width_p</code>, <code>height_p</code>), pointer to a snake struct (not used until part 3), and a RLE-encoded string containing the custom board (<code>compressed</code>), and should populate the cells of the board with the correct values that correspond to the string.
This function returns a value of type <code>enum board_init_status</code>. This type represents whether the board decompression was successful, and will be communicated to the main game loop by `initialize_game`. (This means that the return value of `initialize_game` should no longer unconditionally be the return value of `initialize_default_board`!) If no errors arise, then `INIT_SUCCESS` should be returned.
</details>
<details><summary><b>A Rough Strategy</b></summary>
<br />
Below, we have given you some of the steps you could take to write `decompress_board_str` (but there can be many other valid ways of implementing this function!)
1. Try and first break down the compressed string into smaller pieces. Think about how you can leverage the delimiter character "|", which separates the encoding of our board by row.
2. Figure out how to set the dimensions of your board according to what was specified in the prefix of the encoded string. Here, "x" is your magical delimiter—you know that everything between the starting "B" and "x" should be an integer that corresponds to the height of the board, and everything between the "x" and the first "|" delimiter will correspond to the width of the board. Note: this can be trickier than it seems so we suggest encapsulating the logic in a helper function.
3. You will likely want to loop through your strings and fill in the board's cells as you go. This means that you will need to store some information regarding **where in the board you are**, **what type of cell you are currently reading**, and **how many consecutive cells of that type you want to fill in**.
</details>
<details><summary><b>Hints!</b></summary>
<br />
1. It may be a good idea to first take a look at [**C's string library functions**](https://man7.org/linux/man-pages/man3/string.3.html) (`strtok`, `strtok_r`, or `strtol` might be of particular use). Many of these functions will likely handle some of the steps you come up with to decode a compressed board string, so make sure to take a look at some of their descriptions!
:::warning
<details><summary><b><code>strtok</code> warning!</b></summary>
<br />
`strtok` cannot parse two different strings at once! If you need to begin parsing one string, switch to parsing another string, and then back to the first string, consider [`strtok_r`](https://man7.org/linux/man-pages/man3/strtok.3.html)!
</details>
:::
2. There are a few different ways to convert strings to integers. A helpful function to look at is [<code>atoi</code>](https://man7.org/linux/man-pages/man3/atoi.3.html)! Alternatively, you can also leverage the fact that the hex-encodings of numerical characters range from `0x30` (0) to `0x39` (9), and subtract accordingly.
3. It is *strongly recommended* that you design and implement some helpers for `decompress_board_str`. Parsing and manipulating strings in C can become complicated, so having a few helpers that handle repeated subtasks will help you organize your code.
Here are three helper functions we specifically recommend:
- `enum board_init_status set_dimensions (fill in arguments)`
- Given some dimensions string (ex. 'B78x10'), extract the corresponding width and height
- `int* get_cell_pos (fill in arguments)`
- Takes in row and column of a board and returns corresponding index of the board's cell array
- `enum board_init_status fill_cells (fill in arguments)`
- Given a start index into cells, a number, and a character, fill cells[start index] to cells[start index + number] with the character’s flag
</details>
::::
::::success
**Task:** If you have not already done so, update `initialize_game` to support decompression! This function should call on `decompress_board_str` if the string passed into it (`board_rep`) is *not* `NULL`. If it is, then just initialize the default board with `initialize_default_board`.
Notice that now, `initialize_game` must not always return `INIT_SUCCESS`, because decompression can return a number of failure statuses. If necessary, update the return value accordingly! Also make sure you handle these fail statuses in `main`!
::::
:::warning
**☑️ Testing Point E:** You should pass tests 16-28 at this point. You should also be able to create custom boards from compressed board strings, and play through them.
**Note:** If you're having trouble passing test 28, note that our test runner (c.f. `run_test()` in `autograder.c`) does **not** work by calling the `main()` function. Instead, it calls `update()` for each move until the end of the input string. What does that mean for how you write `update()`?
:::
#### Woo! Congratulations on finishing part 2!
# Part 3: Growing Snake + Game Over
### Part 3A: Growing Snake
Before we add any functionality to our snake, let's reconsider how we store our snake's data. Currently, we use global variables to store our snake data. However, as the amount and complexity of the data we want to store about our snake increases, this becomes harder to keep track of--we have to keep a mental tally of how every function in our program interacts with these global variables. To remedy this, we will move to using a struct to represent snake data, allowing us to pack our snake data together in a much more coherent and readable way.
::::warning
<details>
<summary><b>Caution for global variables</b></summary>
<br />
In general, it is best practice to avoid using non-constant global variables. In part 1, we had not yet covered structs, and so we used global variables to aid as a gentler introduction to C. If you would like more details about why global variables are discouraged, see [this article](https://embeddedartistry.com/fieldatlas/the-problems-with-global-variables/).
</details>
::::
Take a look at `common.h` again. We have declared for you a snake struct that we will refer to as `snake_t` to store all your snake data. Also note that we already define a variable of type `snake_t` in `main` for you.
:::success
**Task:** At the bottom of `common.h` fill in the snake struct with the snake data **you declared in 1A**. We already reserve the space for a `snake_t` in `snake.c`-- so you can remove the definitions of your snake data in `common.c` and the declarations for your snake data in `common.h`. You should only move your snake data to the struct -- you do **not** need to move the global variables `g_game_over` or `g_score`.
Now modify `initialize_game` and `update` to use your snake struct instead of global variables.
:::
We've now got a moving snake with a better data structure… but it is still a very very short snake. It would be nice if our snake would actually grow upon eating food.
This means we'll need to extend our current `snake` struct definition to handle *multiple* snake coordinates (one for each cell of the snake). We could do this in a number of ways:
- We could add, say, 100 extra x- and y-position fields to our struct, and use them to store the snake's position(s) as it grows longer. However, we'd only be able to support snakes of up to length 100.
- We could replace the current coordinate fields with an array of positions, [and extend the array as necessary](https://man7.org/linux/man-pages/man3/realloc.3.html). However, we'd have to update every entry in the array whenever the snake moves. (Note: it is possible to avoid doing so. Can you think of an approach that could avoid updating every single array entry at every step?)
- Or…
#### Linked Lists
- We could add a field with a pointer to a [linked list](https://en.wikipedia.org/wiki/Linked_list) of coordinates, and extend/trim the list as the snake moves. This would require fewer updates than either of the prior two approaches, while accomplishing our goal of implementing a longer-than-one-cell snake!
:::warning
:warning: This part of the project makes use of your linked list code from Lab 2.
**_If you have not completed Lab 2, we suggest that you do it now!_** :warning:
:::
:::success
**Task:** Copy your (fixed!) linked list code from Lab 2. Place it in the (currently empty) `linked_list.<c|h>` file in your project folder.
Additionally, now that your snake representation will need to use the structs and functions from `linked_list.<c|h>`, we need to introduce the linked list code to `common.<c|h>`, where our snake and board are defined.
To do so, add the following line to the top of `common.h`:
```c
#include "linked_list.h"
```
:::
:::success
**Task:** Update your `snake` struct to use a linked list to store the snake's current position, and update your `update` function to handle this new structure. The snake doesn't need to grow yet, but your code should still pass the same tests.
:::
#### A Snake that Grows
We're almost there! Now we need to figure out how to 1) extend the snake when it eats food and 2) update the list of positions when the snake moves.
:::warning
**Warning:** The snake should only grow if `snake_grows` is equal to `1`. If your snake grows when `snake_grows` is 0, your code may fail prior tests!
:::
::::success
**Task:** Implement snake growth on food consumption (and provided `snake_grows` is `1`).
<details><summary><strong>Hints on growth</strong></summary>
<br />
Start by considering the list corresponding to a snake with a length of at least three. When the snake moves one step:
* Which elements can you keep the same?
* Which elements should you discard from the list?
* What do you want to add to / update about the list?
We recommend you draw this out for yourself!
You may also run into a couple of edge cases while testing your implementation:
* What happens if the snake hits itself?
* What if the head of the snake moves into the space that the tail of the snake just left?
* See the note below about the snake backing into itself!
</details>
::::
:::info
**Aside:** In the actual snake game, if the snake is length 2 or longer and immediately tries to go back the way it came (backing into itself), the movement is not accepted and the snake continues to move in its previous direction. If the snake is length 1, it should be able to immediately go back the way it came. *We would like you to replicate this behavior so that the game doesn't end any time the snake tries to collide with itself by moving backwards!*
:::
Last but not least, even though your game loop in `snake.c` should break out of the loop when the game is over, one could imagine a different implementation that just stops updating the board once the game ends. In these cases, we don't want to be able to continue responding to keystrokes and updating the board after the game has ended. In order to make your `update` function extensible to multiple different game loop implementations, we'll add one final feature!
:::success
**Task**: If `g_game_over` is set to true (1), make sure your `update` function does nothing!
:::
:::warning
**☑️ Testing Point F:** You should pass tests 29-39 at this point. Your snake should now grow when it eats food!
:::
### Part 3B: Game Over
You've almost got a complete game! Let's wrap it up nicely with a personalized Game Over screen featuring the player's name and their score.
#### Rendering the Game Over screen
We've provided a few helper functions for rendering the Game Over screen:
- `end_game`, which calls `render_game_over` and manages some graphics boilerplate
- `render_game_over`, which renders a Game Over screen featuring the player's name and score.
`render_game_over` is located within `game_over.c` (and `game_over.h`), which has not been included in compilation up until this point. This is because it isn't in the *Makefile*. Makefiles tell the compiler how to manage your source code files; you'll learn more about this in [Lab 1](https://csci0300.github.io/assign/labs/lab1.html).
:::success
**Task:** Look through the Makefile and find `OBJS = src/game.o src/game_setup.o …`. Add `game_over.o` to the list of objects to be compiled. Then, **uncomment** `render_game_over` in `game_over.c`.
Now, try compiling the project. What happens?
:::
Oh no! It looks like one of our functions assumes that some global variables exist. Look through the code in `game_over.c` and see if you can figure out why they are needed, and what their types are. (**Hint:** How do we represent strings in C?)
:::success
**Task:** Add the necessary global variables to `common.<c|h>` so that the project compiles successfully.
:::
#### Prompting and reading in user input
We now have variables for the player's name and its length. We still need a way to prompt the user to enter their name and parse the input. Head on over to the `read_name` function in `game.c`, where you'll implement this functionality for reading in user input.
First, you want to print out a prompt for the user (e.g. `"Name > "`). Then, use the [`read` system call](https://man7.org/linux/man-pages/man2/read.2.html) to read their response. **Note that the `read` system call does not null-terminate the data that it reads!**
* If the user presses enter without inputting anything else, *print an error message saying `"Name Invalid: must be longer than 0 characters."` and reprompt the user for their name.*
Once the user inputs a name with at least one character (apart from hitting enter), then store their name and return.
:::success
**Task:** Implement `read_name` as detailed in the dropdowns below. Once you have done so, uncomment the following lines in `main` (`snake.c`):
```c
// TODO: Implement (in Part 3B)
char name_buffer[1000];
read_name(name_buffer);
```
```c
void read_name(char* write_into)
```
<details>
<summary><b>Function Summary</b></summary>
<br />
**Purpose:** Prints a prompt to the console asking the user to input their name (e.g. `"Name > "`, and reads in the user input. If at least one character is inputted, stores the input in the string buffer, `write_into`. Otherwise, prints an error and reprompts.
**Arguments:**
* `write_into`: String buffer where the user's input will be stored
**Returns:** Nothing, but if the user enters without typing any other characters, the function should print the following error to the console and repeat the earlier steps of the function.
* Error: `"Name Invalid: must be longer than 0 characters."`
</details>
<details>
<summary><b>Implementation Notes!</b></summary>
<br />
**Prompting**
1. You may use the `printf` function to print out your prompt to the console.
2. After you print out your prompt, use `fflush` to forcibly flush the buffer:
```c
fflush(<file descriptor of where you are reading from>);
```
*This is because by default, whatever you wish to write gets added to a buffer, so you sometimes need to forcibly flush the buffer before reading in input! Without the call to `fflush`, you may or may not see your printed prompt.*
**File Descriptors**
The first argument to the [`read` system call](https://man7.org/linux/man-pages/man2/read.2.html) is an integer representing a *file descriptor*. You'll learn more about file descriptors later in the course, but for now, you can think of it as telling the computer where it should read from. Listed below are three very common file descriptors and the locations that they correspond to. It's up to you to figure out which one you want to read input from!
| FD | Location | Abbreviation |
| --- | --- | --- |
| 0 | Standard Input | STDIN |
| 1 | Standard Output | STDOUT |
| 2 | Standard Error | STDERR |
**Length to read**
The third argument to the `read` system call is a value of type `size_t` denoting how many bytes we want to read. While we ideally do not want to be making assumptions about how long people's names are, we unfortunately need to tell the computer how much to read. To that end, **set this value to 1000** in order to be fairly generous with the size of our input.
**Erroring appropriately**
When the user presses enter without entering any other characters, what will be the string read into the buffer? What will `read` return? Use this to assess how you will know to print an error and reprompt the player!
</details>
:::
### Socially Responsible Computing: Character Sets
The default string encoding in C, as you might recall from lectures and SRC Section #1, uses the ASCII standard, which represents each character as one byte. ASCII is the American Standard Code for Information Interchange. However, ASCII is only meant for English and it does not support accents or characters from non-Latin alphabets (for example, you cannot type the character ñ in a C@B override request).
To ensure that we are able to read in all player names that a user could input, and render them correctly, we will need to support a different character encoding. Programmers often turn to extended character sets (most commonly **Unicode**, and specifically its [UTF-8](https://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8) encoding) to create more inclusive applications. We covered Unicode in SRC Section #1, so feel free to refer to the [section slides](https://docs.google.com/presentation/d/1GbwBnwZZIek4sCWp3MDVjEEdSjhFBG3h7IOxN5MmLWA/edit?usp=sharing) for more information on what Unicode is and is not.
When programming for extended character sets, some of the standard library support functions for single character (ASCII) strings will not work as intended.
C provides support for multibyte characters through their `wchar_t` struct, which represents a character in 4 bytes. However, since many extended character sets have variable length characters (for example, UTF-8 encodes characters in 1-4 bytes), using `wchar_t` leads to wasted space in memory. Moreover, programs often receive UTF-8 text as input that isn't nicely laid out in 4-byte chunks, and must still be able to process it. (As an example, consider a website that processes input from a form: users may put strings like "γνωρίζω" or "❤️😺" into the form!)
UTF-8 characters follow a set byte format based on their length. 1 byte UTF-8 characters are meant to be compatible with ASCII, which means that they always begin with a 0 bit. For 2-4 byte UTF-8 characters, the number of leading 1s indicates the length of the character (in bytes). Each subsequent byte begins with the bits 10. You can find an overview of the format below:
| Length (bytes) | Encoding (binary) |
| ---------------------- | ----------------------------------------------------------------------- |
| 1 | `0xxxxxxx` |
| 2 | `110xxxxx 10xxxxxx` |
| 3 | `1110xxxx 10xxxxxx 10xxxxxx` |
| 4 | `11110xxx 10xxxxxx 10xxxxxx 10xxxxxx` |
#### `mbslen()`
Now, to ensure that we are able to read in any name that a user could input, and render them correctly, we need to implement a function that will allow us to find the number of UTF-8 characters that a user inputs.
:::success
**Task:** Implement `mbslen()` in `mbstrings.c`.
:::
::::info
<details>
<summary><b>mbslen:</b> Counts the number of UTF-8 code points ("characters") in a UTF-8 encoded string.</summary>
<br />
**Synopsis:**
`mbslen` counts the number of code points in the UTF-8 multi-byte string stored in **bytes** until a null character is hit. Note that in this part of the assignment the number of "characters" does not correspond to the number of bytes, but to the number of valid UTF-8 code points; see the UTF-8 encoding table for more details.
```c
size_t mbslen(const char *bytes);
```
**Examples:**
```clike
char *emojis = "🥳😊😄";
size_t len = mbslen(emojis);
// len = 3
```
</details>
::::
::::info
<details>
<summary id="src"><b>Hint: </b>Remember bitwise operators?</summary>
<br />
While we already showed you `&`, `|`, and `^`, C supports several other bitwise operators as well. Here is a full rundown of operators you may find useful:
* `&` is AND, which puts a 1 bit in the result if both input numbers have a 1 in the same position, otherwise it puts 0. For example, 0b0110 & 0b1100 results in 0b0100.
* `|` is OR, which puts a 1 bit in the result if either one or both input numbers have a 1 in a position, otherwise it puts 0. For example, 0b0110 | 0b1100 results in 0b1110.
* `^` is XOR, which puts a 1 bit in the result if **exactly one** of the two input numbers has 1 in a specific position (but not if both do), otherwise it puts 0. For example, 0b0110 ^ 0b1100 results in 0b1010.
* `~` is binary negation, which flips all the bits in a number. For example, ~0b0111 results in 0b1000.
* `>>` shifts a binary number right by some number of places. For **unsigned** numbers, this means filling the top bits with zeros and dropping the bottom bits. For example, 0b0111 >> 2 results in 0b0001. For **signed** numbers, this means filling the top bits with the most significant bit, which you can learn more about [<ins>here</ins>](https://www.chipverify.com/digital-fundamentals/signed-unsigned-binary). For example, 0b0111 >> 2 results in 0b0001 and 0b1000 >> 2 results in 0b1110. This difference applies to all unsigned/signed types, including characters, since all values are stored as binary.
* `<<` shifts a binary number left by some number of places, filling the bottom bits with zeros and dropping the top bits. For example, 0b0111 << 2 results in 0b1100. This is consistent between signed and unsigned numbers.
We recommend casting to `unsigned char` for this assignment, specifically due to how the right shift operator differs between `signed` and `unsigned` numbers.
If you need examples on using bitwise operators in calculations, see <u>[this section](#bitwise)</u> in part 1!
</details>
::::
:::success
**Task:** Once you have successfully implemented `mbslen()`, call it in snake.c on the name the user inputs and store the result in the appropriate place.
:::
We're almost done! Let's tie it all together and present the game over screen.
:::success
**Task:** Edit `end_game(…)` and uncomment the lines around and including `render_game_over(…)` (which implicitly requires the name and its length) so that your snake game renders the final screen when the game is over.
:::
:::warning
**☑️ Testing Point G:** You should pass all the tests at this point! You should be able to prompt a user for their name, and then store their name and name's length (according to unicode characters), and see the final game over screen rendered when a game ends.
You should also ensure that your game is playable by running ```./snake 1```.
:::
### More on Unicode and Reflection Questions
![relevant xkcd](https://i.imgur.com/R1tBhi8.png)
::: success
**Task:** Read sections 2.1 and 2.2 of version 1.0 of The Unicode Standard and the quotations below as indicated. Write your answers to the following three questions in the SRC section of your `README`. Please note that **you will need to consult a classmate for Question 3!** You will be given the opportunity to find a partner in your section. If you have special circumstances or encounter any difficulty, please contact us and we will match you with a classmate. Also, this [linked table](https://docs.google.com/document/d/1SaOhYPv52iHEiNDB0zsYQIT8-DDKhBvrGh7jmvfq2cg/edit?usp=sharing) from section is a resource that could be helpful to define the terms used in the rest of this assignment.
:::
The primary goal of the Unicode character encodings is to accurately represent "all characters in widespread use today." With that, the [Unicode Consortium](https://www.unicode.org/consortium/consort.html), the organization responsible for maintaining and updating the Unicode standard, must appropriately decide which characters to include.
1. Who founded the Consortium? Who is represented among the current members, and how might that affect decisions being made for Unicode?
Accounting for every possible language is difficult; the Unicode standard supports many, but not all.
2. Find a language that is not yet in Unicode, has only recently been supported (e.g., in the last 10 years), or is/was missing important characters. What is/was missing, and how does this affect the users of the language?
Beyond missing languages and characters in languages, Unicode has also faced controversies with representing and unifying the encodings of similar languages. One of the biggest controversies is [Han Unification](https://en.wikipedia.org/wiki/Han_unification) (which you should already be familar with from section), an attempt to unify the Chinese, Japanese, and Korean (CJK) languages into one common encoding.
The Ideographic Research Group (IRG) is responsible for creating and detailing the principles of Han Unification. The IRG is made up of experts from China, Japan, North and South Korea, Vietnam, and other regions that have historically used Chinese characters, as well as representatives from the Unicode Consortium. **Read sections 2.1 and 2.2** of this [excerpt](https://www.unicode.org/versions/Unicode1.0.0/V2ch02.pdf) (feel free to skim the rest) from version 1.0 of The Unicode Standard that explains some of the history and rationale of Han Unification (Note that the IRG is the modern reincarnation of the CJK-JRG mentioned in the document).
This [Unicode Technical Note](https://www.unicode.org/notes/tn26/) explains why CJK scripts are unified, while Latin, Greek, and Cyrillic scripts aren’t. Meanwhile, [Suzanne Topping of IBM explains](https://web.archive.org/web/20131216023226/http://www.ibm.com/developerworks/library/u-secret.html) some of the downfalls of this decision:
>Fonts are the delivery mechanism for glyph display. This means that while Han characters have been unified, fonts can't be. In order to provide the glyph variations required to meet the needs of writing system differences, fonts must be specific to the system in use. Trying to use only one font guarantees that some characters won't look right to everyone.
For example, because of Han Unification, writing bilingual texts in multiple CJK languages can be difficult because it necessitates constantly shifting typefaces in order to display characters correctly. Topping also explores the social implications of Han Unification:
>Part of the objection to Han Unification is cultural. There is a perception that languages have been merged rather than just characters. This is an understandable confusion given that previous character sets and encoding methods have been language-specific. If the old character sets were merged and unified in Unicode, there is a feeling that the languages are somehow unified as well. East Asian cultures are unique and diverse, and would have valid objections if there were attempts made to somehow merge languages. While this is not the case, there is obviously some confusion about how unification works.
3. For this question, you will need to work with a classmate! Make sure to coordinate so that you outline **opposing positions** for Part A (e.g. if you outline the 'for' position, your partner defends the 'against' position). Then, come together and discuss in Part B!
In your `README`, include your:
* Partner’s CS login
* Your individual answer to Part A
* A summary of your discussion for Part B (include disagreements you had, ideas you may have had in common, convincing evidence you encountered, etc.)
* Feel free to represent this discussion however you think is most effective (e.g. bullet points, as a dialogue, standard written paragraph(s), etc.).
* This summary can be identical to your partner's.
A. Outline either the position for Han Unification, or the position against Han Unification. (1-2 paragraphs, note: this is individual work)
B. Have a discussion with your partner where you each advocate for your chosen position. In your response, note how your partner pushed back on your position and how you pushed back on your partner's position. What evidence did they provide? Which of their arguments challenged yours directly? Describe both partners' positions at the end of your conversation independent of each person's initial position in part A (it is okay if your position is neutral or if you are unsure, if that is the case then explain why).
#### You did it!! 😃 Go get some high scores (and post them on Ed) 🎮
# Extra Credit
If you're feeling adventurous, try to extend your game with some additional fun features! Make sure to gate your extra credit with a command line flag, special optional characters in board strings, or a `#define` so that existing tests don't break.
The amount of TA help you can get here may be limited. Please describe your implementation and how to test it in the "Extra Credit Attempted" section of your README.
_Extra credit is in no sense required to get an A, except if you’re a graduate student taking CSCI 1310, in which case we require you to achieve 3 extra credit points on this project._
**Extra credit's contribution to your grade is limited to 10 points. You're very welcome to attempt more, though, and impress us 😊.**
## Gameplay
### Grow more than one length on each food
* You may find that some [online snake implementations](https://patorjk.com/games/snake/) have the snake grow more than one unit on each food. `#define` a constant that corresponds to the snake's growth length, and have the snake grow accordingly (**2 points**).
### Make a bot
* Create a simple bot that plays your game by moving the snake in random directions! (**3 points**).
* Go beyond the simple bot — make it do :sparkles: better :sparkles: (**3 points**).
### Powerups!
* MULTISNAKE (**2 points for design, 4 for implementation**).
* Multi-speed snake? Support areas of the board (or items the snake can collect) that make it slower or faster. [Hint: recall that we use bit flags to represent cell state, so a cell could be in "slippery" state and change the snake speed when the snake is on it!] (**2 points**).
Set your creativity free with the following freestyle ideas! Figure out what thesy could mean and add support to the game. There is no right or wrong design here, just FUN 🥳!
* A snake that shrinks??? (**2 points**).
* Temporarily Incorporeal Snake? (**2 points**).
* Extra Life Snake? (**2 points**).
* Enemy snakes (**4 points**).
* Rainbow invincible snake (**2 points**).
### Wraparound
* We currently do not define behavior when the snake hits the edge of the board (N.B.: the edge, **not** an outside wall). Make it so that the snake wraps around to the other edge when it runs off the board. (**4 points**)
## Visuals
These ideas will likely involve working with the renderer (`render.c`), which uses the `ncurses` and `ncursesw` libraries (for wide character support).
### Better Snake visuals
* Rather than rendering "S" characters, make a solid green snake [Hint: use Unicode characters like we do for the walls] (**2 points**).
* Make the food look like food [Hint: Unicode food emojis] (**2 points**).
### Bell character on food 🔔
* Read about [the "bell" character](https://en.wikipedia.org/wiki/Bell_character) (ASCII `0x7`, Unicode `U+0007`) and then add it to the game at relevant points (**2 points**).
### Better UI
* Add a startup menu screen (**2 points**).
* Add a pause menu screen (**1 point**).
* Add a high score board! For this, you'll need to be able to run the game multiple times on one run of `snake.c`, and find a data structure to track high scores (**3 points**).
## Misc
### Interesting Level Design
* Make a cool level and share it on EdStem (**0.1 points per heart received**).
* Make the coolest level as voted by your peers (**1 point, gold star and a round of applause**).
* Recreate a Brown building's floorplan as a level (**0.5 points**).
# Handing In Your Code
You will hand in your code using Git. **In the `snake/` subdirectory, you MUST fill in the text file called `README.md` given in the stencil**. The `README.md` file will include the following:
1. Any design decisions you made and comments for graders, under _"Design Overview"_. If there's nothing interesting to say, just list "None".
2. Any collaborators and citations for help that you received, under _"Collaborators"_. CS 300 encourages collaboration, but we expect you to acknowledge help you received, as is standard academic practice.
3. Your answers to the SRC reflection questions under _"Responsible Computing Questions"_.
4. A description of your extra credit work (if any) under _"Extra Credit Attempted"_.
5. Notes on approximately how long it took you to complete the project. We won't use this for grading, but we collect the data to calibrate for future years.
Here's a Markdown template for your README. We strongly recommend that you use this template! (This template is already given to you in the stencil.)
```
CSCI 0300 Project 1 - Snake
===========================
## Design Overview:
*TODO!*
## Collaborators:
*TODO!*
## Responsible Computing:
*TODO!*
## Extra credit:
If you choose to do extra credit, detail it here.
## How long did it take to complete Snake?
*TODO!*
```
_**Grading breakdown:**_
* **15% (15 points)** answers to Socially Responsible Computing questions.
* **5% (5 points)** completed README.
* **80% (80 points)** tested functionality.
* Each test is weighted equally: 1.5 points per test, and you get 0.5 points for passing them all.
* Note that even if you pass every test, you will lose points if your game isn't playable.
* **Extra Credit** will be worth up to 10 additional points. _Students in CSCI 1310 need to complete at least 3 points of extra credit._
Your submission will not be graded until you configure the **[grading server](http://cs300.cs.brown.edu/grade/2025)**. You should have used the grading server for Lab 0 already.
:::danger
If you were registered for CS 300 on the first day of classes, you should have received credentials by email.
If you only recently registered or you're shopping the course but haven't registered, you won't have an account yet. In that case, please complete [**this form**](https://forms.gle/hgvKusk3BFHXshLs7) and wait until you receive your credentials. **We will create accounts based on the form entries at 8pm every day.**
:::
**Step 1:** Log into the grading server with the password you received. Once you're logged in, enter your GitHub username (at the top).
**Step 2:** Add the repository URL under the "Project 1: Snake" category. Click "Project 1: Snake" to initiate a fetch of your repository from GitHub.
:::info
**Note:** If your GitHub account is not linked to your Brown email address, the grading server will give you a command to run to verify your project repository.
:::
**Step 3:** You should see your commit listed on the "Project 1: Snake" page. You can use the buttons below the commit list to test your code in our grading environment. Make sure it still passes all the tests!
:::info
**Note:** If the commit you want graded is not your most recent one, you should flag the one you want graded and the grading commit will be updated after the autograder finishes.
:::
Congratulations, you've completed the first project! :clap:
---
<small>_Acknowledgements:_ This project was developed for CS 300 by Benjamin Lee, Casey Nelson, Livia Zhu, Rhea Goyal, Sreshtaa Rajesh, Paul Biberstein, Richard Tang, Sophia Liu, Vic (Shihang) Li, and Malte Schwarzkopf.</small>