0

The game is made using 2D arrays, 12 images are loaded; 6 of pairs to be exact. When a tile is clicked, then 1 is added to it, thus making it 2, thus revealing it. When 2 tiles are shown, and a third is clicked the first 2 are hidden.

My questions: How can I check if two revealed tiles are showing the same image? If they are showing the same images, how can I make sure they stay up and don't become hidden with 3rd tile is clicked?

I tried making some kind of counter that corresponds with the arrays, that didn't work at all. Neither did checking if one array was equal to another.

Any help would be appreciated. Thank you very, very much.

Here is my code for reference:

    // Number of columns and rows in the grid
int cols;
int rows;
int counter = 0; //counts how many are shown
boolean[] tracker = new boolean[12];
PImage [][] img = new PImage[4][3]; // load images

int[][] grid;

void setup() {
  size(600, 600);
  cols = height/4; //I want 4
  rows = width/3; //I want 3
  for (int m =0; m<tracker.length;m++){
        tracker[m]=false; //To laod array as false. Will be used to track numbers given out to make sure each every image is only put out once (but in reality I have pairs)
  }
  for (int i =0; i<img.length; i++) {
    for (int j=0; j<img[i].length;j++) {
      //int k = (j * 4 + i)%6;//2D array to 1D
      int k = (int)random(0,12);
      while(tracker[k]) //While it's true, pick 0-11 corepsonding to an image
      {
        k = (int)random(0,12);
      }
      tracker[k]=true; //Make it true, therefore can't be picked again
      img[i][j] = loadImage("img" + k + ".jpeg"); //Load the images
    }
  }
  grid = new int[cols][rows]; //To make the 2D array of 30 and 30
  for (int i=0; i<cols; i++) {
    for (int j=0; j<rows; j++) {
      grid[i][j] = 1; //If mouse clicked, will by multiplied by -1 to change if black or white
    }
  }
}

void draw() {
  rects(); //Calls the function to display rectangles
}

void rects() {
  for (int i=0; i<cols; i++) {
    for (int j=0; j<rows; j++) {
      if (grid[i][j]== 1) { //if grid at i,j is 1, change white
        fill(255); //white
        rect(cols*i, rows*j, cols, rows);
      }
      if (grid[i][j]== 2) { //if grid at i,j is -1, change black
        image(img[i][j], cols*i, rows*j);
        //rect(cols*0, rows*0, cols, rows);
      }
    }
  }
}

void mouseClicked() {
  for (int i=0; i<cols; i++) {
    for (int j=0; j<rows; j++) {
      if (mouseX<(i*cols)+cols && mouseX >= i*cols && mouseY<((j)*rows+rows) && mouseY >= (j)*rows) { // find square that is clicked then change color
        counter++;
        if (counter == 3) {
          for (int k=0; k<cols; k++) {
            for (int l=0; l<rows; l++) {
              grid[k][l]=1; //Making it 1 again, thus white
            }
          }
          counter =1;
        }
        grid[i][j] +=1; //By adding one, we make it show the imahe
      }
    }
  }
}
Charles
  • 50,943
  • 13
  • 104
  • 142
Abushawish
  • 1,466
  • 3
  • 20
  • 35

1 Answers1

3

You don't want to try and compare PImage as it is slow and not always accurate.

You don't want to store more images than you need, as this wastes memory. Only store unique images.

You want to separate the model from the view. You don't need to ever manipulate your images. You only need to know where they should be displayed and the procedure of searching for matches. The way you store your images should be separate from that logic.

I would recommend creating a "PImage" array of your 6 unique images. Then, create an "int" array with a size twice that of the number of unique images (since you want to have pairs). Lastly, create a "boolean" array with the same length as the "int" array.

So at setup, you will populate the image array (the "PImage" array) with 6 images, populate the label array (the "int" array) with the keys of the image array (0 through 5 repeated, i.e. {0,1,2,3,4,5,0,1,2,3,4,5), shuffle the label array, and populate the display array (the "boolean" array) with the "false" value.

When the user clicks a tile, you will determine the tile number (0 through 11), set the display of that tile number to true, and store the tile number as the first clicked. When the user clicks another tile, first check that it is a different tile number from the first clicked, then set the display to true, and store the tile number as the second clicked. Now that two have been clicked, check the label array for each tile number and see that they are equal. If so, you have a match, if not, there is no match and the first and second clicked are reset and the displays for those tile numbers set to false again.

In the example I have created below, there are two pde files, match and colors. The match.pde file includes everything you need to understand the label matching method. The colors.pde is only included as a method for generating random colors, you don't need to worry about it. The point is to show you the logic using colors so that you can then implement it using images.

match.pde

import java.util.Arrays; // used for shuffling
import java.util.Collections; // used for shuffling

int COLOR_COUNT = 6; //number of colors we will try to match

int WINDOW_X = 600;
int WINDOW_Y = 600;

//we will set these values during setup
int COLUMN_COUNT;
int ROW_COUNT;

color TILE_BACK_COLOR = color(255);
color TILE_EDGE_COLOR = color(0);

//we will set these values after we set the size of the window
int TILE_WIDTH;
int TILE_HEIGHT;

int ROUNDED_CORNER = 10;

//this is where we will store the colors
color[] COLORS = new color[COLOR_COUNT];

//the following must be Integer to allow for shuffle
//see http://stackoverflow.com/a/3981494/1736092
//this is where we will store the labels for the colors
Integer[] LABELS = new Integer[COLOR_COUNT*2]; 

//this is where we will store which tiles may be displayed
boolean[] DISPLAY = new boolean[COLOR_COUNT*2];

//this is where we will store the last 2 clicked tiles
int[] CLICKED = new int[2];

boolean MATCHED = false;
int MATCH_COUNT = 0;
int GUESS_COUNT = 0;

void setup() {
  /* calculate how many rows and columns we need based on number of tiles
   * since this is a matching game, number of tiles is twice the number of colors
   * we want a board that is as square as possible
   * take the square root of the number of tiles and round down (floor) and set that as rows
   * if rows divide evenly into the number of tiles, just divide tiles by rows to get columns
   * if not, keep subtracting a row until they divide evenly then get columns
   */
  println("Tiles: " + COLOR_COUNT*2);
  ROW_COUNT = floor(sqrt(COLOR_COUNT*2));
  println("Initial ROW_COUNT: " + ROW_COUNT);
  if((COLOR_COUNT*2) % ROW_COUNT == 0) {
    COLUMN_COUNT = (COLOR_COUNT*2) / ROW_COUNT;
    println("Keeping ROW_COUNT: " + ROW_COUNT + ", Setting COLUMN_COUNT: " + COLUMN_COUNT);
  } else {
    for(int i = ROW_COUNT - 1; i > 1; i--) {
      ROW_COUNT = i;
      if((COLOR_COUNT*2) % ROW_COUNT == 0) {
        COLUMN_COUNT = (COLOR_COUNT*2) / ROW_COUNT;
        println("Calculated ROW_COUNT: " + ROW_COUNT + ", Setting COLUMN_COUNT: " + COLUMN_COUNT);
      }
    }  
  }

  // make sure that the rows and columns are valid
  assert(COLUMN_COUNT * ROW_COUNT == COLOR_COUNT*2);

  size(WINDOW_X, WINDOW_Y);
  TILE_WIDTH = width/COLUMN_COUNT;
  TILE_HEIGHT = height/ROW_COUNT;

  populateColors(); // populate the colors
  newGrid(); // set up the initial grid

  background(0);
  drawGrid();
}

void draw() {
// //With no animations, this game is visually static
// //No need for a draw loop
}

void populateColors() {
  //for this game, we'll just generate random colors
  //however, you could change this function to set specific colors
  for(int i = 0; i < COLORS.length; i++) {
    COLORS[i] = getRandomColor();
  }  
}

//this function will create a new grid
//labels will be populated and shuffled
//clicks will be initialized to -1, which we will use to mean no click
void newGrid() {
  print("Initial LABELS[" + LABELS.length + "]:\t");

  for(int i = 0; i < LABELS.length; i++) {
    if(i < LABELS.length/2) LABELS[i] = i;
    else LABELS[i] = i-COLOR_COUNT;

    DISPLAY[i] = false;

    print(LABELS[i] + " ");
  }

  CLICKED[0] = -1;
  CLICKED[1] = -1;

  // shuffles the labels array
  Collections.shuffle(Arrays.asList(LABELS));

  print("\nShuffled LABELS[" + LABELS.length + "]:\t");
  for(int i = 0; i < LABELS.length; i++) print(LABELS[i] + " ");
  println();
}

//this just iterates through the grid and displays tiles as necessary
void drawGrid() {
  stroke(TILE_EDGE_COLOR); //set outline color
  for(int row = 0; row < ROW_COUNT; row++) {
    for(int col = 0; col < COLUMN_COUNT; col++) {
      int tile_number = col + row*(COLUMN_COUNT);
      if(DISPLAY[tile_number]) {
        fill(COLORS[LABELS[tile_number]]); //set fill color to that of tile front
      } else {
        fill(TILE_BACK_COLOR); //set fill color to that of tile back
      }
      //rect(top left x, top left y, width, height)
      rect(col*TILE_WIDTH, row*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT, ROUNDED_CORNER);
    }
  }
}

//this is called when two tiles have been clicked
//it checks the labels of the colors of the given tiles, not the actual colors 
void checkMatch() {
  //we want to make sure that both of the clicked tiles have been properly logged
  if(CLICKED[0] != -1 || CLICKED[1] != -1) {
    println("Comparing LABELS of COLORS[" + LABELS[CLICKED[0]] + "] and COLORS[" + LABELS[CLICKED[1]] + "]");
    if(LABELS[CLICKED[0]] == LABELS[CLICKED[1]]) {
      println("Colors match! MATCHED set to true and MATCH_COUNT++");
      MATCHED = true;
      MATCH_COUNT++;
      println("MATCH_COUNT now " + MATCH_COUNT);
      if(MATCH_COUNT == COLOR_COUNT) {
        println("MATCH_COUNT now equals COLOR_COUNT, board must be complete, executing win()"); 
        win();
      }
    } else {
      println("Colors do not match");
    }
  }  
}

//this funciton is called when a win condition has been met
void win() { 
  println("You Win! You made " + GUESS_COUNT + " tile flips, best possible is " + COLOR_COUNT*2);
  if(GUESS_COUNT == COLOR_COUNT*2) println("PERFECT GAME!");
  println("Press SPACE to generate a new board."); 
}

//this function is called when the user wants to reset the board
void reset() {
  println("Resetting the board");

  COLORS = new color[COLOR_COUNT];
  LABELS = new Integer[COLOR_COUNT*2]; 
  DISPLAY = new boolean[COLOR_COUNT*2];

  CLICKED = new int[2];
  MATCHED = false;
  MATCH_COUNT = 0;
  GUESS_COUNT = 0;

  populateColors();
  newGrid();
}

void mouseClicked() {
  println("Mouse Clicked");
  int clicked_column = mouseX/TILE_WIDTH;
  int clicked_row = mouseY/TILE_HEIGHT;
  int tile_number = clicked_column + clicked_row*(COLUMN_COUNT);
  println("Clicked: " + clicked_column + "," + clicked_row + " [" + tile_number + "]");

  //we don't want to allow clicking a tile that is already being displayed
  if(!DISPLAY[tile_number]) {
    GUESS_COUNT++;
    println("Guess count incremented to " + GUESS_COUNT);

    if(CLICKED[0] != -1 && CLICKED[1] != -1) {
      if(!MATCHED) {
        println("Set DISPLAY[" + CLICKED[0] + "] and DISPLAY[" + CLICKED[1] + "] to false");
        DISPLAY[CLICKED[0]] = false;
        DISPLAY[CLICKED[1]] = false;
      } else {
        println("Set MATCHED to false");
        MATCHED = false;  
      }
      CLICKED[0] = -1;
      CLICKED[1] = -1;
    }

    if(CLICKED[0] == -1 && CLICKED[1] == -1) {
      CLICKED[0] = tile_number;
      DISPLAY[tile_number] = true;
      println("Tile " + tile_number + " set as CLICKED[0], set DISPLAY[" + tile_number + "] to true");
    } else if(CLICKED[0] != -1 && CLICKED[1] == -1) {
      CLICKED[1] = tile_number; 
      DISPLAY[tile_number] = true;
      println("Tile " + tile_number + " set as CLICKED[1], set DISPLAY[" + tile_number + "] to true");
      checkMatch();
    } else {
      println("error in mouseClicked()");
    }
  }

  drawGrid();
  if(DISPLAY[tile_number]) {
    println("Tile " + tile_number + " is already being displayed");
    for(int i = 0; i < LABELS.length; i++) {
      print("-["+i+"]-");
      if(i != tile_number && LABELS[tile_number] == LABELS[i]) {

        break;  
      }
    }
  }

}

//allow user to reset the board by pressing SPACE
void keyPressed() {
  if(key == ' ') reset();
}

colors.pde

final float PHI = (1 + sqrt(5))/2;
float rand = random(0,1);

color HSVtoRGB(float h, float s, float v) {
  float r, g, b;
  if (s == 0) {
    r = v * 255;
    g = v * 255;
    b = v * 255;
  } else {
    float var_h = h * 6;
    float var_i = floor(var_h);
    float var_1 = v * (1 - s);
    float var_2 = v * (1 - s * (var_h - var_i));
    float var_3 = v * (1 - s * (1 - (var_h - var_i)));

    float var_r, var_g, var_b;

    if (var_i == 0) {var_r = v; var_g = var_3; var_b = var_1;}
    else if (var_i == 1) {var_r = var_2; var_g = v; var_b = var_1;}
    else if (var_i == 2) {var_r = var_1; var_g = v; var_b = var_3;}
    else if (var_i == 3) {var_r = var_1; var_g = var_2; var_b = v;}
    else if (var_i == 4) {var_r = var_3; var_g = var_1; var_b = v;}
    else {var_r = v; var_g = var_1; var_b = var_2;}

    r = var_r * 255;
    g = var_g * 255;
    b = var_b * 255;
  }
  return color(r, g, b);
}

// returns a random color
color getRandomColor() {
  //0.25,0.8
  float sat = 0.65;
  float val = 0.6;
  rand += PHI - 1;
  rand %= 1;
  //this is a custom function to convert HSV to RGB
  return HSVtoRGB(rand, sat, val);
}

As an aside, you don't have to use the Java shuffle method, especially if you want to avoid using the Java libraries and stick with Processing only. One popular method is the Fisher-Yates Shuffle. http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

spex
  • 1,110
  • 10
  • 21
  • Thank you very much, mind explaing this however: int column_count = floor(counter*2/sqrt(counter*2)); Why floor and why squareroot? Thanks! – Abushawish Nov 29 '12 at 03:09
  • You can safely ignore that line. I was just dynamically figuring out how many columns and rows to set for a given number of colors. If you have 6 colors, then you have 12 tiles (6*2 since you are matching pairs of colors), and needed to know how many rows and columns to set. Since we're setting the tiles out in a square, square root made sense. However, it's not ideal, for example it goes bad when you have 5 colors. You already know how many rows and columns you want, so you could just set them directly instead of trying to calculate them. I'll rewrite that bit, but you can ignore it. – spex Nov 29 '12 at 18:49
  • Tidied up some bits of the code and removed the confusing row and column calculation. It should be much clearer now. – spex Nov 29 '12 at 19:43
  • Also, don't forget to click the check mark next to my answer if you accept it as answering your question. – spex Nov 29 '12 at 19:56
  • Thank you very much, I will make sure to do that! Thanks again :)! – Abushawish Dec 03 '12 at 05:33
  • Thanks! I improved the code if you're interested. Otherwise, good luck on future Processing sketches! – spex Dec 03 '12 at 14:21