Week 3: Using Classes

In my Computer Systems Organization class last year, one of our homework assignments was to code Conway’s Game of Life in C. The Game of Life is a cellular automaton game developed by a British mathematician named Conway. 

 

The rules of the game (according to Wikipedia):
1. Any live cell with two or three live neighbours survives.

2. Any dead cell with three live neighbours becomes a live cell.

3.  All other live cells die in the next generation. Similarly, all other dead cells stay dead.

The version we did in CSO was pretty lame simply using 1s and 0s printed, so I thought I could make a graphic version for my practice this week. I also lost access to the code I wrote, so I wanted to see if I could do it again and track whether it felt easier to build a year later as an indicator of my progression. The only thing I remembered from that assignment was creating classes for the Cell and Game.

My code was pretty straightforward. In general, I created the Cell and Game classes. I first began with coding the Cell class. Originally, I only had an attribute for the current state. However, when it came time to visually display it, I realized I needed a way to keep track of where to draw it. I ended up adding the x and y positions and size with each cell. The display of the tile occurred at the cell level with each cell being represented by a rectangle with its fill determined by whether it was currently alive or dead. 

I first created the grid through a nested for loop setting up a cell at each row and column intersection. Then, for each generation of the cells, I looped through each cell and then counted how many of its neighbors were alive. Based on the rules of the game, I used its current state and number of live neighbors to determine its state in the next round. After each generation of the cells, I redrew the board.

The main struggles I had:

  1. Creating the 2D array. I kept getting an undefined length for the array. The problem? I didn’t actually define the dimension variable. Oops.
  2. Handling the edges of the grid. I ended up just checking to make sure the neighbor was within the bounds of the array, but I’m sure there’s a more efficient way to do this.
  3. Getting the right neighbor count. I realized I was double counting the cell itself if it was alive so ended up just subtracting it from the neighbor count.

I wanted to add some level of interaction and user input so ended up just making simple buttons with dimension size choices. I originally wanted to have a slider or a text input box so the user could have more choice in determining the size of the board, but these seemed quite laborious to implement in Processing. When clicking the button, a degree of user feedback is provided in the board changing sizes and the rectangle lighting up.

Here’s my sketch and code!

 

//Game of Life
//Any live cell with two or three live neighbours survives.
//Any dead cell with three live neighbours becomes a live cell.
//All other live cells die in the next generation. Similarly, all other dead cells stay dead.
//rules from https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

//the decision to create a Cell and Game class was based on the instructions given in my Computer Systems Organization course last year when we had to do this as a mini homework assignment in C
//in that lab we simply used 1s and 0s so I wanted to graphically represent it. I also lost access to the code I wrote for that course so wanted to refresh my memory by retrying creating the game of life

Game game;
//array of grid dimension choices for the user
int[] sizeChoice = new int[]{ 10, 25, 50, 100 };

//each cell has attributes of what its current state is
//to graphically display the cell, i also include the x, y, and size in the class
class Cell {
  boolean currAlive;
  int x;
  int y;
  int size;
  
  Cell(int x_pos, int y_pos, int size_) {
    //randomly decides if its alive or not by picking 0 or 1 randomly and casting that as a boolean
    currAlive = boolean(int(random(2)));
    x = x_pos;
    y = y_pos;
    size = size_;
  }
  
  void drawCell() {
    //if cell is alive, color it white
    if(currAlive) {
      fill(240);
      //if the cell is dead, make it black
    } else {
      fill(0);
    }
    //draw a tile to represent each cell
    rect(x,y, size, size);
}
}

class Game {
  Cell[][] grid;
  int dimension;
  int size;
  
  Game(int dimension_) {
    //create a grid given what is passed to the constructor
    dimension = dimension_;
    grid = new Cell[dimension][dimension];
    createCells();
  }
    
    void createCells() {
      //make the size of tile responsive to the dimension
      size = width/dimension;
      //loop through each row and column to create a cell and draw the tile representing it
      for (int r = 0; r < dimension; r++) {
        for (int c = 0; c < dimension; c++) {
          grid[r][c] = new Cell(r*size, c *size, size);
          grid[r][c].drawCell();
        }
      }
    }
    
    void generation() {
      //loop through each row and column in the grid to count neighbors
      for (int r = 0; r < dimension; r++) {
        for (int c = 0; c < dimension; c++) {
          //restart neighbor count for each cell
          int n_count = 0;
          //I kept getting the wrong cell count and realized that I forgot to exclude the cell itself
          n_count = n_count - int(grid[r][c].currAlive);
          //looping over its neighbors
          for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {
              int x = r + i;
              int y = c + j;
              //found it difficult to check for the edge cells
              //ended up just checking to make sure the counting was within the array
              if(0 <= x && x < dimension && 0 <= y && y < dimension) {
                if(grid[x][y].currAlive) {
                  n_count += 1;
                }
              }
              }
            }
            //for the rule if the cell has less than 2 or more than 3 neighbors, it dies in the next generation
            if (grid[r][c].currAlive && (n_count < 2 || n_count > 3)) {
                grid[r][c].currAlive = false;
            } 
            //if the cell is dead but has 3 live neighbors, it becomes alive in the next generation
            if (!grid[r][c].currAlive && n_count == 3) {
              grid[r][c].currAlive = true;
            }  
        }
      }
    }
    
    //redraw the grid after each generation
    void drawGrid() {
      for (int r = 0; r < dimension; r++) {
        for (int c = 0; c < dimension; c++) {
          grid[r][c].drawCell();
        }
      }
    }
}

void setup() {
  size(500,600);
  //default grid size is 25 x 25
  game = new Game(25);
}

void draw() {
  background(0);
  //creates buttons to change grid dimensions
  for (int n=0; n < sizeChoice.length; n++) {
    fill(123);
    int x_pos = width/8 * n + 20;
    rect(x_pos, height - 50, 30, 20);
    fill(255);
    text(sizeChoice[n], x_pos + 8, height-35);
  }
    text("Conway's Game of Life", width/3 * 2 - 20, height - 45);
    text("Change grid size with button", width/3 * 2 - 20, height - 25);
    //each frame is a new generation
    //redraw grid after every generation
    game.generation();
    game.drawGrid();
}

void mousePressed() {
  //checks if button is clicked
  if(mouseX < 50 && mouseX >=  20 && mouseY >= height - 50 && mouseY < height - 30) {
    pressButton(0);
  }
  if(mouseX < width/8 + 50 && mouseX >= width/8 + 20 && mouseY >= height - 50 && mouseY < height - 30) {
    pressButton(1);
  }
  if(mouseX < width/8 * 2 + 50 && mouseX >= width/8 * 2 + 20 && mouseY >= height - 50 && mouseY < height - 30) {
    pressButton(2);
  }
  if(mouseX < width/8 * 3 + 50 && mouseX >= width/8 * 3 + 20 && mouseY >= height - 50 && mouseY < height - 30) {
    pressButton(3);
  }
}

void pressButton(int n) {
    //changes fill if button is clicked and recreates the game with the new dimension
    fill(200);
    rect( width/8 * n + 20, height - 50, 30, 20);
    fill(255);
    text(sizeChoice[n],  width/8 * n + 20 + 8, height-35);
    game = new Game(sizeChoice[n]);
}

 

Week 2: Using For Loops

This week, I’ve been feeling a lot of chaotic energy with balancing my professional commitments, class deadlines, friends, capstone, and job interviews. Thus, I wanted my sketch to reflect the seemingly segmented aspects of my life going a bit berserk.

Processing Sketch of colored squares within squares tiled across sketch

Because the squares ended up moving so quickly and brightly, I wanted to make the default option be a paused version of a sketch with the option to play in case the quick movements bothered anyone. Thus, I ended up adding a pause and play button. I had considered just using a space bar to toggle between pause and play, but wanted to use more visual instructions.

Video of Sketch (Warning: Bright Flashing Lights)

My code is pretty straightforward. I created a function that created each “tile.” I didn’t want each tile to be uniform so I randomly changed the color and number of internal squares when drawing each tile. From there, I simply used a double for loop to create the grid of tiles.

I used an array of colors to store my palette which I created using coolors.co (my go to palette inspiration site).

Something I messed up and fixed: originally, I created a String array, having the hex colors in strings. However, this was not compatible with the fill method so after looking at the Processing documentation, I realized that color was a data type in Processing and was able to create an array using the color primitive.

If I were to repeat this sketch creation again, I would try to avoid having so many nested for loops and do the mousePressed function in a less hard-coded way.

Here’s my code!

//color palette
color[] colors = {#8ac4ff, #7e7f9a, #4FB286, #F3DE8A, #Eb9486};
boolean pause = true;

void setup() {
  size(500, 500);
  rectMode(CENTER);
  //play button
  triangle(width/6, height -50, width/6,  height - 20, width/4.5, height-35);
  fill(colors[0]);
  //pause button
  rect(width/10, height - 35, 5, 30);
  rect(width/10 - 10, height - 35, 5, 30);
  //title
  textSize(20);
  fill(0);
  text("Senior Year", width/3 * 2, height - 35);
}

void draw() {
  //looping through width
  for (int i = 40; i < width; i += 60) {
    //looping through height
    for (int j = 40; j < height- 50; j += 60) {
      drawSquare(i, j);
    }
  }
  //sketch starts paused
  if(pause) {
    noLoop();
  }
}

 void drawSquare(int x,int y) {
  int w = 5;
  //changes number of internal squares within a tile
  float count = random(5,9);
  //picks a color randomly from palette
  color c = colors[int(random(colors.length))];
  fill(c);
  rect(x,y, 10 * w, 10 * w);
  for (float i = count; i > 0; i--) {
    fill(c);
    rect(x,y, i * (count/1.5), i* (count/1.5));
  }
}

void mouseClicked() {
  //if pause button is clicked, toggle button colors and stop loop
  if(mouseX < (width/10 + 5) && mouseX >= (width/10 -15) && mouseY > height - 50 && mouseY <= height -20) {
    pause = true;
    fill(255);
    triangle(width/6, height -50, width/6,  height - 20, width/4.5, height-35);
    fill(colors[0]);
    rect(width/10, height - 35, 5, 30);
    rect(width/10 - 10, height - 35, 5, 30);
    noLoop();
    //if play button is clicked, toggle button colors and start loop

  } else if (mouseX > width/6 && mouseX <= width/4.5 && mouseY > height - 50 && mouseY <= height -20) {
    pause = false;
    loop();
    fill(colors[0]);
    triangle(width/6, height -50, width/6,  height - 20, width/4.5, height-35);
    fill(255);
    rect(width/10, height - 35, 5, 30);
    rect(width/10 - 10, height - 35, 5, 30);
  }
}
  
  

 

Week 1: Self-Portrait

In class this week, we played around with the coordinate system and making shapes in Processing.

Processing sketch of three ellipses and arc creating face structure with purple background
Processing sketch from Wednesday’s class.

For our practice this week, I wanted to experiment with pixel art but the prospect of measuring and coloring every little rectangle filled me with dread. I remember watching a Coding Train video a while ago where Dan Shiffman used a .get() method to get the color from an image, so I decided to create the rectangles based on an image.

In terms of the pseudocode:

  1. Load an image (keep in same sketch folder)
  2. Define the canvas size.
  3. Pick a random x and y coordinate from the photo.
  4. Get the color of the photo at that x,y point.
  5. Create a rectangle at that x,y point with the fill of the color.

Here is my sketch:

Some of the design choices I thought about were the size of the rectangles (how pixellated should the portrait be?), the fill of the rectangles (should they be the same as the image or should there be different colors, alphas, brightnesses?), the framerate of the drawing (how fast should the rectangles be created?).

An interesting part of the code to me was the .get() method I learned about in the Coding Train video. This method essentially gets the color of the image at the x,y point. You can see the code below.

PImage me;
//define size of the rectangle
int pixelSize = 20;

void setup() {
  size(600,715);
  me = loadImage("me.jpg");
  //controls speed of drawing pixels
  frameRate(300);
}

void draw() {
    //create random x,y point within the sketch
    float x = random(width);
    float y = random(height);
    //learned about .get() from from Daniel Shiffman's Processing tutorial on pixels https://www.youtube.com/watch?v=NbX3RnlAyGU
    //get color from image at the x,y point
    color c = me.get(int(x), int(y));
    fill(c);
    noStroke();
    //draws rectangle at the x,y point with the color from the image
    rect(x, y, pixelSize, pixelSize);
}

The whole sketch is available here.