This was a challenging assignment! Mainly because I had a game I wanted to do in mind, and after spending a lot of time on getting the basics to work I realized it might need more practice and time than what I have now, so I changed the direction of my assignment and decided to go with a more “artwork” focused approach.
Recently I’ve been thinking about the ‘paint by numbers notebooks’ I used to have as a child and how accessible they made creating artwork that is above my artistic skills. This made me want to buy a ‘paint by numbers’ set, but also I asked if I could use the same approach to create artwork on processing.

I realized I can do a similar approach but with pixels! I made a class called “square” which represents each square in a grid, and then used a 2D array list that contains different combinations of numbers of squares to be filled.
My class has a display function, and a fill function that relies on a boolean variable.
For the drawing, I decided to go for a heart that is filling gradually with every number the user inputs from their keyboard. At the final level, when the heart is full, it goes from black to a mix of random shades of red and pink which is controlled by the mouseX movement to a certain extent (to avoid the colors being too random).

After finalizing the logic, which depends on creating an array of square objects and then displaying and referencing them as a grid. I went to figure out the square numbers needed in my 2D array for each heart stage. For this part, I used pixilart.com and created a grid of the same size as mine then compared the square values.


I settled on bigger pixels, but since the code contains, row, column, and square number variables, it is easy to change the grid appearance and square size.
I think the program has the potential to be more complex or contain more user interaction, I would love any suggestions!
Have a look at my main code:
Square[] allSquares; //declaring an array of objects that consists of the squares that will be in the grid
// 2D Array of the different heart level drawings, each inner list contains the number of the pixel that should be filled
int[][] heartLevels = { {2,3,4,11,15,21,26,32,37,43,48,53,58,62,67,71,76,81,85,92,93,94}, //empty heart
                   {2,3,4,11,15,21,26,32,37,43,47,48,53,57,58,62,67,71,76,81,85,92,93,94}, //initially filled heart (Level 1)
                   {2,3,4,11,15,21,25,26,32,35,36,37,43,45,46,47,48,53,55,56,57,58,62,65,66,67,71,75,76,81,85,92,93,94}, // medium full heart (Level 2) 
                   {2,3,4,11,13,14,15,21,23,24,25,26,32,33,34,35,36,37,43,44,45,46,47,48,53,54,55,56,57,58,62,63,64,65,66,67,71,73,74,75,76,81,83,84,85,92,93,94}, 
// almost full heart (Level 3)
                   {2,3,4,11,12,13,14,15,21,22,23,24,25,26,32,33,34,35,36,37,43,44,45,46,47,48,53,54,55,56,57,58,62,63,64,65,66,67,71,72,73,74,75,76,81,82,83,84,85,92,93,94}}; // full heart
                   
// a boolean to create the color effect for the full heart
boolean fullHeart; 
                   
 // declaring and initializing the column and row number for the grid                  
int cols = 10; 
int rows = 10;
void setup(){
 size(500,500);
 background(255);
 //creating the grid array, the array size corresponds to the dimension of the grid
 allSquares = new Square[cols*rows]; 
 //initializing and declaring an index variable to create different objects every time the  nested loop runs
 int index = 0; 
 //a nested loop that goes through each column and row to create a grid of objects
 for (int c = 0; c < cols; c++){
  for (int r = 0; r < rows; r++){
    // creating the objects, the arguments of the class are X location, Y location, Width, Height, and index
//we multiply c and r by 50 because it corresponds to the square dimension, so if my x is 2 and y is 2, the square will be at 100,100.
   allSquares[index] = new Square(c*50,r*50,50,50,index); 
   index+=1;
   
  }
 }
 fullHeart = false; //initializing the color effect variable with false
}
void draw(){
  //a loop that displays the grid
  for(int i = 0; i < cols*rows; i++){
   allSquares[i].display();
   
   //an if statement that checks which heart we are creating by checking if the fullHeart bool is true, it is changed to true below, when the key is pressed.
   if (fullHeart == true){
   for (int j = 0; j < heartLevels[4].length; j++){
     //accessing the heart color variable defined in the square class, and altering it for the color effect
//using random within a small range to generate the different shades of red, and applying interaction through mouseX
    allSquares[heartLevels[4][j]].heartColor = color(random(mouseX,255), random(0,90),random(90,100));  //interacting with user through changing the R random bottom range using mouseX. 
 
                                             
                                             
   //filling the squares, it keeps changing because it is in the draw function
    allSquares[heartLevels[4][j]].fillingSquare(true); 
   
  }
   }
  }
}
  
  
  
  void keyPressed(){
  //key pressed function that displays different heart shapes based on the number pressed  
    
  if (key == '0'){ //zero gives an empty screen
      for(int i = 0; i < cols*rows; i++){
        fullHeart = false; //this boolean is set to false for every key that does not correspond to a full heart
   allSquares[i].fillingSquare(false); //First loop goes through all the squares in the object list and unfills them to remove the effect of other shapes, this loop is repeated for all the other keys
   
      }
  }
  if (key == '1'){
      for(int i = 0; i < cols*rows; i++){ //same loop
   allSquares[i].fillingSquare(false); 
  }
      for (int i = 0; i < heartLevels[0].length; i++){ 
        fullHeart = false;
       allSquares[heartLevels[0][i]].heartColor = color(0); //sets the color variable from the class to black
       allSquares[heartLevels[0][i]].fillingSquare(true); ////fills every pixel that corresponds to values in the first heart shape list with black
       
  }
  }
  
  //the same repeats for the next if conditions
  else if (key == '2'){
    for(int i = 0; i < cols*rows; i++){
   allSquares[i].fillingSquare(false);
  }
    for (int i = 0; i < heartLevels[1].length; i++){
      fullHeart = false;
     allSquares[heartLevels[1][i]].heartColor = color(0);
     allSquares[heartLevels[1][i]].fillingSquare(true); 
  }
 
  
 
  }
  else if(key=='3'){
   for(int i = 0; i < cols*rows; i++){
   allSquares[i].fillingSquare(false); 
  }
    for (int i = 0; i < heartLevels[2].length; i++){
      fullHeart = false;
     allSquares[heartLevels[2][i]].heartColor = color(0);
     allSquares[heartLevels[2][i]].fillingSquare(true);
   
  }
  }
  else if(key=='4'){
     for(int i = 0; i < cols*rows; i++){
   allSquares[i].fillingSquare(false);
  }
     for (int i = 0; i < heartLevels[3].length; i++){
       fullHeart = false;
     allSquares[heartLevels[3][i]].heartColor = color(0); 
     allSquares[heartLevels[3][i]].fillingSquare(true);
   
  }
  }
  else if(key=='5'){
    for(int i = 0; i < cols*rows; i++){
  allSquares[i].fillingSquare(false);
  }
    for (int i = 0; i < heartLevels[4].length; i++){
     fullHeart = true; //this turns on the random color fill in the draw function
    allSquares[heartLevels[4][i]].fillingSquare(true); //and based on those colors fills the squares
    
    
    }
  }
  }
and my Square Class:
class Square{
//initializing variables that will be used throughout the class  
int squareX , squareY;  //square x and y location
int squareW , squareH; 
int squareNo; //the number of the pixel on the grid
boolean filled; //the boolean that determines which pixels on the screen are filled
color heartColor; //a color variable to control the color of the pixel
color gridStroke = color(203,197,197); // color of grid lines
Square(int tempX, int tempY, int tempW, int tempH, int tempSquareNoMinus1){ //constructor, takes X location, Y location, Width, Height, and the index as arguments
 //setting the constructor arguments to permanent variables
 squareX = tempX;
 squareY = tempY;
 squareW = tempW;
 squareH = tempH;
 squareNo = tempSquareNoMinus1 + 1; // 1 is added to the index as the counting of squares starts at 1, not zero. 
 
}
void display(){
 //function that displays the square on the screen
 stroke(gridStroke);
 strokeWeight(0.25);
 noFill();
 rect(squareX , squareY , squareW , squareH);
 
 
}
void fillingSquare(boolean filled){
  //a function that fills the pixels based on a filled? boolean
  if (filled == true){
    
   fill(heartColor); 
  }
  else{
  //if the boolean is false the square is filled with white and redrawn  
   fill(255); 
  }
  rect(squareX,squareY,squareW,squareH);
  }
}