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  🙂

2 thoughts on “Midterm Prototype”

  1. Lots of great work Jade. I’m a little confused by what is going on though. It looks like when you land a piece sometimes things disappear and sometimes things just randomly appear. It probably has something to do with the “goal” of the pink piece? It’ll be good when you have your instructions page, but perhaps there is something you can do design-wise in the actual game to make it more clear? Maybe there is some sort of positive feedback when you do the “right” thing (both visually and sound wise). And then likewise, something that would make the sudden appearance of blocks make sense if you do the wrong thing (which I’m assuming is what makes the blocks appear?).

    1. Thank you for your suggestions Professor Aaron! The sudden appearance and disappearance of blocks is because I designed a mechanism to random generate blocks so that the game can progress. I also agree that I should add some effects to suggest the changes. Your advices are very helpful!

Leave a Reply