(Final Project) Toy Grabber Machine – Jade

VIDEOS / IMAGES

__________________

 

 

PROCESS

__________________

How to play

First, the player should toss the coin into the coin pusher, which will start the game. Then the player uses the joystick to move the claw left or right, and the button to make the claw go down for the toys. I also added sound effects for different moves, so that it’s more interesting.

There is also a hidden prize which only three lucky persons today got it. Some people really wanted to get the prize and tried so hard at it — almost grabbed every toy.

 

Challenges

The coding part on Processing is quite challenging, and I spent quite some time on the física package so that I know how it works. One of the difficulties I encounter was the part where the claw goes down to grab, then drops it and finally returns.  It was such a headache to figure out how to make the claw know if it touches a toy or not. I solved this by setting another física object on the tip of the claw to detect physical collision. I also used many booleans to make this small animation.

The edge setting was also tricky. I have to make sure that the claw doesn’t go out of the screen or works weirdly. To avoid those rule-breaking people who wants to see what happens if the claw goes down in the drop box — I set it that the claw will just head back instead of going down. And if the claw doesn’t catch anything, as it touches the edge, it will automatically go back.

When it came to the Arduino part, the coding was much easier, and the physical setting wasn’t that complicated. The most hard part was the coin pusher, I had to glue the distance to the box really tight to make sure it measures the distance correctly.

But the joystick was really hard to control — the numbers it gave off were pretty random at first, but after I soldered it, I found the patterns of the numbers although it wasn’t really stable. Also, the cables and the distance sensor often fell off, and the port sometimes got disconnected.

Right before the IMA show, the port got disconnected and some wires fell off. Fortunately, during the entire show, everything worked perfectly and didn’t crash, which was amazing and surprising as well!

 

Conclusion

It was such an amazing journey of learning Processing and Arduino, and connecting them through serial communication to produce such a complex project. I could never expect to do something like this at the beginning of this semester.

I want to thank the professor for being supportive and giving great suggestions. Thank you for pointing out the problems with the machine  and helping me fix them before the show, so that they all worked great for the entire time. I feel sorry for not being able to let you play the perfect version 🙁

Thank you all for this fabulous course! Everyone’s projects are so creative and interesting! It was pleasant to work with everyone :))

 

CODE

__________________

Arduino:

int joyX = A0;
int joyY = A1;
int button = 4;
int button_state;
const int trigPin = 7;
const int echoPin = 6;
int duration = 0;
int dist = 0;
int prize=0;

int red = 10;
int yellow = 9;
int blue = 8;

int posX;
int posY;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("0,0,0,0");
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(button, OUTPUT);
  pinMode(trigPin , OUTPUT);
  pinMode(echoPin , INPUT);
  pinMode(red, OUTPUT);
  pinMode(yellow , OUTPUT);
  pinMode(blue , OUTPUT);
  pinMode(2,INPUT);
  digitalWrite(2,HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:
//  int posX = analogRead(joyX); 
//  int posY = analogRead(joyY); 
//  Serial.println(posY);
//  Serial.println(posX);
  
  digitalWrite(red,prize);
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(2);
  digitalWrite(trigPin, LOW); 
   
  duration = pulseIn(echoPin, HIGH);
  dist = duration/58.2;
//  Serial.println(dist);

  while (Serial.available()) {
    prize = Serial.parseInt();
    if (Serial.read() == '\n'){ 
       button_state = digitalRead(button);
       // move 0
       posX = analogRead(joyX); 
       posY = analogRead(joyY); 
       
       if (posX >= 550) {
          Serial.print(1);   
       } else {
          Serial.print(0);
       }
       Serial.print(",");
       
       // move 1
       if (posX <= 360) {
          Serial.print(1);   
       } else {
          Serial.print(0);
       }
       Serial.print(",");   
           
       // grab 2
       if (button_state == 0) {
          Serial.print(0);
       } else {
          Serial.print(1);
       }
       Serial.print(",");
       
       // coin pusher 3

       if ( dist <= 5 )
          {
            Serial.println(1);
          } else {
            Serial.println(0);       
          }
        digitalWrite(red,prize);
        digitalWrite(yellow,prize);
        digitalWrite(blue,prize);
      }

     } 

}

Processing:

import fisica.*;
import processing.serial.*;
import processing.sound.*;

Serial myPort;

PImage claw;
PImage[][] claws;
PImage bird;
PImage[][] birds;
PImage bg;

boolean start = false;
boolean grab = false;
boolean get = false;
boolean drop = false;
boolean ret = false;
boolean prize = false;
boolean coin = false;
boolean right = false;
boolean left = false;

float speed;
float posX=20, posY=10;
FCircle[] bodies;

ArrayList<FBody> touching = new ArrayList<FBody>();
FCircle tip;
FBody prev_touch;

FWorld world;
int toy_num = 30;

SoundFile grab_sound;
SoundFile drop_sound;
SoundFile prize_sound;
SoundFile start_sound;
 
void setup() {
  fullScreen();
  size(750,600);
  
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  
  //
  bg = loadImage("bg.jpeg");
  bg.resize(displayWidth, displayHeight);
  //bg.resize(750, 600);
  
  translate((displayWidth-750)/2, (displayHeight-600)/2+100);
  
  generate_claw();
  Fisica.init(this);
  world = new FWorld();
  world.setEdges(0, 0, 750, 530, 255);
  generate_birds();
  
  tip = new FCircle(35);
  tip.setFill(0);
  tip.setStatic(true);
  tip.setDrawable(false);
  world.add(tip);
  
  grab_sound = new SoundFile(this, "pick_up.wav");
  drop_sound = new SoundFile(this, "drop.wav");
  prize_sound = new SoundFile(this, "prize.wav");
  start_sound = new SoundFile(this, "coin.wav");
}

void draw() {
  //println(mouseX,mouseY);
  background(bg);
  
  translate((displayWidth-750)/2, (displayHeight-600)/2+100);
  world.step();
  world.draw();
  
  // box
  fill(255, 200);
  noStroke();
  rect(603, 250, 200, 280);
  strokeWeight(3);
  stroke(0);
  line(603, 250, 605, 530);
  
  if (coin) {
    start_sound.play();
    coin = false;
  }
  
  if (start){
    initialize_claw();
  } else {
    fill(#A09B10);
    PFont f = createFont("Dialog-48", 40);
    textFont(f);
    text("SWIPE a coin to START", 145, -100);
    fill(#671235);
    text("SWIPE a coin to START", 140, -100);
  }
}

void initialize_claw() {
  if (posX >= 750 - 80) {
    posX = 750-80;
  }
  
  if (posX <= 20) {
    posX = 20;
  }
  
  
  if (!grab) {
    if (ret) {
      if (posX > 20) {
        image(claws[0][0], posX, 10);
        posX -=3;
        if (prize) {
          fill(#901758);
          text("CONGRATULATIONS!!!", 150, 100);
          text("You are so LUCKY to get an AWARD", 80, 100);
        }
      } else {
        ret = false;
        world.remove(prev_touch);
        prize = false;
      }
    } else {
      image(claws[0][0], posX, 10);
      if (right) {
        posX += speed;
      }
      
      if (left) {
        posX -= speed;
      }
    }
  } else {
    if (posX >= 550 && !get) {
      grab = false;
      get = false;
      ret = true;
    } else {
    
    claw_down();
    if (posY<0) {
      posY+=3;
    }
    if (!drop) {
      FBody toy = world.getBody(posX+60, posY+80);
      if (get && posY < 20) {
        image(claws[6][0], posX, posY);
        if (posX <= 598) {
          posX += 3;
          toy.setPosition(posX+60, posY+80);
        } else {
          drop = true;
          drop_sound.play();
          toy.setStatic(false);
          grab = false;
          get = false;
          drop = false;
          ret = true;
          
        // prize
        int ram = (int)random(100);
        if (ram <= 1) {
          prize = true;
          prize_sound.play();
        }
        
         }
        }
      }
    }
  }
    
}

void claw_down() {
  FBody touch;
  tip.setPosition(posX+40, posY+90);
  if (posY < 500 && posY > 0) {
    touching = tip.getTouching();
    if (get) {
      image(claws[6][0], posX, posY);
      posY -= 3;
      if (touching.size()>0) {
        touch = touching.get(0);
        prev_touch = touch;
 
        touch.setStatic(true);
        touch.setPosition(posX+60, posY+80);
      }
    } else{
      image(claws[7][0], posX, posY);
      if (touching.size()>0) {
        get = true;
        grab_sound.play();
      } else {
        posY += 3;
        if (posY >= 500) {
          grab = false;
          get = false;
          ret = true;
        }
      }
    }
  }
}



void generate_claw() {
  claw = loadImage("claw.png");
  claws = new PImage[8][5];
  int w = claw.width / 5; 
  int h = claw.height / 8; 

  for (int y=0; y < 8; y++) {
    for (int x=0; x< 5; x++) {
      claws[y][x] = claw.get(x*w, y*h, w, h);
      claws[y][x].resize(100,120);
    }
  }   
}

void generate_birds() { 
  bodies = new FCircle[30];
  
  bird = loadImage("bird.png");
  birds = new PImage[2][3];
  int w1 = bird.width / 3;
  int h1 = bird.height / 2;
  
  for (int y=0; y < 2; y++) {
    for (int x=0; x< 3; x++) {
      birds[y][x] = bird.get(x*w1, y*h1, w1, h1);
      birds[y][x].resize(85, 85);
    }
  }
  
  for (int i=0; i<toy_num; i++) {
    int radius = 50;
    FCircle b = new FCircle(radius);
    b.setPosition(random(100, 500), height/2);
    b.setStroke(0);
    b.setStrokeWeight(2);
    b.setDensity(radius / 10);
    int random_x = int(random(0,2));
    int random_y = int(random(0,3));
    b.attachImage(birds[random_x][random_y]);
    world.add(b);
    bodies[i] = b;
  }
  
  FBox edge = new FBox(10, 300);
  edge.setPosition(600, 400);
  edge.setStatic(true);
  world.add(edge);
}


void mousePressed() {
  // if button pressed, reset
  world.clear();
  grab = false;
  ret = false;
  get = false;
  drop = false;
  start = false;
  
  bg = loadImage("bg.jpeg");
  bg.resize(displayWidth, displayHeight);
  translate((displayWidth-750)/2, (displayHeight-600)/2+100);
  generate_claw();
  Fisica.init(this);
  world = new FWorld();
  world.setEdges(0, 0, 750, 530, 255);
  generate_birds();
  
  tip = new FCircle(35);
  tip.setFill(0);
  tip.setStatic(true);
  tip.setDrawable(false);
  world.add(tip);
  posX = 20;
  posY = 10;
  
  initialize_claw();
}



void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    println(s);
    int values[]=int(split(s,','));
    if (values.length>=1){
      if (values[3] == 1) {
        coin = true;
        start = true;
      }
      
      if (values[0] == 1 && start) {
        left = false;
        right = true;
      } else {
        right = false;      
      }
      
      if (values[1] == 1 && start) {
        right = false;
        left = true;
      } else {
        left = false;      
      }      
      
      if (values[2] == 1 && start) {
        grab = true;
      }
      
    }
  }
  myPort.write(int(prize)+"\n");
  
}

 

 

Final User Test

 

FEEDBACK

According to my friend’s feedback, I think what I can improve is to make clearer instructions on the coin pusher. Since when I was making the physical setting, the coin pusher was pretty tricky.

I used the distance sensor to check if a coin has approached. But if the coin falls into the gap, the distance sensor would output an outlier, which could then lead to error. So I wrote “SWIPE a coin to START” before the game starts, hoping that people could just swipe — put the coin into the space and hold it for a second.

At first, my friend just tossed the coin into it, and the distance sensor didn’t capture that before the coin fell through it. Since she majors in IMA, she figured it out herself, but not everyone can understand how distance sensor works! So I decided to write specific instructions on the box about how to swipe a coin.

The other parts of the game was straightforward and understandable. As soon as my friend saw the claw/hand, she realized that it is a claw machine, so naturally she knew the way to play around it.

The last part was to show that there is possibility that you are lucky enough to grab a toy with an award. I set the probability to be quite low, so it took a long time before she could get the award. I had to change the code so that she could cheat on it.

 

PROGRESS:

int joyX = A0;
int joyY = A1;
int button = 4;
int button_state;
const int trigPin = 7;
const int echoPin = 6;
int duration = 0;
int dist = 0;
int prize=0;

int red = 10;
int yellow = 9;
int blue = 8;

int posX;
int posY;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("0,0,0,0");
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(button, OUTPUT);
  pinMode(trigPin , OUTPUT);
  pinMode(echoPin , INPUT);
  pinMode(red, OUTPUT);
  pinMode(yellow , OUTPUT);
  pinMode(blue , OUTPUT);
  pinMode(2,INPUT);
  digitalWrite(2,HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:
//  int posX = analogRead(joyX); 
//  int posY = analogRead(joyY); 
//  Serial.println(posY);
//  Serial.println(posX);
  
  digitalWrite(red,prize);
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(2);
  digitalWrite(trigPin, LOW); 
   
  duration = pulseIn(echoPin, HIGH);
  dist = duration/58.2;
//  Serial.println(dist);

  while (Serial.available()) {
    prize = Serial.parseInt();
    if (Serial.read() == '\n'){ 
       button_state = digitalRead(button);
       // move 0
       posX = analogRead(joyX); 
       posY = analogRead(joyY); 
       
       if (posX >= 550) {
          Serial.print(1);   
       } else {
          Serial.print(0);
       }
       Serial.print(",");
       
       // move 1
       if (posX <= 360) {
          Serial.print(0);   
       } else {
          Serial.print(0);
       }
       Serial.print(",");   
           
       // grab 2
       if (button_state == 0) {
          Serial.print(0);
       } else {
          Serial.print(1);
       }
       Serial.print(",");
       
       // coin pusher 3
       if ( dist < 6 )
          {
            Serial.println(1);
          } else {
            Serial.println(0);       
          }
        digitalWrite(red,prize);
        digitalWrite(yellow,prize);
        digitalWrite(blue,prize);
      }

     } 

}

 

import fisica.*;
import processing.serial.*;
import processing.sound.*;
Serial myPort;
PImage claw;
PImage[][] claws;
PImage bird;
PImage[][] birds;
PImage bg;

boolean start = false;
boolean grab = false;
boolean get = false;
boolean drop = false;
boolean ret = false;
boolean prize = false;
boolean coin = false;
boolean right = false;
boolean left = false;

float speed;
float posX=20, posY=10;
FCircle[] bodies;

ArrayList<FBody> touching = new ArrayList<FBody>();
FCircle tip;
FBody prev_touch;

FWorld world;
int toy_num = 30;

SoundFile grab_sound;
SoundFile drop_sound;
SoundFile prize_sound;
SoundFile start_sound;
 
void setup() {
  size(750, 600);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  
  //
  bg = loadImage("bg.jpeg");
  bg.resize(750,600);
  generate_claw();
  Fisica.init(this);
  world = new FWorld();
  world.setEdges(0, 0, 750, 530, 255);
  generate_birds();
  
  tip = new FCircle(35);
  tip.setFill(0);
  tip.setStatic(true);
  tip.setDrawable(false);
  world.add(tip);
  
  grab_sound = new SoundFile(this, "pick_up.wav");
  drop_sound = new SoundFile(this, "drop.wav");
  prize_sound = new SoundFile(this, "prize.wav");
  start_sound = new SoundFile(this, "coin.wav");
}

void draw() {
  //println(mouseX,mouseY);
  
  background(bg);
  world.step();
  world.draw();
  
  // box
  fill(255, 200);
  noStroke();
  rect(603, 250, 200, 280);
  strokeWeight(3);
  stroke(0);
  line(603, 250, 605, 530);
  
  if (coin) {
    start_sound.play();
    coin = false;
  }
  
  if (start){
    initialize_claw();
  } else {
    fill(#A09B10);
    PFont f = createFont("Dialog-48", 40);
    textFont(f);
    text("SWIPE a coin to START", 145, 100);
    fill(#671235);
    text("SWIPE a coin to START", 140, 100);
  }
}

void initialize_claw() {
  if (posX >= width - 80) {
    posX = width-80;
  }
  
  if (posX <= 20) {
    posX = 20;
  }
  
  
  if (!grab) {
    if (ret) {
      if (posX > 20) {
        image(claws[0][0], posX, 10);
        posX -=3;
        if (prize) {
          fill(#901758);
          text("CONGRATULATIONS!!!", 150, 100);
        }
      } else {
        ret = false;
        world.remove(prev_touch);
        prize = false;
      }
    } else {
      image(claws[0][0], posX, 10);
      if (right) {
        posX += speed;
      }
      
      if (left) {
        posX -= speed;
      }
    }
  } else {
    if (posX >= 550 && !get) {
      grab = false;
      get = false;
      ret = true;
    } else {
    
    claw_down();
    if (posY<0) {
      posY+=3;
    }
    if (!drop) {
      FBody toy = world.getBody(posX+60, posY+80);
      if (get && posY < 20) {
        image(claws[6][0], posX, posY);
        if (posX <= 598) {
          posX += 3;
          toy.setPosition(posX+60, posY+80);
        } else {
          drop = true;
          drop_sound.play();
          toy.setStatic(false);
          grab = false;
          get = false;
          drop = false;
          ret = true;
        // prize
        int ram = (int)random(100);
        if (ram <= 1) {
          prize = true;
          prize_sound.play();
        }
        
         }
        }
      }
    }
  }
    
}

void claw_down() {
  FBody touch;
  tip.setPosition(posX+40, posY+90);
  if (posY < 500 && posY > 0) {
    touching = tip.getTouching();
    if (get) {
      image(claws[6][0], posX, posY);
      posY -= 3;
      if (touching.size()>0) {
        touch = touching.get(0);
        prev_touch = touch;
 
        touch.setStatic(true);
        touch.setPosition(posX+60, posY+80);
      }
    } else{
      image(claws[7][0], posX, posY);
      if (touching.size()>0) {
        get = true;
        grab_sound.play();
      } else {
        posY += 3;
        if (posY >= 500) {
          grab = false;
          get = false;
          ret = true;
        }
      }
    }
  }
}



void generate_claw() {
  claw = loadImage("claw.png");
  claws = new PImage[8][5];
  int w = claw.width / 5; 
  int h = claw.height / 8; 

  for (int y=0; y < 8; y++) {
    for (int x=0; x< 5; x++) {
      claws[y][x] = claw.get(x*w, y*h, w, h);
      claws[y][x].resize(100,120);
    }
  }   
}

void generate_birds() { 
  bodies = new FCircle[30];
  
  bird = loadImage("bird.png");
  birds = new PImage[2][3];
  int w1 = bird.width / 3;
  int h1 = bird.height / 2;
  
  for (int y=0; y < 2; y++) {
    for (int x=0; x< 3; x++) {
      birds[y][x] = bird.get(x*w1, y*h1, w1, h1);
      birds[y][x].resize(85, 85);
    }
  }
  
  for (int i=0; i<toy_num; i++) {
    int radius = 50;
    FCircle b = new FCircle(radius);
    b.setPosition(random(100, 500), height/2);
    b.setStroke(0);
    b.setStrokeWeight(2);
    b.setDensity(radius / 10);
    int random_x = int(random(0,2));
    int random_y = int(random(0,3));
    b.attachImage(birds[random_x][random_y]);
    world.add(b);
    bodies[i] = b;
  }
  
  FBox edge = new FBox(10, 300);
  edge.setPosition(600, 400);
  edge.setStatic(true);
  world.add(edge);
}


void mousePressed() {
  // if button pressed, reset
  world.clear();
  grab = false;
  ret = false;
  get = false;
  drop = false;
  start = false;
  
  bg = loadImage("bg.jpeg");
  bg.resize(750,600);
  generate_claw();
  Fisica.init(this);
  world = new FWorld();
  world.setEdges(0, 0, 750, 530, 255);
  generate_birds();
  
  tip = new FCircle(35);
  tip.setFill(0);
  tip.setStatic(true);
  tip.setDrawable(false);
  world.add(tip);
  
  initialize_claw();
}

void keyPressed() {
  // joystick
  
  //if (keyCode == ENTER) {
  //  speed = 0;
  //  grab = true;
  //} 
  //else {
  //  speed = 8;  
  //}
  speed = 8;
  
  if (keyCode == RIGHT) {
    posX += speed;
  } 
  if (keyCode == LEFT) {
    posX -= speed;
  } 

  
}



void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    println(s);
    int values[]=int(split(s,','));
    if (values.length>=1){
      if (values[3] == 1) {
        coin = true;
        start = true;
      }
      
      if (values[0] == 1 && start) {
        left = false;
        right = true;
      } else {
        right = false;      
      }
      
      if (values[1] == 1 && start) {
        right = false;
        left = true;
      } else {
        left = false;      
      }      
      
      if (values[2] == 1 && start) {
        grab = true;
      }
      
    }
  }
  myPort.write(int(prize)+"\n");
  
}

 

Final Project Progress – Jade

IDEA

For my final project, I plan to simulate a Claw Toy Grabber Machine.  A window showing the claw and toys should be displayed on Processing. On Arduino, there will be a joystick to control the movement of the claw, and a button which makes the claw go down to grab.  I also want to make a coin pusher where player should first toss a coin into the box to start the game.

 

Materials

  1. joystick
  2. wooden box/ drilling tools
  3. piezo + sponge
  4. a larger button

 

Hardest Part

I think the hardest part is the coding part on Processing. Since I want to add physical collisions to the claw and the toys, I think it would be challenging to develop the mechanism of the claw machine, especially for the part where the claw grabs the toy. To make it more real, I want to design in a way that there are several cases when the claw touches a toy. If the player is lucky enough, and the position is correct, then the toy should be lifted up. But the toy might fall off because the claw is loose. So I assume the codes would be complex.

Also I think the physical setting will also be difficult. I want to make a wooden box which has a joystick and a button on it outside my Arduino, and another box for the coin pusher. I have scheduled to borrow the joystick, but I haven’t studied how to use it, so I am not sure about how the setting would look like.

 

Progress

I am currently working on the coding part on Processing. I have already done the visual part (background, toys, claw), and I have also added physical attributes to the toys and the claw, so that there is gravity and they can collide with each other. I am still thinking about how to make the claw detect that it has touched a toy, and then to lift it up. I am kind of stuck at this part.

I plan to start connecting the circuits on Nov.28 and start writing serial communication. When I am off the lab, I would continue writing the codes on Processing.

Final Project Proposal – Jade

IDEA

For my final project, I plan to simulate a Claw Toy Grabber Machine.  A window showing the claw and toys should be displayed on Processing. On Arduino, there will be a joystick to control the movement of the claw, and a button which makes the claw go down to grab. If possible, I also want to make a coin pusher where player should first toss a coin into the box to start the game.

 

Arduino

I plan to make a box with a joystick on it, and leave a space to hold the coins. I am thinking about using a piezo for the coin pusher to check if something heavy has fallen on it. I will make some coins and also probably add some LED lights. When you win the game, toss a coin or press the button, the LED lights should light up in various patterns.

 

Processing

I will add physical collision to the toys, and try to simulate the difficulty of the real Claw Machine. So it won’t be easy to grab the toys. After pressing the button on Arduino, the claw will move automatically to the drop point, and the toy might fall down any time during the process. The goal is to successfully drop the toy into the box, and you can always play the game again by tossing a coin.

The difficulty is the physical collision between the claw and the toys, and how to judge if the claw has grabbed the toy or not. I think it would be better if the toy are composed of several parts, so that the claw could grab a toy by different angles/parts.

 

Serial Communication

Arduino should send data on the movement of the joystick, button status and coin pusher status to Processing. Processing will send signals of the game status to control the LEDs.

 

 

Exercises – Jade

Exercise 1

Arduino:

void setup() {
  Serial.begin(9600);
  Serial.println("0,0");
  pinMode(A0, INPUT);
}
 
void loop() {
  while (Serial.available()) {
    if (Serial.read() == '\n') {
     int sensor = analogRead(A0);
     Serial.println(sensor);
   }
  }
}

 

Processing:

import processing.serial.*;
Serial myPort;
int xPos=0;

void setup(){
  size(960,720);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
}

void draw(){
  background(255);
  ellipse(xPos,height/2,30,30);
}

void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    println(s);
    int values[]=int(split(s,','));
    if (values.length==1){
      xPos=(int)map(values[0],0,1023,width, 0);
    }
  }
  myPort.write("\n");
}

 

 

Exercise 2

Arduino:

int bright = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("0,0");
  pinMode(2, OUTPUT);
  pinMode(5, OUTPUT);
}

void loop() {
  while (Serial.available()) {
      bright = Serial.parseInt();
      if (Serial.read() == '\n') {
        analogWrite(5, bright);
        Serial.println(0);
    }
  }
}

Processing:

import processing.serial.*;
Serial myPort;

void setup(){
  size(960,720);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
}

void draw(){
  background(255);
  ellipse(mouseX,mouseY,30,30); // mark
}

void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  myPort.write((int)(map(mouseY, height,0, 0, 255))+"\n");
}

 

Exercise 3

Arduino:

int bounce = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("0,0");
  pinMode(5, OUTPUT);
  pinMode(A0, INPUT);
}

void loop() {
  while (Serial.available()) {
    bounce = Serial.parseInt();
    if (Serial.read() == '\n') {
      digitalWrite(5, bounce);
      int sensor = analogRead(A0);
      Serial.println(sensor);
    }
  }
}

Processing:

import processing.serial.*;
Serial myPort;

PVector velocity;
PVector gravity;
PVector position;
PVector acceleration;
PVector wind;
float drag = 0.99;
float mass = 50;
float hDampening;

boolean flag = false;

void setup() {
  size(640,360);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  
  
  noFill();
  position = new PVector(width/2, 0);
  velocity = new PVector(0,0);
  acceleration = new PVector(0,0);
  gravity = new PVector(0, 0.5*mass);
  wind = new PVector(0,0);
  hDampening=map(mass,15,80,.98,.96);
}

void draw() {
  background(255);
  velocity.x*=hDampening;
  applyForce(wind);
  applyForce(gravity);
  velocity.add(acceleration);
  velocity.mult(drag);
  position.add(velocity);
  acceleration.mult(0);
  ellipse(position.x,position.y,mass,mass);
  if (position.y >= height-mass/2) {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      position.y = height-mass/2;
    } 
  if (round(velocity.y)<0) {
    flag = true;
  } else {
    flag = false;
  }
}
  
void applyForce(PVector force){
  // Newton's 2nd law: F = M * A
  // or A = F / M
  PVector f = PVector.div(force, mass);
  acceleration.add(f);
}

void keyPressed(){
  if (key==' '){
    mass=random(15,80);
    position.y=-mass;
    velocity.mult(0);
  }
}

void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null) {
    println(s);
    int values[]=int(split(s, ','));
    if (values.length==1) {
      wind.x=(float)map(values[0], 0, 1023, 5, -5);
    }
  }
  myPort.write(int(flag)+"\n");

}

Jade and Bhavicka’s Keys of Misery

 

Description / Mechanism

For this week, Bhavicka and I made a keyboard with a little drum around. We planned to make something like a DJ mixer, so we used the copper tapes as a keyboard, piezo as a drum and the slider as the sliding control.

At the very beginning, we thought about using MP3 shield, AD card and another speaker to play MP3 files of the sound for drum and keyboard. But it was too complicated because of all the codes that are beyond our understanding, libraries that should be installed and some compatible issues, so eventually it didn’t work out.

Then there is plan B. We used four notes for the drum sound, making it sounds like an opening theme. For the keys, we used C, D, E, F, G, A, B, C (higher) in order. The slider could change the pitch of the keys, so the pitch gets higher as we slide it to the middle and even higher when it reaches the other end.

 

Code

# include "pitches.h"

const int knockSensor = A0;
const int buzzer = 7;
const int slide = A1;
const int threshold = 1000;
int sensorReading = 0;
int slideReading = 0;

const int note1 = 2;
const int note2 = 3;
const int note3 = 4;
const int note4 = 5;
const int note5 = 6;
const int note6 = 8;
const int note7 = 9;
const int note8 = 10;

int drum[4] = {NOTE_C1, NOTE_C2, NOTE_C3, NOTE_C4};

int pitch0[8] = {NOTE_C3, NOTE_D3, NOTE_E3, NOTE_F3, NOTE_G3, NOTE_A4, NOTE_B4, NOTE_C4};
int pitch1[8] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A5, NOTE_B5, NOTE_C5};
int pitch2[8] = {NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5, NOTE_A6, NOTE_B6, NOTE_C6};

int pitch = 0;

void setup() {
  Serial.begin(9600);
  pinMode(note1, INPUT);
  pinMode(note2, INPUT);
  pinMode(note3, INPUT);
  pinMode(note4, INPUT);
  pinMode(note5, INPUT);
  pinMode(note6, INPUT);
  pinMode(note7, INPUT);
  pinMode(note8, INPUT);
}

void loop() {

  slideReading = analogRead(slide); // 562-1023
  sensorReading = analogRead(knockSensor);

  Serial.print("Knob Value: ");
  Serial.println(sensorReading);
  Serial.print("Slide Value: ");
  Serial.println(slideReading);
  
  if (sensorReading >= threshold) {
    tone(buzzer, drum[0], 200);
    delay(200);
    tone(buzzer, drum[1], 200);
    delay(200);
    tone(buzzer, drum[2], 200);
    delay(200);
    tone(buzzer, drum[3], 200);
  }

  if (slideReading <= 700) {
    pitch = 0;
  }

  else if (slideReading <= 880) {
    pitch = 1;
  }

  else {
    pitch = 2;
  }


  if (pitch == 0) {
    if (digitalRead(note1) == HIGH) {
      tone(buzzer, pitch0[0], 500);

    } else if (digitalRead(note2) == HIGH) {
      tone(buzzer, pitch0[1], 500);

    }
    else if (digitalRead(note3) == HIGH) {
      tone(buzzer, pitch0[2], 500);

    }
    else if (digitalRead(note4) == HIGH) {
      tone(buzzer, pitch0[3], 500);

    }

    else if (digitalRead(note5) == HIGH) {
      tone(buzzer, pitch0[4], 500);

    }

    else if (digitalRead(note6) == HIGH) {
      tone(buzzer, pitch0[5], 500);

    }
    else if (digitalRead(note7) == HIGH) {
      tone(buzzer, pitch0[6], 500);

    }

    else if (digitalRead(note8) == HIGH) {
      tone(buzzer, pitch0[7], 500);

    } else {
      noTone(buzzer);
    }
  }

  else if (pitch == 1) {
    if (digitalRead(note1) == HIGH) {
      tone(buzzer, pitch1[0], 500);

    } else if (digitalRead(note2) == HIGH) {
      tone(buzzer, pitch1[1], 500);

    }
    else if (digitalRead(note3) == HIGH) {
      tone(buzzer, pitch1[2], 500);

    }
    else if (digitalRead(note4) == HIGH) {
      tone(buzzer, pitch1[3], 500);

    }

    else if (digitalRead(note5) == HIGH) {
      tone(buzzer, pitch1[4], 500);

    }

    else if (digitalRead(note6) == HIGH) {
      tone(buzzer, pitch1[5], 500);

    }
    else if (digitalRead(note7) == HIGH) {
      tone(buzzer, pitch1[6], 500);

    }

    else if (digitalRead(note8) == HIGH) {
      tone(buzzer, pitch1[7], 500);

    } else {
      noTone(buzzer);
    }
  }

  else {
    if (digitalRead(note1) == HIGH) {
      tone(buzzer, pitch2[0], 500);

    } else if (digitalRead(note2) == HIGH) {
      tone(buzzer, pitch2[1], 500);

    }
    else if (digitalRead(note3) == HIGH) {
      tone(buzzer, pitch2[2], 500);

    }
    else if (digitalRead(note4) == HIGH) {
      tone(buzzer, pitch2[3], 500);

    }

    else if (digitalRead(note5) == HIGH) {
      tone(buzzer, pitch2[4], 500);

    }

    else if (digitalRead(note6) == HIGH) {
      tone(buzzer, pitch2[5], 500);

    }
    else if (digitalRead(note7) == HIGH) {
      tone(buzzer, pitch2[6], 500);

    }

    else if (digitalRead(note8) == HIGH) {
      tone(buzzer, pitch2[7], 500);

    } else {
      noTone(buzzer);
    }
  }
}

 

Our Process

This is the most challenging project so far. We spent a lot of time figuring out the circuits as there were 8 keys, a piezo and a slider. One difficulty we encountered which took us so much time was to close the circuits for the 8 keys.

We attached a jumper cable to the copper tape below which connects to power. Then we attached other jumper cables to the keys and connected them to various pins respectively, and we also put resistors to the ground to create a switch. So when the keys touched the copper tape below, this should close a circuit.

Our plan didn’t work out well at first, maybe because we put these key circuits near the circuits for the piezo and slider, which may cause a short circuit. After we moved all the cables to the other side of the bread board, the problem was solved. Since there were so many wires, we had to be really careful to not let them collide with each other, causing short circuits.

The coding part was not so challenging as the circuit part. The only problem was about the tone() and noTone() function. We realized that we should use else if condition for different notes.

else if (digitalRead(note7) == HIGH) {
 tone(buzzer, pitch0[6], 500);
 } else if (digitalRead(note8) == HIGH) {
 tone(buzzer, pitch0[7], 500);
 } else {
 noTone(buzzer);
 }

At first, we wrote the codes like:

if (digitalRead(note7) == HIGH) {
 tone(buzzer, pitch0[6], 500);
 } else {
 noTone(buzzer);
 }

if (digitalRead(note8) == HIGH) {
 tone(buzzer, pitch0[7], 500);
 } else {
 noTone(buzzer);
 }

which was problematic because when the other keys were not pressed, then noTone() was always executed. As a result, this caused the buzzer to make strange broken noise.

 

 

 

Although the project is demanding, the process of doing it was interesting and pleasant. The teamwork was great and it was really enjoyable working with Bhavicka!

 

Little Robot (Analog) – Jade

EYELASH                                  EYELASH

 

Description / Mechanism

For this week’s assignment, I made a project that looks like a face of a robot, which has lights as its eyes, cables as nose and the distance sensor as mouth.

Normally, it looks straight at you, with the lights at the center of both sides on. If you turn the potentiometer left, the robot looks left, so the red lights are on. If you turn it right, the blue lights are on, showing that the robot is now looking right. If you move close to the robot’s face (mouth), it gets shy and closes its eyes, so the lights are all off.

If you press the button, the robot will enter into another state. Instead of looking at you, it starts processing data, so I have the delays between LEDs to show how data flows through. Then if you press again, it will go back to its original state.

 

Code

const int led1 = 2;
const int led2 = 3;
const int led3 = 4;
const int led4 = 5;
const int led5 = 6;
const int led6 = 7;
const int led7 = 8;
const int led8 = 9;
const int button = 10;
const int echo = 12;
const int trig = 13;
const int knob = A1;

int count = 0;
int ledState = LOW;
int buttonState1;
int buttonState2;
int de = 100;
bool off = false;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);
  pinMode(led6, OUTPUT);
  pinMode(led7, OUTPUT);
  pinMode(led8, OUTPUT);
  
  pinMode(button, INPUT);
  pinMode(knob, INPUT);
  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT);
  buttonState1 = digitalRead(button); 
}

void loop() {
  buttonState2 =  digitalRead(button);
  if (buttonState2 != buttonState1) {
    if (buttonState2 == LOW) {
      count ++;
    }
  }
  buttonState1 = buttonState2;

  if (count%2 == 1) {
    ledState = HIGH;
  } else {
    ledState = LOW;
  }
  
  int knob_val = analogRead(knob);


  digitalWrite(trig, LOW);
  delayMicroseconds(2);
  digitalWrite(trig, HIGH);
  delayMicroseconds(2);
  digitalWrite(trig, LOW); 
   
  int duration = pulseIn(echo, HIGH);
  int dist = duration/58.2;

  
  if (ledState == HIGH) {  
    digitalWrite(led1, HIGH);
    delay(de);
    digitalWrite(led2, HIGH);
    delay(de);    
    digitalWrite(led3, HIGH);
    delay(de);    
    digitalWrite(led4, HIGH);
    delay(de);
    digitalWrite(led5, HIGH);
    delay(de);
    digitalWrite(led6, HIGH);
    delay(de);    
    digitalWrite(led7, HIGH);
    delay(de);    
    digitalWrite(led8, HIGH);
    
    digitalWrite(led1, LOW);
    delay(de);
    digitalWrite(led2, LOW);
    delay(de);    
    digitalWrite(led3, LOW);
    delay(de);    
    digitalWrite(led4, LOW);
    delay(de);
    digitalWrite(led5, LOW);
    delay(de);
    digitalWrite(led6, LOW);
    delay(de);    
    digitalWrite(led7, LOW);
    delay(de);    
    digitalWrite(led8, LOW);
  } else {
    int del;
    if (dist <= 10) {
      off = true;
    } else {
      off = false;
    }
    if (off) {
      digitalWrite(led1, LOW);
      digitalWrite(led4, LOW);
      digitalWrite(led5, LOW);
      digitalWrite(led8, LOW);
      digitalWrite(led2, LOW);
      digitalWrite(led3, LOW);
      digitalWrite(led6, LOW);
      digitalWrite(led7, LOW);
    } else {
    
    if (knob_val>300 and knob_val<630) {
      digitalWrite(led1, LOW);
      digitalWrite(led4, LOW);
      digitalWrite(led5, LOW);
      digitalWrite(led8, LOW);
      digitalWrite(led2, HIGH);
      digitalWrite(led3, HIGH);
      digitalWrite(led6, HIGH);
      digitalWrite(led7, HIGH);
    }
    if (knob_val<=300) {
      digitalWrite(led1, HIGH);
      digitalWrite(led4, HIGH);
      digitalWrite(led5, LOW);
      digitalWrite(led8, LOW);
      digitalWrite(led2, HIGH);
      digitalWrite(led3, HIGH);
      digitalWrite(led6, LOW);
      digitalWrite(led7, LOW);
    }
    if (knob_val>=630) {
      digitalWrite(led1, LOW);
      digitalWrite(led4, LOW);
      digitalWrite(led5, HIGH);
      digitalWrite(led8, HIGH);
      digitalWrite(led2, LOW);
      digitalWrite(led3, LOW);
      digitalWrite(led6, HIGH);
      digitalWrite(led7, HIGH);
    }
    }
  }

  
}

 

Challenges

Initially, instead of the distance sensor, I was going to use the LDR sensor as the robot’s nose. However, the light in the dorm was not suitable for it and the output of the LDR value was quite random. I couldn’t figure out how to use the photo cell properly, so I changed it to the distance sensor, which is more reliable 🙂 Then I readjusted the position and found that the cables could be the nose!

The challenge I encountered was how to arrange the elements to create a correct circuit. Since I had many parts on the board, I needed to be careful not to overlap the positions for power/ground, so that they don’t touch each other to form another circuit. Thanks to Professor Aaron, I successfully connected the button and also the distance sensor.

The code part was not so challenging, but it took me some time to figure out the logics. The order of the conditions in the codes was difficult to sort out.

Overall, I think I have learned a lot from this project and have a better understanding of how the circuits work, especially how the sensors work.

Unusual Switch – Jade

 

Description

In this project, I used LEDs to create a switch that doesn’t require the need of my hands. The mechanism is that if you move close enough to each other, the red light will turn on and the green light goes out. Otherwise, the green light is on.

The idea of this project is inspired by social distance. If you are keeping a distance with other person, the green light is on, so normally it keeps on. If you are too close, red light is on, suggesting that you violate the social distance. I used masks to protect our arms and also as a symbol.

 

Code

const int LED2 = 2;
const int LED = 3;
int state = HIGH;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED2, OUTPUT);
  pinMode(LED, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(LED, LOW);
  digitalWrite(LED2, state);
  if (digitalRead(LED) == HIGH) {
    state = LOW;
  } else {
    state = HIGH;
  }
}

 

Process

I connect the LEDs to pins 2, 3. I read input from the red one and output the green light. On the circuit of red LED, I connected another two cables which make the switch. When the two cables meet, the circuit will be closed, and the red light turns on, so the green light goes off. Because in my code, I set it so that when the red light is on, the green light will change state.

The challenge I encounter is to figure out a creative way to make a switch free of the use of hands. Initially, I also thought of several ideas, but they required indirect use of hands. So it took me sometime to think of which part of my body I want to use for the switch.

Though, it was interesting to work on circuits, cables and codes!

MATCH-GAME Midterm Project – Jade

 

Description:

For my midterm project, I made a mutant version of Match Three game, including elements of Tetris. The patterns of the cubes come from Mahjong, a traditional Chinese tile-based chess game. The player needs to match 3 or more cubes with the same patterns and make combos to eliminate the cubes, and the goal is to eliminate the highlighted cube. I designed a help window, so when you press “H”, you can see some of the combos allowed in this game. There are some more kinds of combos, and I intend to leave the part for the player to figure out.

In the game, the player uses “s”, “a”, “d” to move the cube, and can press “enter” to start, “p” to pause the game, “h” for help and “esc” to exit the game. The players can press “enter” to restart the game once they clear or they lose the game.

I inserted some sound effects, like when the cube falls into the place or the player successfully makes a combo, also when the player wins or loses the game.

There is also one more mechanism that as the score accumulates, the cubes will fall down at a higher speed.

 

Mahjong

 

Challenges:

It is challenging to design the mechanism for the game and also to figure out the logic part of the codes. During this week, I focused on the UI of the game. And using images instead of colors is harder than I thought. I had to modify pretty many codes to replace all the colors with images.

And for the sound part, I had to pay attention to where to put the sound.play code so that it won’t loop over and over. It also takes me a lot of time designing the UI and locating the texts and images.

But the whole process of making the game from scratch is interesting. I learned a lot from this midterm assignment!

import processing.sound.*;
SoundFile same_sound;
SoundFile win_sound;
SoundFile fit_sound;
SoundFile over_sound;

int w = 10;
int h = 10;
int q = 40;
int dt;
int currentTime;

PImage[] cubes;
PImage bg;
PImage bg1;
PImage win;
PImage over;

Grid grid;
Piece piece;
Piece nextPiece;
Pieces pieces;
Score score;
int level = 1;
int nbLines = 0;

int txtSize = 20;
int textColor = 0;
int x1;

Boolean gameOver = false;
Boolean gameOn = false;
Boolean gamePause = false;
Boolean gameWin = false;
Boolean help = false;

void setup()
{
  size(600, 480, P2D);
  textSize(30);
  cubes = new PImage[4];
  cubes[0] = loadImage("dong.png");
  cubes[1] = loadImage("nan.png");
  cubes[2] = loadImage("xi.png");
  cubes[3] = loadImage("bei.png");
  bg = loadImage("bg.png");
  bg1 = loadImage("bg1.png");
  win = loadImage("win.jpeg");
  over = loadImage("over.png");
  
  same_sound = new SoundFile(this, "same.wav");
  win_sound = new SoundFile(this, "win.wav");
  fit_sound = new SoundFile(this, "fit.wav");
  over_sound = new SoundFile(this, "over.wav");
}

void initialize() {
  nbLines = 0;
  dt = 1000;
  currentTime = millis();
  score = new Score();
  grid = new Grid();
  pieces = new Pieces();
  piece = new Piece(-2);
  nextPiece = new Piece(-2);
  grid.generate();
  score = new Score();
  level = 1;
  
  x1 = int(random(0, w)); 
  while (grid.isFree(x1,h-1)) {
    x1 = int(random(0, w));
  }
}

void draw()
{
  background(0);
  image(bg, 0, 0, 600, 480);
  if (grid != null) {
    grid.drawGrid();
    int now = millis();
    if (gameOn) {
      if (now - currentTime > dt) {
        currentTime = now;
        piece.oneStepDown();
      }
    }
    fill(#F8FC08);
    PFont f = createFont("", 20);
    textFont(f);
    text("P - PAUSE", 30, 360);
    text("H - HELP", 30, 400);
    
    PFont pixel = createFont("ARCADECLASSIC.TTF", 40);
    textFont(pixel);
    piece.display(false);
    score.display();
    fill(#DE9F16);
    text("Goal: ", 30, 200);
    if (grid.cells[x1][h-1] != -1) {
      image(cubes[grid.cells[x1][h-1]], 50, 220, 40, 40);
    }    
    stroke(#F8FC08);
    strokeWeight(5);
    line(50, 220, 90, 220);
    line(50, 220, 50, 260);
    line(90, 220, 90, 260);
    line(50, 260, 90, 260);
  }
  
  if (gameOver) {
     noStroke();
     background(255);
     fill(0);
     over.resize(600, 200);
     image(over, 0, 100);
     text("Press 'ENTER' to     restart.", 50, 400);
     over_sound.play(); 
     noLoop();
  }
  
  if (!gameOn) {
    image(bg1, 0, 0, 600, 480);
    noStroke();    
    fill(#A04C13);
    PFont f = createFont("", 30);
    textFont(f);
    text("MOVE LEFT", 70, 140);
    text("MOVE RIGHT", 70, 180);
    text("MOVE DOWN", 70, 220);
    text("PAUSE", 70, 260);
    text("HELP", 465, 390);
    text("EXIT", 500, 430);

    PFont pixel = createFont("ARCADECLASSIC.TTF", 40);
    textFont(pixel);
    text("A", 25, 140);
    text("D", 25, 180);
    text("S", 25, 220);
    text("P", 25, 260);
    text("H", 420, 390);
    text("ESC", 420, 430);
    
    fill(0);
    stroke(0);
    strokeWeight(3);
    line(20, 140, 50, 140);
    line(20, 115, 50, 115);
    line(20, 115, 20, 140);
    line(50, 115, 50, 140);
    
    line(20, 155, 50, 155);
    line(20, 180, 50, 180);
    line(20, 155, 20, 180);
    line(50, 155, 50, 180);   

    line(20, 195, 50, 195);
    line(20, 220, 50, 220);
    line(20, 195, 20, 220);
    line(50, 195, 50, 220); 
    
    line(20, 235, 50, 235);
    line(20, 260, 50, 260);
    line(20, 235, 20, 260);
    line(50, 235, 50, 260);    

    line(415, 390, 445, 390);
    line(415, 365, 445, 365);
    line(415, 365, 415, 390);
    line(445, 365, 445, 390);  
    
    line(415, 430, 490, 430);
    line(415, 405, 490, 405); 
    line(415, 405, 415, 430);
    line(490, 405, 490, 430);  
    
  }
  
  if (gameWin) {
     noStroke();
     image(win, 0, 0, 600, 480);
     fill(#E36917);
     text("Press 'ENTER' to    restart.", 30, 460);
     win_sound.amp(0.5);
     win_sound.play();
     noLoop();
  }
  
  if (help) {
    fill(#EAD0E4);
    rect(0, 0, 600, 480);
    fill(#E36917);
    PFont f = createFont("", 30);
    textFont(f);
    text("Match 3 or more cubes with", 40, 100);
    text("the same pattern to eliminate.", 40, 140);
    image(cubes[3], 60, 180, 50, 50);
    image(cubes[3], 110, 180, 50, 50);
    image(cubes[3], 160, 180, 50, 50);
    
    image(cubes[2], 280, 180, 50, 50);
    image(cubes[2], 330, 180, 50, 50);
    image(cubes[2], 380, 180, 50, 50);
    image(cubes[2], 430, 180, 50, 50);
    image(cubes[2], 480, 180, 50, 50);  
    
    image(cubes[1], 110, 250, 50, 50);
    image(cubes[1], 110, 300, 50, 50);
    image(cubes[1], 110, 350, 50, 50);
    
    image(cubes[0], 280, 250, 50, 50);
    image(cubes[0], 330, 250, 50, 50);
    image(cubes[0], 380, 250, 50, 50);
    image(cubes[0], 380, 300, 50, 50);
    image(cubes[0], 380, 350, 50, 50);
   }
}

void goToNextPiece() {
  piece = new Piece(nextPiece.kind);
  nextPiece = new Piece(-2);
}

void goToNextLevel() {
  level = 1 + int(nbLines / 100);
  dt *= .98;
}

void keyPressed() {
  if (gameOn) {
      piece.inputKey(keyCode);
  }
  if (keyCode == 80) {
    if (gameOn) {
      gamePause = !gamePause;
      
      if (gamePause) {
        fill(0, 60);
        rect(width/2 - 220, height/2 - 50, 450, 100, 20);
        fill(255);
        textSize(25);
        PFont f = createFont("", 30);
        textFont(f);
        stroke(0);
        text("Press  'P'  to restart the game", width/2 - 200, height/2);
        noLoop();
      } else if (!gamePause){
        loop();
      }
    }
    
  } else if (key == ENTER) {
    if (!gameOn || gameWin || gameOver) {
      initialize();
      gameWin = false;
      gameOver = false;
      gameOn = true;
      loop();
    }    
  } 
  
  if (keyCode == 72) {
    help = !help;
    if (gameOn) {
      gamePause = true;
    }
  }
}
class Piece {

  final int[] colors = {0, 1, 2, 3};

  final int[][] pos;
  int x = int(w/2);
  int y = 0;
  int kind;
  int c;

  Piece(int k) {
    if (k == -2) {
      kind = int(random(0, 4));
    } else {
      kind = k;
    }
    c = colors[kind];
    pos = pieces.pos[kind];
  }

  void display(Boolean still) {
    stroke(250);
    //fill(c);
    pushMatrix();
    if (!still) {
      translate(160, 40);
      translate(x*q, y*q);
    }
    image(cubes[c], pos[0][0] * q, pos[0][1] * q, 40, 40);
    popMatrix();
  }

  // returns true if the piece can go one step down
  void oneStepDown() {
    y += 1;
    if(!grid.pieceFits()){
      piece.y -= 1;
      grid.addPieceToGrid();
    }
  }

  // try to go one step left
  void oneStepLeft() {
    x --;
  }

  // try to go one step right
  void oneStepRight() {
    x ++;
  }

  void goToBottom() {
    grid.setToBottom();
  }

  void inputKey(int k) {
    switch(k) {
    case 65:
      x --;
      if(grid.pieceFits()){
      }else {
         x++; 
      }
      break;
    case 68:
      x ++;
      if(grid.pieceFits()){
      }else{
         x--; 
      }
      break;
    case 83:
      oneStepDown();
      break;
    }
  }

}



class Pieces {
  int[][][] pos = new int [4][1][2];
  
  Pieces() {
    pos[0][0][0] = -1;
    pos[0][0][1] = 0;
    pos[1][0][0] = -1;
    pos[1][0][1] = 0;
    pos[2][0][0] = -1;
    pos[2][0][1] = 0;
    pos[3][0][0] = -1;
    pos[3][0][1] = 0;
  }
}
class Grid {
  int [][] cells = new int[w][h];

  Grid() {
    for (int i = 0; i < w; i ++) {
      for (int j = 0; j < h; j ++) {
        cells[i][j] = -1;
      }
    }
  }

  Boolean isFree(int x, int y) {
    if (x > -1 && x < w && y > -1 && y < h) {
      return cells[x][y] == -1;
    } else if (y < 0) {
      return true;
    }
    return false;
  }

  Boolean pieceFits() {
    int x = piece.x;
    int y = piece.y;
    int[][] pos = piece.pos;
    Boolean pieceOneStepDownOk = true;
    int tmpx = pos[0][0]+x;
    int tmpy = pos[0][1]+y;
    if (tmpy >= h || !isFree(tmpx, tmpy)) {
      fit_sound.play();
      pieceOneStepDownOk = false;
     }
    return pieceOneStepDownOk;
  }

  void addPieceToGrid() {
    int x = piece.x;
    int y = piece.y;
    int[][] pos = piece.pos;
      if(pos[0][1]+y >= 0){
        cells[pos[0][0]+x][pos[0][1]+y] = piece.c;
      }else{
        gameOn = false;
        gameOver = true;
        return;
      }
    checkSame();
    
    
    goToNextPiece();
    checkSame();
    drawGrid();
    checkGame();
    
  }
  

  void checkSame() {
    int nb = 0;
    int mark_x = 0;
    int mark_y = 0;
    boolean same = false;
    
    // check horizontal
    for (int j = 0; j < h; j ++) {
      for (int i = 0; i < w; i++) {
        int c1 = cells[i][j];
        if ((i+2 < w) && (!isFree(i, j))) {
          if ((cells[i+1][j] == c1) &&
            (cells[i+2][j] == c1)) {
              int mark_num = 2;
              mark_x = i;
              mark_y = j;
              if ((i+3 < w) && (cells[i+3][j] == c1)) {
                mark_num = 3;
              }
              if ((i+4 < w) && (cells[i+4][j] == c1)) {
                mark_num = 4;
              }            
              nb = nb + mark_num - 1;
              same_sound.play();
              for (int k = mark_y; k > 0; k--) {
                for (int m = mark_x;  m <= mark_x + mark_num; m++) {
                  if (m==x1 && j == h-1) {
                    cells[x1][h-1] = -1;
                    checkGame();
                  }
                  if ((j+2 < h) && (cells[m][j+1] == c1) &&
                  (cells[m][j+2] == c1)) {       
                    cells[m][j+1] = cells[m][j];
                    cells[m][j+2] = cells[m][j+1];
                    checkGame();
                  }
                  cells[m][k] = cells[m][k-1];
                  cells[m][0] = -1;
                  checkGame();
              continue;
             }           
            }
           }
      }
      if ((j+2 < h) && (!isFree(i, j))) {
          if ((cells[i][j+1] == c1) &&
          (cells[i][j+2] == c1)) {
            same = true;
            mark_x = i;
            mark_y = j;         
      if (same) {
        nb++;
        same_sound.play();
        for (int k = mark_y; k < mark_y + 3; k++) {
          try{       
          cells[mark_x][k] = -1;
          checkGame();
          } catch(Exception e) {           
          }
        }
        for (int k = mark_y; k > 2; k--){
          cells[mark_x][k] = cells[mark_x][k-2];
          checkGame();
        }
     
      } 
      }
      }
      }
    }
    checkGame();
    deleteLines(nb);
    }
    
    
  Boolean checkWin() {
    if (isFree(x1, h-1)) {
      return true;
    }
    return false;
  }

  void deleteLines(int nb) {
    nbLines += nb;
    if (int(score.points / 100) > level) {
      goToNextLevel();
    }
    score.addLinePoints(nb);
  }

  void setToBottom() {
    int j = 0;
    for (j = 0; j < h; j ++) {
      if (!pieceFits()) {
        break;
      } else {
        piece.y++;
      }
    }
    piece.y--;
    delay(1500);
    addPieceToGrid();
  }

  void drawGrid() {
    stroke(150);
    pushMatrix();
    translate(160, 40);
    line(-10, 0, -10, h*q);

    for (int i = 0; i < w; i ++) {
      for (int j = 0; j < h; j ++) {
        if (cells[i][j] != -1) {
          image(cubes[cells[i][j]], i * q, j * q, 40, 40);
        }
      }
    }
    pick(x1, h-1);
    popMatrix();
  }
  
  void generate() {
     for (int i = 0; i < w; i ++) {
      for (int j = h * 1/2 ; j < h ; j ++) {
        int kind = int(random(0, 4));
        cells[i][j] = kind;
        image(cubes[kind], i * q, j * q, 40, 40);
      }
      checkSame();
    }
    checkSame();
  }

  
  void checkGame() {
    for (int i=0; i<w; i++) {
      if (!isFree(i, 0)) {
        gameOver = true;    
      }
      for (int j=0; j<h; j++) {
        if (!isFree(i, j) && isFree(i,j+1)) {
          cells[i][j] = -1;
        }
      } 
    }
    if (checkWin()) {
      gameWin = true;
    }
  }
  
  void pick(int x, int y) {
    stroke(#F8FC08);
    strokeWeight(5);
    if (cells[x][y] != -1) {
      image(cubes[cells[x][y]], x * q, y * q, 40, 40);
    }
    line(x*q, y*q, (x+1)*q, y*q);
    line(x*q, y*q, x*q, (y+1)*q);
    line((x+1)*q, y*q, (x+1)*q, (y+1)*q);
    line(x*q, (y+1)*q, (x+1)*q, (y+1)*q);
  }

}
class Score {
  int points = 0;

  void addLinePoints(int nb) {
    points += level * nb * 10;
  }


  void display() {
    pushMatrix();
    translate(30, 60);

    //score
    fill(#DE9F16);
    text("score: ", 0, 0);
    fill(230, 230, 12);
    text(""+formatPoint(points), 0, txtSize + 10);
    
    popMatrix();

  }

  String formatPoint(int p) {
    String txt = "";
    int qq = int(p/1000000);
    if (qq > 0) {
      txt += qq + ",";
      p -= qq * 1000000;
    }

    qq = int(p/1000);
    if (txt != "") {
      if (qq == 0) {
        txt += "000";
      } else if (qq < 10) {
        txt += "00";
      } else if (qq < 100) {
        txt += "0";
      }
    }
    if (qq > 0) {
      txt += qq;
      p -= qq * 1000;
    }
    if (txt != "") {
      txt += ",";
    }

    if (txt != "") {
      if (p == 0) {
        txt += "000";
      } else if (p < 10) {
        txt += "00" + p;
      } else if (p < 100) {
        txt += "0" + p;
      } else {
        txt += p;
      }
    } else {
      txt += p;
    }
    return txt;
  }
}

 

Midterm Prototype

Description:

For my midterm project, I referred to OpenProcessing. This game is  a combination of Tetris and SameGame. In this game, the player’s goal is to eliminate the highlighted cube. Every turn, a cube falls down and the player can move it left or right, just like in the Tetris, but only in the form of a cube. Also, random cubes will generate across the canvas to increase the difficulty and playability. The player should manage to make combos of cubes with the same color in order to eliminate them. It is set that the player can eliminate at most 5 cubes.

 

Challenges:

Considering the mechanisms of the game is the most challenging part so far. Since it is a new game (I’ve never seen it elsewhere), it takes me a lot of time to play it myself and adjust the game rules.

At first, I set the goal to be eliminating every cube, which is almost impossible because if you are left with only a few cubes, you have to be really lucky to get the color you want to make combos.

I also found it logically tricky to write the codes for combos.  👇

void checkSame() {
    int nb = 0;//number of full lines
    int mark_x = 0;
    int mark_y = 0;
    boolean same = false;
    
    // check horizontal
    for (int j = 0; j < h; j ++) {
      for (int i = 0; i < w; i++) {
        color c1 = cells[i][j];
        if ((i+2 < w) && (!isFree(i, j))) {
          if ((cells[i+1][j] == c1) &&
            (cells[i+2][j] == c1)) {
              int mark_num = 2;
              mark_x = i;
              mark_y = j;
              if ((i+3 < w) && (cells[i+3][j] == c1)) {
                mark_num = 3;
              }
              if ((i+4 < w) && (cells[i+4][j] == c1)) {
                mark_num = 4;
              }
              nb = nb + mark_num - 1;
              for (int k = mark_y; k > 0; k--) {
                for (int m = mark_x;  m <= mark_x + mark_num; m++) {
                  if ((j+2 < h) && (cells[m][j+1] == c1) &&
                  (cells[m][j+2] == c1)) { 
                    cells[m][j+1] = 0;
                    cells[m][j+2] = 0;
                  }
                  checkGame();
                  cells[m][k] = cells[m][k-1];
                  cells[m][0] = 0;
              continue;
             }           
            }
           }
      }
      if ((j+2 < h) && (!isFree(i, j))) {
          if ((cells[i][j+1] == c1) &&
          (cells[i][j+2] == c1)) {
            same = true;
            mark_x = i;
            mark_y = j;
           }           
          }
      if (same) {
        nb++;
        for (int k = mark_y; k < mark_y + 3; k++) {
          try{
          cells[mark_x][k] = 0;
          checkGame();
          } catch(Exception e) {           
          }
        }
        for (int k = mark_y; k > 2; k--) {
          cells[mark_x][k] = cells[mark_x][k-2];
        }
      } 
      }
      }
    deleteLines(nb);
    }

 

Codes:

int w = 10;
int h = 10;
int q = 40;
int dt;
int currentTime;
Grid grid;
Piece piece;
Piece nextPiece;
Pieces pieces;
Score score;
int level = 1;
int nbLines = 0;

int txtSize = 20;
int textColor = 0;
int x1;

Boolean gameOver = false;
Boolean gameOn = false;
Boolean gamePause = false;
Boolean gameWin = false;

void setup()
{
  size(600, 480, P2D);
  textSize(30);
}

void initialize() {
  nbLines = 0;
  dt = 1000;
  currentTime = millis();
  score = new Score();
  grid = new Grid();
  pieces = new Pieces();
  piece = new Piece(-1);
  nextPiece = new Piece(-1);
  grid.generate();
  score = new Score();
  level = 1;
  
  x1 = int(random(0, w)); 
  while (grid.isFree(x1,h-1)) {
    x1 = int(random(0, w));
  }
}

void draw()
{
  background(#3D4E81);
  if (grid != null) {
    grid.drawGrid();
    int now = millis();
    if (gameOn) {
      if (now - currentTime > dt) {
        currentTime = now;
        piece.oneStepDown();
      }
    }
    piece.display(false);
    score.display();
    fill(#F2D7D7);
    text("Goal: ", 40, 200);
    stroke(#F8FC08);
    strokeWeight(5);
    fill(grid.cells[x1][h-1]);
    rect(50, 220, q, q);
  }
  
  if (gameOver) {
     noStroke();
     background(0);
     fill(255);
     text("Game Over", width/2 - 80, height/2 - 80);
     text("Press 'ENTER' to restart.", width/2 - 140, height/2 + 60);
     text("Press 'ESC' to exit.", width/2 - 140, height/2 + 100);
  }
  
  if (!gameOn) {
    noStroke();    
    fill(0, 60);
    rect(30, 30, 540, 420, 10);
    fill(255);
    text("Press <-- or --> to move the cubes.", 40, 200);
    text("If a chain of three same colors is formed, ", 40, 100);
    text("they can be eliminated!", 40, 140);
    text("Press 'P' to pause.", 40, 260);
    fill(#F0A5A5);
    text("Press 'ENTER' to start.", width/2-160, 360);
  }
  
  if (gameWin) {
     noStroke();
     background(#C9A4D8);
     fill(255);
     text("Congratulations! You win!", width/2 - 160, height/2 - 80);
     text("Press 'ENTER' to restart.", width/2 - 140, height/2 + 60);
     text("Press 'ESC' to exit.", width/2 - 140, height/2 + 100);
  }
}

void goToNextPiece() {
  piece = new Piece(nextPiece.kind);
  nextPiece = new Piece(-1);
}

void goToNextLevel() {
  score.addLevelPoints();
  level = 1 + int(nbLines / 100);
  dt *= .98;
}

void keyPressed() {
  if (key == CODED && gameOn) {
    switch(keyCode) {
    case LEFT:
    case RIGHT:
    case DOWN:
    case UP:
    case SHIFT:
      piece.inputKey(keyCode);
      break;
    }
  } else if (keyCode == 80) {
    if (gameOn) {
      gamePause = !gamePause;
      
      if (gamePause) {
        fill(0, 60);
        rect(width/2 - 200, height/2 - 50, 400, 100, 10);
        fill(255);
        text("Press 'P' to restart the game.", width/2 - 180, height/2);
        noLoop();
      } else if (!gamePause){
        loop();
      }
    }
    
  } else if (keyCode == ENTER) {
    if (gameOver) {
      gameOn = false;
      gameOver = false;
      loop();
    }
    
    if (!gameOn) {
      initialize();
      gameOver = false;
      gameOn = true;
      loop();
    }
  }  
}

 

class Piece {

  final color[] colors = {
    //color(#047E83), 
    //color(#760483), 
    //color(#830424), 
    color(#DE5410), 
    color(#368E6D), 
    color(#D380C1), 
    color(#E3A7AC)
  };

  final int[][] pos;
  int x = int(w/2);
  int y = 0;
  int kind;
  int c;
  int c0, c1, c2, c3, c4;

  Piece(int k) {
    if (k < 0) {
      kind = int(random(0, 4));
    } else {
      kind = k;
    }
    c = colors[kind];
    pos = pieces.pos[kind];
  }

  void display(Boolean still) {
    stroke(250);
    fill(c);
    pushMatrix();
    if (!still) {
      translate(160, 40);
      translate(x*q, y*q);
    }
    rect(pos[0][0] * q, pos[0][1] * q, 40, 40);

    popMatrix();
  }

  // returns true if the piece can go one step down
  void oneStepDown() {
    y += 1;
    if(!grid.pieceFits()){
      piece.y -= 1;
      grid.addPieceToGrid();
    }
  }

  // try to go one step left
  void oneStepLeft() {
    x --;
  }

  // try to go one step right
  void oneStepRight() {
    x ++;
  }

  void goToBottom() {
    grid.setToBottom();
  }

  void inputKey(int k) {
    switch(k) {
    case LEFT:
      x --;
      if(grid.pieceFits()){
      }else {
         x++; 
      }
      break;
    case RIGHT:
      x ++;
      if(grid.pieceFits()){
      }else{
         x--; 
      }
      break;
    case DOWN:
      oneStepDown();
      break;
    case SHIFT:
      goToBottom();
      break;
    }
  }

}



class Pieces {
  int[][][] pos = new int [4][1][2];
  
  Pieces() {
    pos[0][0][0] = -1;
    pos[0][0][1] = 0;
    pos[1][0][0] = -1;
    pos[1][0][1] = 0;
    pos[2][0][0] = -1;
    pos[2][0][1] = 0;
    pos[3][0][0] = -1;
    pos[3][0][1] = 0;
    //pos[4][0][0] = -1;
    //pos[4][0][1] = 0;
    //pos[5][0][0] = -1;
    //pos[5][0][1] = 0;
    //pos[6][0][0] = -1;  
    //pos[6][0][1] = 0;
  }
}
class Grid {
  int [][] cells = new int[w][h];

  Grid() {
    for (int i = 0; i < w; i ++) {
      for (int j = 0; j < h; j ++) {
        cells[i][j] = 0;
      }
    }
  }

  Boolean isFree(int x, int y) {
    if (x > -1 && x < w && y > -1 && y < h) {
      return cells[x][y] == 0;
    } else if (y < 0) {
      return true;
    }
    return false;
  }

  Boolean pieceFits() {
    int x = piece.x;
    int y = piece.y;
    int[][] pos = piece.pos;
    Boolean pieceOneStepDownOk = true;
    int tmpx = pos[0][0]+x;
    int tmpy = pos[0][1]+y;
    if (tmpy >= h || !isFree(tmpx, tmpy)) {
      pieceOneStepDownOk = false;
     }
    return pieceOneStepDownOk;
  }

  void addPieceToGrid() {
    int x = piece.x;
    int y = piece.y;
    int[][] pos = piece.pos;
      if(pos[0][1]+y >= 0){
        cells[pos[0][0]+x][pos[0][1]+y] = piece.c;
      }else{
        gameOn = false;
        gameOver = true;
        return;
      }
    checkSame();
    
    
    goToNextPiece();
    random_gen(int(random(0, 2)));
    checkSame();
    drawGrid();
    checkGame();
    
  }
  

  void checkSame() {
    int nb = 0;//number of full lines
    int mark_x = 0;
    int mark_y = 0;
    boolean same = false;
    
    // check horizontal
    for (int j = 0; j < h; j ++) {
      for (int i = 0; i < w; i++) {
        color c1 = cells[i][j];
        if ((i+2 < w) && (!isFree(i, j))) {
          if ((cells[i+1][j] == c1) &&
            (cells[i+2][j] == c1)) {
              int mark_num = 2;
              mark_x = i;
              mark_y = j;
              if ((i+3 < w) && (cells[i+3][j] == c1)) {
                mark_num = 3;
              }
              if ((i+4 < w) && (cells[i+4][j] == c1)) {
                mark_num = 4;
              }
              nb = nb + mark_num - 1;
              for (int k = mark_y; k > 0; k--) {
                for (int m = mark_x;  m <= mark_x + mark_num; m++) {
                  if ((j+2 < h) && (cells[m][j+1] == c1) &&
                  (cells[m][j+2] == c1)) { 
                    cells[m][j+1] = 0;
                    cells[m][j+2] = 0;
                  }
                  checkGame();
                  cells[m][k] = cells[m][k-1];
                  cells[m][0] = 0;
              continue;
             }           
            }
           }
      }
      if ((j+2 < h) && (!isFree(i, j))) {
          if ((cells[i][j+1] == c1) &&
          (cells[i][j+2] == c1)) {
            same = true;
            mark_x = i;
            mark_y = j;
           }           
          }
      if (same) {
        nb++;
        for (int k = mark_y; k < mark_y + 3; k++) {
          try{
          cells[mark_x][k] = 0;
          checkGame();
          } catch(Exception e) {           
          }
        }
        for (int k = mark_y; k > 2; k--) {
          cells[mark_x][k] = cells[mark_x][k-2];
        }
      } 
      }
      }
    deleteLines(nb);
    }
    
    
  Boolean checkWin() {
    if (isFree(x1, h-1)) {
      return true;
    }
    return false;
  }

  void deleteLines(int nb) {
    nbLines += nb;
    if (int(nbLines / 100) > level-1) {
      goToNextLevel();
    }
    score.addLinePoints(nb);
  }

  void setToBottom() {
    int j = 0;
    for (j = 0; j < h; j ++) {
      if (!pieceFits()) {
        break;
      } else {
        piece.y++;
      }
    }
    piece.y--;
    delay(1500);
    addPieceToGrid();
  }

  void drawGrid() {
    stroke(150);
    pushMatrix();
    translate(160, 40);
    line(-10, 0, -10, h*q);

    strokeWeight(sin(10/4-200)*6);
    stroke(0,0,10);
    for (int i = 0; i < w; i ++) {
      for (int j = 0; j < h; j ++) {
        if (cells[i][j] != 0) {
          fill(cells[i][j]);
          rect(i*q, j*q, q, q);
        }
      }
    }
    pick(x1, h-1);
    popMatrix();
  }
  
  void generate() {
    color[] colors = {
    //color(#047E83), 
    //color(#760483), 
    //color(#830424), 
    color(#DE5410), 
    color(#368E6D), 
    color(#D380C1), 
    color(#E3A7AC)
  };
     for (int i = 0; i < w; i ++) {
      for (int j = h * 2/3 ; j < h ; j ++) { // h * 3/4
        color c1 = colors[int(random(0, 4))];
        fill(c1);
        cells[i][j] = c1;
      }
    }
    checkSame();
  }
  
  void random_gen(int num) {
    color[] colors = {
    //color(#047E83), 
    //color(#760483), 
    //color(#830424), 
    color(#DE5410), 
    color(#368E6D), 
    color(#D380C1), 
    color(#E3A7AC)
  };
    int i=1, j=1;
    for (int k=0; k<=num; k++) {
      while (isFree(i, j+1) || !isFree(i, j)){
        i = int(random(1, w));
        j = int(random(1, h-1));
      }
      color c1 = colors[int(random(0, 4))];
      fill(c1);
      cells[i][j] = c1;
    }
    checkSame();
    
  }
  
  void checkGame() {
    for (int i=0; i<w; i++) {
      if (!isFree(i, 0)) {
        gameOver = true;
      }
    }    
    if (checkWin()) {
      gameWin = true;
    }
  }
  
  void pick(int x, int y) {
    stroke(#F8FC08);
    strokeWeight(5);
    fill(cells[x][y]);
    rect(x*q, y*q, q, q);
  }

}
  void display() {
    pushMatrix();
    translate(40, 60);

    //score
    fill(#F2D7D7);
    text("score: ", 0, 0);
    fill(230, 230, 12);
    text(""+formatPoint(points), 0, txtSize + 10);
    
    popMatrix();

  }

  String formatPoint(int p) {
    String txt = "";
    int qq = int(p/1000000);
    if (qq > 0) {
      txt += qq + ",";
      p -= qq * 1000000;
    }

    qq = int(p/1000);
    if (txt != "") {
      if (qq == 0) {
        txt += "000";
      } else if (qq < 10) {
        txt += "00";
      } else if (qq < 100) {
        txt += "0";
      }
    }
    if (qq > 0) {
      txt += qq;
      p -= qq * 1000;
    }
    if (txt != "") {
      txt += ",";
    }

    if (txt != "") {
      if (p == 0) {
        txt += "000";
      } else if (p < 10) {
        txt += "00" + p;
      } else if (p < 100) {
        txt += "0" + p;
      } else {
        txt += p;
      }
    } else {
      txt += p;
    }
    return txt;
  }
}

 

Plan:

For the next week, I plan to insert images and music to make the game visually better, and changing the display for the game rules,  game over and game clear.

I will also modify the calculations of the scores to be more reasonable.

 

 

Looking forward to your suggestions  🙂