[Week 6] Midterm project – Ideas & initial progress

For the midterm, coming up with a concrete idea took me quite a while. My initial plan was to continue developing the 2048 game that I made for week 3, but I found that once finished, there is not much room for tweaking the game any further. So I changed my direction and was hoping to use the tile-based premise (like what I used for 2048) and add in character movement (using sprite sheets) and probably a reward system with trading options for the midterm. What I settled on is a game I temporarily name Minehunter.

Idea

As the name suggest, the inspiration for the game comes from the classic game Minesweeper. A quick recap of rules can be found here but I am taking much liberty with it and will not strictly follow these rules. My goal is to use the underlying premise of Minesweeper and add to the user interaction aspect as well as complexity of the game (that is, winning is harder). I plan to implement the following features to achieve this:

  1. Instead of mouse clicking on game cells, the player has an avatar on the game board (the hunter) that they move around using arrow keys. In order to reveal a cell (that the player is sure to be a safe cell) or to plant a flag (that the player is sure to be a mine), the player will have to move their avatar to the specific cell and press corresponding keys.
  2. Once a safe cell is revealed, according to the formal rules, a number is shown representing the number of nearby mines (within the vicinity of 9 cells). I plan to change this so that the number will disappear once the player moves to a different cell, and will only reappear if the player toggles the cell again. This will increase the complexity of the game since the player will need to remember the figures instead of having them being shown permanently on the screen. (This is a loose idea for now, since I’m uncertain on whether it should be a feature.)
  3. My plan is to implement a reward system consisting of points that the player can trade for help. Specifically, the player will start the game with a default of 10 points. For each flag the player manages to plant on a correct mine cell, 5 points are added. There will be a game agent capable of making safe moves (revealing non-mine cells) that the player can trigger, but at the price of having 5 points deducted. This means that if the player has 0 point left, they can’t ask for help from the agent until they successfully flag a mine and earn more points.

Progress

Settling on an idea took me a while, so I have not had much progress with the actual implementation. So far I have had a basic Minehunter class for implementing the game board, which is currently an 8×8 board with 8 mines randomly scattered across the board. The function numNeighborMines() has been written for calculating the number of nearby mines of a particular cell. I also have a basic main program to quickly visualize the game board and test out the functions I’ve written. When running the current program, pressing a random key will reveal the positions of the mines (in darker color) and the number of nearby mines for each of the non-mine cells. So far, the functions seem to run correctly and without errors.

Development

Here is an outline of the main game features that I will need to implement soon in order to build the rest of the game:

  • The Player class that will display the player’s avatar and allow the player to move around and interact with the game board.
  • The Reward class that will keep track of the player’s points and adjust according to the player’s actions.
  • The Agent class capable of making safe moves.
  • The GameState class for coordinating the game, reseting the game board, etc.

Screenshots & code

Here is a snapshot of my progress so far with initializing the game board:

Minehunter class

class Minehunter {
  int numRow = 8;
  int numCol = 8;
  int numMines = 8;
  boolean board[][] = new boolean[numRow][numCol];
  ArrayList<String> mines = new ArrayList<String>();
  ArrayList<String> minesFound = new ArrayList<String>();

  Minehunter() {
    // Initializing gameboard with no mine
    for (int i=0; i<numRow; i++) {
      for (int j=0; j<numCol; j++) {
        board[i][j] = false;
      }
    }

    // Formula for encoding coordinates:
    // (i, j): i = row coordinate, j = col coordinate
    // i, j <= 7 so can encoding in the form 'ij'
    // e.g. '56' = (5, 6) = row 5, col 6

    // Adding random mines (max 8) to gameboard
    while (mines.size() != numMines) {
      int i = int(random(numRow));
      int j = int(random(numCol));
      if (board[i][j] == false) {
        board[i][j] = true;
        mines.add(tupleToString(i, j));
      }
    }
  }
  
  int numNeighborMines(int row, int col) {
    int count = 0;
    for (int i=row-1; i<row+2; i++) {
      for (int j=col-1; j<col+2; j++) {
        if (i==row && j==col) {
          continue;
        } else {
          if (0 <= i && i < numRow && 0 <= j && j < numCol) {
            if (board[i][j] == true) {
              count += 1;
            }
          }
        }
      }
    }
    return count;
  }
  
  boolean isMine(int i, int j) {
    return board[i][j];
  }
  
  boolean gameWon() {
    return minesFound.size() == mines.size();
  }

  /**
   * Print a textual form of the board
   * For testing purposes
   */
  void printBoard() {
    for (int i=0; i<numRow; i++) {
      for (int j=0; j<numCol; j++) {
        if (board[i][j] == true) {
          print("X");
        } else {
          print("_");
        }
      }
      println();
    }
  }

  String tupleToString(int i, int j) {
    return str(i)+str(j);
  }
  
  /**
  * Coordinate in the form of 'ij'
  * i = ij / 10
  */
  int stringToX(String coordinate) {
    return int(int(coordinate) / 10);
  }
  
  int stringToY(String coordinate) {
    return int(int(coordinate) % 10);
  }
  
  void testMinehunter() {
    //println(stringToX("00"), stringToY("00"));
    //println(numNeighborMines(4, 4));
  }
}

Main program

Minehunter minehunter;
float padding = 20;
float boardDim = 8;
float boardWidth, boardHeight, cellSize;
color cellColor = color(238, 228, 218);
color tmpBomb = color(246, 124, 96);
boolean showingMines = false;

void setup() {
  fullScreen();
  boardWidth = (float(2)/float(3) * width) - (padding * 2);
  boardHeight = height - (padding * 2);
  cellSize = int(min((boardWidth / boardDim), (boardHeight / boardDim)));
  minehunter = new Minehunter();
}

void displayBoard() {
  translate(padding, padding);
  for (int i=0; i<boardDim; i++) {
    for (int j=0; j<boardDim; j++) {
      stroke(255);
      strokeWeight(2);
      if (showingMines == true && minehunter.isMine(i, j)) {
        fill(tmpBomb);
      } else {
        fill(cellColor);
      }
      rect(i*cellSize, j*cellSize, cellSize, cellSize);
      if (showingMines == true && !minehunter.isMine(i, j)) {
        pushMatrix();
        translate(cellSize*.5, cellSize*.5);
        fill(0);
        textSize(cellSize*.4);
        textAlign(CENTER, CENTER);
        text(str(minehunter.numNeighborMines(i, j)), i*cellSize, j*cellSize);
        popMatrix();
      }
    }
  }
}

void draw() {
  background(255);
  displayBoard();
}

void keyPressed() {
  if (keyPressed) {
    showingMines = !showingMines;
  }
}

 

Leave a Reply