Difference between revisions of "F24: Brick Breaker"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Team Members & Responsibilities)
(Schedule)
 
(6 intermediate revisions by the same user not shown)
Line 167: Line 167:
 
* Update the wiki page.
 
* Update the wiki page.
 
|
 
|
* <span style="color:orange">In progress</span>
+
* <span style="color:green">Completed</span>
* <span style="color:orange">In progress</span>
+
* <span style="color:green">Completed</span>
 
|-
 
|-
 
! scope="row"| 10
 
! scope="row"| 10
Line 188: Line 188:
 
*Update the wiki page.
 
*Update the wiki page.
 
|
 
|
* <span style="color:red">Not started</span>
+
* <span style="color:green">Completed</span>
* <span style="color:red">Not started</span>
+
* <span style="color:green">Completed</span>
* <span style="color:red">Not started</span>
+
* <span style="color:orange">In progress</span>
* <span style="color:red">Not started</span>
+
* <span style="color:orange">In progress</span>
 
|-
 
|-
 
|}
 
|}
Line 243: Line 243:
  
 
== Design & Implementation ==
 
== Design & Implementation ==
The design section can go over your hardware and software design. Organize this section using sub-sections that go over your design and implementation.
+
For the design and implementation of the Brick Breaker game, the SJ2 board serves as the central controller to interface with a 64x64 LED matrix, control buttons, and an accelerometer. Each of these components plays a crucial role in the functionality of the game, enabling dynamic gameplay and interactivity.
  
 
=== Hardware Design ===
 
=== Hardware Design ===
Discuss your hardware design here. Show detailed schematics, and the interface here.
+
In the hardware setup for this project, we outlined a few high-level connections that were necessary to implement the Brick Breaker game. These connections can be visualized in the top-level schematic shown below. The schematic was designed using draw.io.
 +
 
 +
Hardware Interconnects:
 +
*SJ2 Connection to the LED Matrix
 +
*SJ2 Connection to Buttons
 +
*SJ2 Connection to Accelerometer
 +
 
 +
SJ2 Board Connection to LED Matrix:
 +
The SJ2 board controlled the 64x64 LED matrix, which served as the primary visual interface for the game. The matrix required a range of GPIO pins for row, column, and color control. It did not use standard communication protocols like I2C or UART but relied on GPIO switching to drive the display. The key pin connections are as follows:
 +
{| class="wikitable"
 +
|- style="background-color:#34ff34;"
 +
! Address Pins
 +
! Color Pins
 +
! Control Pins
 +
|-
 +
| A, B, C, D, E
 +
| R1, G1, B1 | R2, G2, B2
 +
| OE, CLK, LAT
 +
|}
 +
*Address Pins: Allowed selection of specific rows for updating pixel data (rows 0 - 31).
 +
*Color Pins: Enabled 8 distinct colors through combinations of red, green, and blue.
 +
*Control Pins:
 +
** OE (Output Enable): Controlled when the display was active.
 +
** CLK (Clock): Synchronized data transfer for row and pixel updates.
 +
** LAT (Latch): Locked data into the display.
 +
 
 +
SJ2 Board Connection to Buttons
 +
The buttons served as an input interface for the player to control the paddle in the game. The buttons were connected to GPIO pins on the SJ2 board and configured to trigger on falling-edge interrupts. Each button press caused an interrupt, which was handled by a software ISR to update the paddle’s position on the LED matrix.
 +
 
 +
Key features of the button setup:
 +
 
 +
*Interrupt-Driven Design:
 +
**Each button press generated an interrupt to ensure immediate responsiveness.
 +
*Debouncing:
 +
**Software debouncing eliminated the effects of mechanical noise in button presses.
 +
 
 +
SJ2 Board Connection to Accelerometer
 +
The accelerometer provided an alternative input method by allowing the player to control the paddle through tilting. It was connected to the SJ2 board via the I2C interface, which facilitated real-time reading of the accelerometer's X-axis data. This data was processed to move the paddle proportionally to the tilt.
 +
 
 +
Key features of the accelerometer setup:
 +
 
 +
*I2C Communication:
 +
**Enabled efficient data transfer between the accelerometer and the SJ2 board.
 +
*Real-Time Input:
 +
**X-axis readings were mapped to paddle movement for smooth control.
 +
*Calibration:
 +
**Initial calibration ensured accurate and stable readings, compensating for noise or offsets.
  
 
=== Hardware Interface ===
 
=== Hardware Interface ===
In this section, you can describe how your hardware communicates, such as which BUSes used. You can discuss your driver implementation here, such that the '''Software Design''' section is isolated to talk about high level workings rather than inner working of your project.
+
The 64x64 LED matrix acts as the primary display for the game, rendering all visual elements such as the paddle, ball, and bricks. The SJ2 board interfaces with the matrix using GPIO pins, which are configured to drive the rows, columns, and colors.
 +
 
 +
*GPIO Configuration:
 +
 
 +
**The matrix requires GPIO pins for address selection (A, B, C, D, E), color data (R2, G2, B2), and control signals (OE, CLK, LAT).
 +
**The pins are switched at high frequencies to refresh the display and maintain smooth animations.
 +
*Driving the Display:
 +
 
 +
**Row Selection: Address pins are used to select a specific row on the matrix for updating pixel data.
 +
**Pixel Data Transfer: Color pins determine the intensity of red, green, and blue for each pixel, enabling up to 8 colors.
 +
**Control Signals:
 +
***OE (Output Enable): Controls when the LEDs are active.
 +
***CLK (Clock): Synchronizes data transfer to the display.
 +
***LAT (Latch): Locks the updated data into the matrix for display.
  
 
=== Software Design ===
 
=== Software Design ===
Show your software design. For example, if you are designing an MP3 Player, show the tasks that you are using, and what they are doing at a high level. Do not show the details of the code. For example, do not show exact code, but you may show psuedocode and fragments of code. Keep in mind that you are showing DESIGN of your software, not the inner workings of it.
+
The Brick Breaker game was implemented using a state machine design, which allowed for modular and organized control over the game's logic. The main function driving the game, game_state_machine__run_game(), transitions through different states based on user interactions and game events. These states, such as INIT, GAME, PAUSE, and GAME_OVER, provide a clear structure to the game flow. A state machine design was chosen for its ability to simplify complex logic by dividing it into smaller, well-defined states, making the code easier to understand and maintain. Transitions between states occur based on specific triggers, such as a button press to pause the game or the ball falling out of bounds. By using a state machine, redundant operations were minimized, particularly in the GAME state, where initialization functions were executed only once during transitions to ensure optimal performance.
 +
 
 +
The LED matrix was updated dynamically within the GAME state to display the paddle, ball, and bricks. Instead of rewriting the entire display buffer, only the portions of the matrix affected by the game elements were updated. This approach ensured smoother animations and reduced computational overhead. For instance, paddle movement involved modifying only the row where the paddle resided, while ball updates modified the ball's current and next positions. This optimization kept the gameplay visually responsive without overloading the system's resources. The main gameplay loop also called specialized functions to handle game elements like the paddle, ball, and brick states, ensuring modularity and scalability for future enhancements.
 +
 
 +
The ball movement and collision logic, handled within game_state_machine__update_ball_position, formed the core of the game’s mechanics. The ball's trajectory was calculated based on its current velocity and direction. Collisions with walls, the paddle, and bricks were detected and resolved in real time. Wall collisions caused the ball to bounce, while paddle collisions adjusted its angle of reflection based on the contact point, giving players greater control. Brick collisions were handled by checking the corresponding coordinates in the matrix buffer. If a collision occurred, the brick's state was updated, and the ball's direction was adjusted. Special cases, such as corner collisions with multiple bricks, were also addressed by updating all affected bricks and recalculating the ball’s trajectory.
 +
 
 +
To enhance gameplay, bricks were designed with varying levels of durability represented by their color. Each brick color indicated its "life," with red bricks having one life and higher durability bricks represented by colors like green, blue, and purple. When a brick was hit, its life was decremented, and the LED matrix was updated to reflect the new state. Scoring was integrated into this system, with players earning points for each hit or destroyed brick. To add a competitive aspect, a high-score table was maintained, tracking the top five scores. Although the high scores were stored in a sorted array for this implementation, future plans include saving them to the SJ2 board’s flash memory to ensure persistence across power cycles.
  
=== Implementation ===
+
Player input was managed through buttons and an accelerometer, providing intuitive and responsive control. The buttons allowed precise left or right movement of the paddle, while the accelerometer offered an alternative control method, enabling the paddle to move based on the tilt of the SJ2 board. This dual-input system enhanced player engagement and added variety to the gameplay experience. Additionally, dynamic game elements, such as increasing ball speed and additional levels of difficulty, kept the game challenging as players progressed. The game state transitions were smooth and well-optimized, with the state machine ensuring clear separation of responsibilities and efficient execution of tasks.
This section includes implementation, but again, not the details, just the high level.  For example, you can list the steps it takes to communicate over a sensor, or the steps needed to write a page of memory onto SPI Flash. You can include sub-sections for each of your component implementation.
 
  
 
== Testing & Technical Challenges ==
 
== Testing & Technical Challenges ==
Describe the challenges of your project. What advise would you give yourself or someone else if your project can be started from scratch again?
+
A significant technical challenge during the implementation of the Brick Breaker game was ensuring accurate collision detection, especially when the ball interacted with multiple bricks or hit a corner. The issue arose when the ball's next position overlapped with a corner shared by two or more blocks. This led to inconsistencies in how collisions were detected and resolved. For instance, if two bricks were stacked vertically or placed side by side, the ball's direction could behave unpredictably, leading to unexpected bounces. Initially, our collision logic treated corners as part of either a row or a column based on the ball's direction of travel. While this approach worked in simple cases, it failed when multiple blocks were involved.
Make a smooth transition to testing section and described what it took to test your project.
+
 
 +
To address this, we standardized the handling of corners by treating all corners as part of a column rather than a row. This change provided a consistent logic flow for detecting and handling collisions, ensuring the ball's trajectory behaved predictably even in complex scenarios. Testing this approach revealed that treating corners as rows led to erratic bounces and breaks in the gameplay flow, while treating them as part of columns yielded a smoother and more intuitive experience. This adjustment significantly improved the game's performance and user experience, resolving an issue that had initially seemed insurmountable.
 +
 
 +
== Conclusion ==
 +
The Brick Breaker game project served as an excellent opportunity to apply and expand our knowledge of embedded systems, specifically in hardware-software integration and real-time application development. Throughout the project, we faced several challenges, including collision detection issues with block corners and the difficulties associated with debugging real-time game logic. By addressing these problems with a combination of structured design and innovative solutions, such as standardizing collision handling and leveraging modular code, we were able to deliver a functional and engaging game.
  
Include sub-sections that list out a problem and solution, such as:
+
Testing was both a critical and challenging aspect of the project. While print statement debugging helped identify and resolve issues, it became evident that this method is not scalable for larger or more complex projects. This realization underscored the importance of implementing robust unit testing frameworks in future endeavors to streamline the debugging process and improve code reliability.
  
=== <Bug/issue name> ===
+
Through this project, we not only deepened our understanding of the SJ2 board and its peripherals but also gained valuable insights into designing interactive systems with real-time constraints. We learned the significance of modular programming, effective problem-solving, and the practical challenges of integrating multiple hardware components. Moreover, this project reinforced the importance of designing for scalability and maintainability, skills that are critical for larger and more complex systems.
Discuss the issue and resolution.
 
  
== Conclusion ==
+
Ultimately, this project has enhanced our ability to think critically about embedded system design and implementation, providing a strong foundation for tackling similar challenges in future endeavors. The lessons learned and skills developed here will undoubtedly inform and improve our approach to future projects, whether they involve gaming systems, embedded controls, or other interactive applications.
Conclude your project here. You can recap your testing and problems.  You should address the "so what" part here to indicate what you ultimately learnt from this project. How has this project increased your knowledge?
 
  
 
=== Project Video ===
 
=== Project Video ===
Line 277: Line 343:
 
== References ==
 
== References ==
 
=== Acknowledgement ===
 
=== Acknowledgement ===
Any acknowledgement that you may wish to provide can be included here.
+
We'd like to thank Preet for giving us the opportunity to develop and learn through an interactive team project. We would also like to acknowledge the TA Shailja Jain for their feedback throughout the semester.
 
 
=== References Used ===
 
List any references used in project.
 
 
 
=== Appendix ===
 
You can list the references you used.
 

Latest revision as of 01:32, 20 December 2024

Grading Criteria

  • How well is Software & Hardware Design described?
  • How well can this report be used to reproduce this project?
  • Code Quality
  • Overall Report Quality:
    • Software Block Diagrams
    • Hardware Block Diagrams
      Schematic Quality
    • Quality of technical challenges and solutions adopted.

Brick Breaker

Abstract

Brick Breaker is a classic arcade-style game focused on quick reflexes and survival, reminiscent of retro gaming experiences. This project aims to recreate the intense and fast-paced gameplay using the SJ-2 board and an LED matrix display. In this game, players control a character (or an object) that must dodge incoming obstacles from multiple directions, with the speed and frequency of obstacles increasing over time. The objective is to survive as long as possible, setting high scores based on survival time. Players will use buttons or a joystick to maneuver, with core implementation focusing on responsive controls, real-time collision detection, and adaptive difficulty for sustained challenge.

Objectives & Introduction

The project is split into a few sub-topics that could be worked by individuals:

  • Main "clock" task for updating the LED matrix.
  • Main gameplay loop task that would update the position of the ball, paddle, and blocks.
  • Design of various game screens via a state machine.
  • Game logic to determine the ball's bounce relative to its current trajectory.
  • MP3 background track during gameplay.

Team Members & Responsibilities

  • Manoj
    • Game logic
    • Block generator
  • Kevin
    • Hardware connections
    • Game page switching logic
    • Game page switching logic
    • Led testing , button testing
  • Harbans
    • Accerometer and paddle
    • Block generator

Schedule

Week# Start Date End Date Task Status
1
  • 10/20/2024
  • 10/27/2024
  • 10/20/2024
  • 10/27/2024
  • Read previous projects, gather information and discuss among the group members.
  • Create GitLab repository for project - TO_BE_UPDATED
  • Completed
  • Completed
2
  • 10/27/2024
  • 11/02/2024
  • Order necessary parts - LED Matrix , Speaker , Accelerometer
  • Completed
3
  • 11/02/2024
  • 11/08/2024
  • Read and familiarize with LED Matrix Datasheet
  • Completed


4
  • 11/02/2024
  • 11/08/2024
  • Develop graphics driver for LED matrix and implement initial game objects
  • Completed
5
  • 11/09/2024
  • 11/09/2024
  • 11/09/2024
  • 11/09/2024
  • 11/10/2024
  • 11/15/2024
  • 11/15/2024
  • 11/15/2024
  • Finalize wiki schedule
  • Order circuit components
  • circuit and component assembly
  • Circuit testing
  • Additional accessories if required and finalization of hardware
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
6
  • 11/16/2024
  • 11/22/2024
  • Integration of circuit and microcontroller
  • Game logic development
  • Testing and debugging the game logic
  • Completed
  • Completed
  • Completed
7
  • 11/23/2024
  • 11/29/2024
  • Integrate game logic code with LED matrix
  • Completed
8
  • 11/30/2024
  • 12/06/2024
  • Integrate subsystem
  • Finalizing the video game
  • Completed
  • Completed
  • Completed
9
  • 12/07/2024
  • 12/13/2024
  • Work on game page switching
  • Update the wiki page.
  • Completed
  • Completed
10
  • 12/16/2024
  • 12/14/2024
  • 12/14/2024
  • 12/14/2024
  • 12/16/2024
  • 12/16/2024
  • 12/16/2024
  • 12/16/2024
  • Final Demo
  • Update Gitlab repo with final code.
  • Update test video.
  • Update the wiki page.
  • Completed
  • Completed
  • In progress
  • In progress


Parts List & Cost

Part Link Price Quantity
LED Matrix Sparkfun $80 1
Power Supply Adafruit $30 1
Button Adafruit $2.50 1
Quick connect wires Adafruit $4.95 1
MP3 Module Adafruit $34.95 1
Speakers Adafruit $7.50 1
SJ2 Development Board Amazon $50 1

Design & Implementation

For the design and implementation of the Brick Breaker game, the SJ2 board serves as the central controller to interface with a 64x64 LED matrix, control buttons, and an accelerometer. Each of these components plays a crucial role in the functionality of the game, enabling dynamic gameplay and interactivity.

Hardware Design

In the hardware setup for this project, we outlined a few high-level connections that were necessary to implement the Brick Breaker game. These connections can be visualized in the top-level schematic shown below. The schematic was designed using draw.io.

Hardware Interconnects:

  • SJ2 Connection to the LED Matrix
  • SJ2 Connection to Buttons
  • SJ2 Connection to Accelerometer

SJ2 Board Connection to LED Matrix: The SJ2 board controlled the 64x64 LED matrix, which served as the primary visual interface for the game. The matrix required a range of GPIO pins for row, column, and color control. It did not use standard communication protocols like I2C or UART but relied on GPIO switching to drive the display. The key pin connections are as follows:

Address Pins Color Pins Control Pins
A, B, C, D, E R2, G2, B2 OE, CLK, LAT
  • Address Pins: Allowed selection of specific rows for updating pixel data (rows 0 - 31).
  • Color Pins: Enabled 8 distinct colors through combinations of red, green, and blue.
  • Control Pins:
    • OE (Output Enable): Controlled when the display was active.
    • CLK (Clock): Synchronized data transfer for row and pixel updates.
    • LAT (Latch): Locked data into the display.

SJ2 Board Connection to Buttons The buttons served as an input interface for the player to control the paddle in the game. The buttons were connected to GPIO pins on the SJ2 board and configured to trigger on falling-edge interrupts. Each button press caused an interrupt, which was handled by a software ISR to update the paddle’s position on the LED matrix.

Key features of the button setup:

  • Interrupt-Driven Design:
    • Each button press generated an interrupt to ensure immediate responsiveness.
  • Debouncing:
    • Software debouncing eliminated the effects of mechanical noise in button presses.

SJ2 Board Connection to Accelerometer The accelerometer provided an alternative input method by allowing the player to control the paddle through tilting. It was connected to the SJ2 board via the I2C interface, which facilitated real-time reading of the accelerometer's X-axis data. This data was processed to move the paddle proportionally to the tilt.

Key features of the accelerometer setup:

  • I2C Communication:
    • Enabled efficient data transfer between the accelerometer and the SJ2 board.
  • Real-Time Input:
    • X-axis readings were mapped to paddle movement for smooth control.
  • Calibration:
    • Initial calibration ensured accurate and stable readings, compensating for noise or offsets.

Hardware Interface

The 64x64 LED matrix acts as the primary display for the game, rendering all visual elements such as the paddle, ball, and bricks. The SJ2 board interfaces with the matrix using GPIO pins, which are configured to drive the rows, columns, and colors.

  • GPIO Configuration:
    • The matrix requires GPIO pins for address selection (A, B, C, D, E), color data (R2, G2, B2), and control signals (OE, CLK, LAT).
    • The pins are switched at high frequencies to refresh the display and maintain smooth animations.
  • Driving the Display:
    • Row Selection: Address pins are used to select a specific row on the matrix for updating pixel data.
    • Pixel Data Transfer: Color pins determine the intensity of red, green, and blue for each pixel, enabling up to 8 colors.
    • Control Signals:
      • OE (Output Enable): Controls when the LEDs are active.
      • CLK (Clock): Synchronizes data transfer to the display.
      • LAT (Latch): Locks the updated data into the matrix for display.

Software Design

The Brick Breaker game was implemented using a state machine design, which allowed for modular and organized control over the game's logic. The main function driving the game, game_state_machine__run_game(), transitions through different states based on user interactions and game events. These states, such as INIT, GAME, PAUSE, and GAME_OVER, provide a clear structure to the game flow. A state machine design was chosen for its ability to simplify complex logic by dividing it into smaller, well-defined states, making the code easier to understand and maintain. Transitions between states occur based on specific triggers, such as a button press to pause the game or the ball falling out of bounds. By using a state machine, redundant operations were minimized, particularly in the GAME state, where initialization functions were executed only once during transitions to ensure optimal performance.

The LED matrix was updated dynamically within the GAME state to display the paddle, ball, and bricks. Instead of rewriting the entire display buffer, only the portions of the matrix affected by the game elements were updated. This approach ensured smoother animations and reduced computational overhead. For instance, paddle movement involved modifying only the row where the paddle resided, while ball updates modified the ball's current and next positions. This optimization kept the gameplay visually responsive without overloading the system's resources. The main gameplay loop also called specialized functions to handle game elements like the paddle, ball, and brick states, ensuring modularity and scalability for future enhancements.

The ball movement and collision logic, handled within game_state_machine__update_ball_position, formed the core of the game’s mechanics. The ball's trajectory was calculated based on its current velocity and direction. Collisions with walls, the paddle, and bricks were detected and resolved in real time. Wall collisions caused the ball to bounce, while paddle collisions adjusted its angle of reflection based on the contact point, giving players greater control. Brick collisions were handled by checking the corresponding coordinates in the matrix buffer. If a collision occurred, the brick's state was updated, and the ball's direction was adjusted. Special cases, such as corner collisions with multiple bricks, were also addressed by updating all affected bricks and recalculating the ball’s trajectory.

To enhance gameplay, bricks were designed with varying levels of durability represented by their color. Each brick color indicated its "life," with red bricks having one life and higher durability bricks represented by colors like green, blue, and purple. When a brick was hit, its life was decremented, and the LED matrix was updated to reflect the new state. Scoring was integrated into this system, with players earning points for each hit or destroyed brick. To add a competitive aspect, a high-score table was maintained, tracking the top five scores. Although the high scores were stored in a sorted array for this implementation, future plans include saving them to the SJ2 board’s flash memory to ensure persistence across power cycles.

Player input was managed through buttons and an accelerometer, providing intuitive and responsive control. The buttons allowed precise left or right movement of the paddle, while the accelerometer offered an alternative control method, enabling the paddle to move based on the tilt of the SJ2 board. This dual-input system enhanced player engagement and added variety to the gameplay experience. Additionally, dynamic game elements, such as increasing ball speed and additional levels of difficulty, kept the game challenging as players progressed. The game state transitions were smooth and well-optimized, with the state machine ensuring clear separation of responsibilities and efficient execution of tasks.

Testing & Technical Challenges

A significant technical challenge during the implementation of the Brick Breaker game was ensuring accurate collision detection, especially when the ball interacted with multiple bricks or hit a corner. The issue arose when the ball's next position overlapped with a corner shared by two or more blocks. This led to inconsistencies in how collisions were detected and resolved. For instance, if two bricks were stacked vertically or placed side by side, the ball's direction could behave unpredictably, leading to unexpected bounces. Initially, our collision logic treated corners as part of either a row or a column based on the ball's direction of travel. While this approach worked in simple cases, it failed when multiple blocks were involved.

To address this, we standardized the handling of corners by treating all corners as part of a column rather than a row. This change provided a consistent logic flow for detecting and handling collisions, ensuring the ball's trajectory behaved predictably even in complex scenarios. Testing this approach revealed that treating corners as rows led to erratic bounces and breaks in the gameplay flow, while treating them as part of columns yielded a smoother and more intuitive experience. This adjustment significantly improved the game's performance and user experience, resolving an issue that had initially seemed insurmountable.

Conclusion

The Brick Breaker game project served as an excellent opportunity to apply and expand our knowledge of embedded systems, specifically in hardware-software integration and real-time application development. Throughout the project, we faced several challenges, including collision detection issues with block corners and the difficulties associated with debugging real-time game logic. By addressing these problems with a combination of structured design and innovative solutions, such as standardizing collision handling and leveraging modular code, we were able to deliver a functional and engaging game.

Testing was both a critical and challenging aspect of the project. While print statement debugging helped identify and resolve issues, it became evident that this method is not scalable for larger or more complex projects. This realization underscored the importance of implementing robust unit testing frameworks in future endeavors to streamline the debugging process and improve code reliability.

Through this project, we not only deepened our understanding of the SJ2 board and its peripherals but also gained valuable insights into designing interactive systems with real-time constraints. We learned the significance of modular programming, effective problem-solving, and the practical challenges of integrating multiple hardware components. Moreover, this project reinforced the importance of designing for scalability and maintainability, skills that are critical for larger and more complex systems.

Ultimately, this project has enhanced our ability to think critically about embedded system design and implementation, providing a strong foundation for tackling similar challenges in future endeavors. The lessons learned and skills developed here will undoubtedly inform and improve our approach to future projects, whether they involve gaming systems, embedded controls, or other interactive applications.

Project Video

Upload a video of your project and post the link here.

Project Source Code

References

Acknowledgement

We'd like to thank Preet for giving us the opportunity to develop and learn through an interactive team project. We would also like to acknowledge the TA Shailja Jain for their feedback throughout the semester.