How do I interact with a grid using a 2D array (Proce55ing)

355 views Asked by At

For a project I need to create Connect Four using Processing, I have created the grid which the game will be played in however I am lost when it comes to players interacting with it.

I have been told to use a 2D array however my knowledge of arrays is very limited. I am currently trying to code the bit where the program detects where a player has clicked and spawning a coin there.

int c = 8;
int r = 10;
int[][] grid = new int [c][r];
int CoinSpawn = -1;

void setup () {
  size(1000, 800);
}
void draw () {
  background(1, 1, 1);
  translate(100, 100);
  noStroke();
  drawColumns();
  drawRows();
}
void keyPressed () {
  for (int i=0; i<grid.length-1; i++) {
    grid[i][i] = grid[i+1][i+1];
  }
}
void drawRows(){
  for (int i=0; i < r; i++){
   int x = 80;
   x = x * i;
   translate(x,0);
   drawColumns();
   translate(-x,0);
  }
}

void drawColumns(){
 for (int i=0; i < c; i++){
  int y = 80;
  y = y * i;
  translate(0,y);
  drawCell();
  translate(0,-y);
 }
}

void drawCell(){
  fill(0,0,255);
  rect(0,0,80,80);
  noFill();
  fill(0);
  ellipseMode(CENTER);
  translate(40,40);
  ellipse(0,0,75,75);
  noFill();
  translate(-40,-40);
}

Would I be able to assign the 2D array to the grid? so that each slot in the grid represents an element from the array? That is the best option I can see at the moment however I have no idea how to do it.

I really appreciate any replies as I am completely lost at the moment.

1

There are 1 answers

0
Patrick Murphy On

You have a pretty good start, I made a lot more changes than I had planned... got a little into it!

Let me know if you have any questions, I did use one simple OOP class called cell that tracks the value and the cell's position, as well as provides a display function, I converted a lot of your variables and hard coded numbers to constants (starts with final and has the same value for the entire program)

you will notice I left the win conditions to you!

I hope this is helpful, I feel like seeing 2D arrays used in a context you are familiar with might help you understand them!

My normal process using 2D arrays for processing: use Setup() to set initial array values use Draw() to call each of the item's display function use other functions to modify the array data, which the display function of the cell knows how to display

Main File

// Grid Size constants
final int GRID_COLUMNS = 10;
final int GRID_ROWS = 8;

// Display constants

final int CELL_SCALE = 80;
final int COIN_RADIUS = 75;
final int BOARD_PADDING = 100;
final color[] PLAYER_COLORS = new color[]{color(0), color(200, 10, 10), color(200, 200, 10)};

// cell values
final int EMPTY_CELL = 0;
final int PLAYER_1 = 1;
final int PLAYER_2 = 2;

// game data
Cell[][] grid = new Cell [GRID_COLUMNS][GRID_ROWS];
int currentPlayer;

void setup () {
  size(1000, 800);
  ellipseMode(CENTER); // only needs to be set once per sketch

  setupBlankBoard();
}

// method to populate the array with blank cells, used to initiate and reset the game
void setupBlankBoard() {
  currentPlayer = PLAYER_1; // reset game and start with first player
  // populate array
  for (int y=0; y < GRID_ROWS; y++) { // for every vertical row,
    for (int x=0; x < GRID_COLUMNS; x++) { // for every horizontal cell in that row

      // rather than translates I prefer to calculate the actual x,y position and just display it there,
      // we add the padding offset to every cell, and then take the column/row times the width of the square cell
      grid[x][y] = new Cell(BOARD_PADDING+x*CELL_SCALE, BOARD_PADDING+y*CELL_SCALE);
    }
  }
}

void changePlayers() {
  if (currentPlayer == PLAYER_1) {
    currentPlayer = PLAYER_2;
  } else {
    // already was player 2, so change to 1
    currentPlayer = PLAYER_1;
  }
}

boolean placeCoin(int column) {
  boolean coinPlaced = false;

  // now we know the column, we need to find the lowest cell in that column that is empty
  for (int y = GRID_ROWS-1; y >= 0; y-- ) { // let's start at bottom and move up to reduce computations
    // for every cell, test if it is empty
    if (grid[column][y].isEmpty()) {
      // if found a valid cell,  place current players token and exit the loop (break)
      grid[column][y].setValue(currentPlayer);
      coinPlaced = true;
      break;
    }
  }

  return coinPlaced;
}

void checkCoins() {
  // scan the array for 4 of the same value in a row
  for (int y=0; y < GRID_ROWS; y++) { // for every vertical row,
    for (int x=0; x < GRID_COLUMNS; x++) { // for every horizontal cell in that row
      //grid[x][y]
      // I will leave this to you ;) 
      // keep in mind to check neighbors all you need to do is add or subtract 1 from the x or y value
      // however when you are at the edge of the board be careful not to try and look at a neighbor that is off the edge, you will get a null pointer or an array out of bounds exception
      if (x+1<GRID_COLUMNS) {
        Cell toTheRight = grid[x+1][y];
      }

      // for each cell you could look at the 3 above the current, 3 below the current, 3 to the right and 3 to the left, 3 diagnal in each direction and then manually try and find 4 adjacent same color cells
      // or a bit more complicated is a recursive solution that checks its 8 immediate neighbor and for each that match the center cell run the same function to test its 8 neighbors keeping track of the current inARowCount and returning true when it is found.
      // would be a bit hard because you would need to make sure the second cell doesn't follow back to the original cell, and start an endless loop
    }
  }
}

void draw () {
  background(1, 1, 1);
  noStroke();

  // draw all cells
  for (int y=0; y < GRID_ROWS; y++) { // for every vertical row,
    for (int x=0; x < GRID_COLUMNS; x++) { // for every horizontal cell in that row
      grid[x][y].display(); // draw this cell
    }
  }

  // draw next coin floating above the board, contrain its positon to above the board
  fill(PLAYER_COLORS[currentPlayer]);
  int currentMouseX = constrain(mouseX, BOARD_PADDING+COIN_RADIUS/2, BOARD_PADDING+(GRID_COLUMNS*CELL_SCALE)-COIN_RADIUS/2);
  //currentMouseX = 40*(ceil(abs(currentMouseX/40)));
  ellipse(currentMouseX, BOARD_PADDING/2, COIN_RADIUS, COIN_RADIUS);
}

// press any key to rest the game, probably want to test for a certain key here!
void keyPressed () {
  setupBlankBoard();
}

// on press attempt to place a coin
void mousePressed() {
  int currentMouseX = constrain(mouseX, BOARD_PADDING+COIN_RADIUS/2, BOARD_PADDING+(GRID_COLUMNS*CELL_SCALE)-COIN_RADIUS/2);

  // determine what column we are over
  int column = (currentMouseX - BOARD_PADDING)/CELL_SCALE;

  // if choice is a valid coin placement
  if (placeCoin(column)) {
    // toggle players if a coin was placed
    changePlayers();
    // after each placement check win conditions
    checkCoins();
  }
}

Cell Class

class Cell {
  int x, y;
  int value; // 0 for empty, 1 for team 1, 2 for team 2 (constants defined at top of main file)

  Cell(int x, int y) {
    // default constructor to create empty cell
    this(x, y, EMPTY_CELL);
  }

  // allows cell value to be set at creation
  Cell(int x, int y, int value) {
    this.x = x;
    this.y = y;
    this.value = value;
  }

  boolean setValue(int value){
    value = constrain(value, EMPTY_CELL,PLAYER_2); // keeps it from setting a value outside of our range

    if(this.value == EMPTY_CELL){
      this.value = value;
      return true; // placed
    }

    return false; // was not able to place it as there is already a value
  }

  boolean isEmpty(){
    return this.value == EMPTY_CELL;
  }

  void display() {
    fill(0, 0, 255);
    rect(this.x, this.y, CELL_SCALE, CELL_SCALE);

    // Draw Circle color based on current value, could simply just put fill(playerColors[this.value]); but this seems a bit more clear
    if(this.value == EMPTY_CELL){
      fill(PLAYER_COLORS[EMPTY_CELL]);
    }else if(this.value == PLAYER_1){
      fill(PLAYER_COLORS[PLAYER_1]); // red
    }else if(this.value == PLAYER_2){
      fill(PLAYER_COLORS[PLAYER_2]); // yellow
    }

    ellipse(this.x + CELL_SCALE/2, this.y + CELL_SCALE/2, COIN_RADIUS, COIN_RADIUS);
  }
}