Land mine Download

Roger Jaffe

MVC, messaging, objects Student approved


Introduction

Goals: There are three goals for this project:

  1. Create a Land Mine computer game
  2. Work as a team of software developers
  3. Use Github for a source control system

Before starting, you may need to refresh your memory about concepts we've discussed a few weeks ago:

Code template: The MVC framework that you experimented with in the last unit will be the code template for this project. Fork your template code from this repository to create your repository.

Team meetings: At the beginning of the period each day your team will have a short (5 minutes or so) meeting where you will describe to each other the work you did for the last 24 hours, the work you plan to do today, and how your Java class (Controller, View, or Model) will interact with the other two classes. This is your time to make sure that you are all on the same page and understand what the others are doing.

Grading: Your game will be assessed on the following things:

Programs that meet the requirements of the game description above can get a maximum of a ‘3’ on the Overall “coolness” score. Some things that your team can do to get a ‘5’ on “coolness” are:


Task

Game description: In this project you and your team will create a video game called Land Mine!

Here’s how the game works:

  1. The Board is arranged in an 8x8 grid (like a checkerboard).
  2. The 8x8 board will create 64 individual cells
  3. Each cell will either be empty, or contain a landmine that will explode when the cell is uncovered.
  4. There should be 10 landmines placed randomly on the board (see note below about random numbers)
  5. The player uncovers one cell at a time. a. If the cell is empty, the player receives one point  b. If the cell contains a landmine, the player explodes and loses one life
  6. When the player loses 3 lives, the game is over
  7. The game should have a leaderboard that is displayed between games a. If a player gets a high score, the game should ask for his or her name which will be displayed on the leaderboard.

Group planning and discussion: Before you write a single line of code, you need to get together as a group and decide on the methods and instance data that will be required by the Model, View, and Controller classes.

For instance, the Model class will need a method called (for example) clearBoardwhich will set all the cells in the array to false. You will also need a method called randomizeMines(number)which will randomly place some number of mines on the board.

You'll also need to agree on the messaging protocol that will be used to communicate in the MVC framework. For instance, the View could send the message exposeCellwith a message payload of the cell number as so that the Controller could tell the Model to modify the app's data accordingly.

Division of effort: Each team will consist of three people with the following responsibilities:


Starter code

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package com.mrjaffesclass.apcs.mvc.template;

/**
 *
 * @author Roger
 */
public class Constants {
    // Constants
  public static final int UP = 1;
  public static final int DOWN = -1;


}
package com.mrjaffesclass.apcs.mvc.template;
import com.mrjaffesclass.apcs.messages.*;

/**
 * 
 * The Controller is the master of the App you're writing. It instantiates the
 * view and the model, receives messages from the View in response to user
 * interface (UI) actions like clicking a button, changing an input field, 
 * etc.  It also sends and receives messages to the Model to commuincate
 * changes required and changes made to the Model variables.  
 *
 * @author Roger Jaffe
 * @version 1.0
 */
public class Controller implements MessageMailbox {

  private final Messaging mvcMessaging;

  /**
   * Controller constructor The Controller is responsible for creating the View
   * and the Model that it will be controlling. The mvcMessaging object is
   * passed to the view and the model and is used as a local messenger
   * between the Controller, Model and View without have direct access to the
   * View and Model.  Remember, you want the three components separated so
   * that one class works independently of the others.
   *
   * Messages that can be received in the Controller:
   *  view:toggleButtonClick (sent by the View when the toggle button is clicked)
   *  view:buttonClick (sent by the View when the regular button is clicked)
   *  view:changeButton (sent by the View when the Up or Down buttons are clicked)
   * Message that are sent from the Controller:
   *  controller:changeButton (sent by the Controller to notify the Model to change 
   *    the value of a Model variable
   */
  public Controller() {
    // Create the local messaging class
    mvcMessaging = new Messaging();

    // Create the view and set it visible
    View view = new View(mvcMessaging);    // This creates our view
    view.init();
    view.setVisible(true);

    // Create the model
    Model model = new Model(mvcMessaging);  // This creates our model
    model.init();
  }

  /**
   * Initialize the model here and subscribe
   * to any required messages
   * 
   * "this" refers to this controller object.
   */
  public void init() {
    mvcMessaging.subscribe("view:toggleButtonClick", this);
    mvcMessaging.subscribe("view:buttonClick", this); 
    mvcMessaging.subscribe("view:changeButton", this);
  }

  @Override
  public void messageHandler(String messageName, Object messagePayload) {
    // Only handle the changeValue1 and changeValue2 messages
    if (messagePayload != null) {
      System.out.println("RCV (controller): "+messageName+" | "+messagePayload.toString());
    } else {
      System.out.println("RCV (controller): "+messageName+" | No data sent");
    }
    if (messageName.equals("view:changeButton")) {
      mvcMessaging.notify("controller:changeButton", messagePayload, true);          
    }
  }

  /**
   * Program entry -- main is called when the program starts
   *
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    Controller app = new Controller();  // Create our controller...
    app.init();                         // ...and init it too
  }
  
}
package com.mrjaffesclass.apcs.mvc.template;
import java.util.*;
import com.mrjaffesclass.apcs.messages.*;
/**
 * MVC Template
 * This is a template of an MVC framework used by APCS for the 
 * LandMine project (and others)
 * @author Roger Jaffe
 * @version 1.0
 * 
 */
public class View extends javax.swing.JFrame implements MessageMailbox {

  private final Messaging mvcMessaging;
  
  /**
   * Creates a new view
   * @param messages mvcMessaging object
   */
  public View(Messaging messages) {
    mvcMessaging = messages;   // Save the calling controller instance
    initComponents();           // Create and init the GUI components
  }
  
  /**
   * Initialize the model here and subscribe
   * to any required messages
   */
  public void init() {
    // Subscribe to messages here
    mvcMessaging.subscribe("model:variable1Changed", this);
    mvcMessaging.subscribe("model:variable2Changed", this);
  }
  
  @Override
  public void messageHandler(String messageName, Object messagePayload) {
    if (messagePayload != null) {
      System.out.println("RCV (view): "+messageName+" | "+messagePayload.toString());
    } else {
      System.out.println("RCV (view): "+messageName+" | No data sent");
    }
    if (messageName.equals("model:variable1Changed")) {
      jLabel8.setText(messagePayload.toString());
    } else {
      jLabel10.setText(messagePayload.toString());      
    }
  }

  /**
   * Instantiate an object with the field number that was clicked (1 or 2) and
   * the direction that the number should go (up or down)
   * @param fieldNumber 1 or 2 for the field being modified
   * @param direction this.UP (1) or this.DOWN (-1), constants defined above
   * @return the HashMap payload to be sent with the message
   */
  private MessagePayload createPayload(int fieldNumber, int direction) {
    MessagePayload payload = new MessagePayload(fieldNumber, direction);
    return payload;
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // //GEN-BEGIN:initComponents
  private void initComponents() {

    jButton1 = new javax.swing.JButton();
    jLabel1 = new javax.swing.JLabel();
    jToggleButton1 = new javax.swing.JToggleButton();
    jButton2 = new javax.swing.JButton();
    textField = new javax.swing.JTextField();
    fillButton = new javax.swing.JButton();
    jLabel2 = new javax.swing.JLabel();
    jLabel3 = new javax.swing.JLabel();
    jLabel4 = new javax.swing.JLabel();
    jLabel5 = new javax.swing.JLabel();
    jLabel6 = new javax.swing.JLabel();
    jLabel7 = new javax.swing.JLabel();
    jLabel8 = new javax.swing.JLabel();
    jLabel9 = new javax.swing.JLabel();
    jLabel10 = new javax.swing.JLabel();
    jLabel11 = new javax.swing.JLabel();
    upButton1 = new javax.swing.JButton();
    upButton2 = new javax.swing.JButton();
    downButton1 = new javax.swing.JButton();
    downButton2 = new javax.swing.JButton();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    jButton1.setBackground(new java.awt.Color(0, 0, 0));
    jButton1.setText("Click me!");
    jButton1.setToolTipText("Click me to see the View send a message to the Controller to let it know that the button was pushed");
    jButton1.setActionCommand("button1Clicked");
    jButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        jButton1buttonClickActionPerformed(evt);
      }
    });

    jLabel1.setText("MVC Framework Template");

    jToggleButton1.setText("Toggle button!");
    jToggleButton1.setActionCommand("toggleButtonClicked");
    jToggleButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        jToggleButton1ActionPerformed(evt);
      }
    });

    jButton2.setText("No, click me!!");
    jButton2.setActionCommand("noClickMeButton!");
    jButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        jButton2buttonClickActionPerformed(evt);
      }
    });

    textField.setText("Empty");

    fillButton.setText("New text goes here");
    fillButton.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        fillButtonActionPerformed(evt);
      }
    });

    jLabel2.setText("Click the button to move the input text to the button");

    jLabel3.setText("Input text:");

    jLabel4.setText("Button:");

    jLabel5.setText("Clicking these buttons will");
    jLabel5.setMaximumSize(new java.awt.Dimension(45, 60));

    jLabel6.setText("send a message to the console");

    jLabel7.setText("Number 1:");

    jLabel8.setText("jLabel8");

    jLabel9.setText("Number 2:");

    jLabel10.setText("jLabel8");

    jLabel11.setText("Use the buttons to control the numbers");

    upButton1.setText("Up");
    upButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        up1ButtonActionPerformed(evt);
      }
    });

    upButton2.setText("Up");
    upButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        upButton2ActionPerformed(evt);
      }
    });

    downButton1.setText("Down");
    downButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        downButton1ActionPerformed(evt);
      }
    });

    downButton2.setText("Down");
    downButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        downButton2ActionPerformed(evt);
      }
    });

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(jLabel3)
              .addComponent(jLabel4))
            .addGap(18, 18, 18)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(fillButton)
              .addComponent(textField, javax.swing.GroupLayout.PREFERRED_SIZE, 229, javax.swing.GroupLayout.PREFERRED_SIZE)))
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(jLabel1)
              .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
              .addComponent(jLabel6))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(layout.createSequentialGroup()
                .addComponent(jButton1)
                .addGap(34, 34, 34))
              .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jButton2)
                .addComponent(jToggleButton1))))
          .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 343, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addGroup(layout.createSequentialGroup()
            .addComponent(jLabel7)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(jLabel8)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(upButton1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(downButton1))
          .addGroup(layout.createSequentialGroup()
            .addComponent(jLabel9)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(jLabel10)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(upButton2)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(downButton2))
          .addComponent(jLabel11))
        .addContainerGap(51, Short.MAX_VALUE))
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addComponent(jButton1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jToggleButton1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jButton2))
          .addGroup(layout.createSequentialGroup()
            .addGap(7, 7, 7)
            .addComponent(jLabel1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jLabel6)))
        .addGap(18, 18, 18)
        .addComponent(jLabel2)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(textField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(jLabel3))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(fillButton)
          .addComponent(jLabel4))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 12, Short.MAX_VALUE)
        .addComponent(jLabel11)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel7)
          .addComponent(jLabel8)
          .addComponent(upButton1)
          .addComponent(downButton1))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel9)
          .addComponent(jLabel10)
          .addComponent(upButton2)
          .addComponent(downButton2)))
    );

    pack();
  }// //GEN-END:initComponents

  private void jButton1buttonClickActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1buttonClickActionPerformed
    // Send a message to the controller!
    mvcMessaging.notify("view:buttonClick", null, true);
  }//GEN-LAST:event_jButton1buttonClickActionPerformed

  private void jToggleButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleButton1ActionPerformed
    // Send a message to the controller!
    mvcMessaging.notify("view:toggleButtonClick", null, true);
  }//GEN-LAST:event_jToggleButton1ActionPerformed

  private void jButton2buttonClickActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2buttonClickActionPerformed
    // Send a message to the controller!
    mvcMessaging.notify("view:buttonClick", null, true);
  }//GEN-LAST:event_jButton2buttonClickActionPerformed

  private void fillButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fillButtonActionPerformed
    fillButton.setText(textField.getText());
  }//GEN-LAST:event_fillButtonActionPerformed

  /**
   * Handler for the up button for field 1
   * @param evt 
   */
  private void up1ButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_up1ButtonActionPerformed
    // Increment the 1st data field
    mvcMessaging.notify("view:changeButton", createPayload(1, Constants.UP), true);
  }//GEN-LAST:event_up1ButtonActionPerformed

  private void upButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_upButton2ActionPerformed
    // Increment the 2nd data field
    mvcMessaging.notify("view:changeButton", createPayload(2, Constants.UP), true);
  }//GEN-LAST:event_upButton2ActionPerformed

  private void downButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_downButton1ActionPerformed
    // Decrement the first data field
    mvcMessaging.notify("view:changeButton", createPayload(1, Constants.DOWN), true);
  }//GEN-LAST:event_downButton1ActionPerformed

  private void downButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_downButton2ActionPerformed
    // Decrement the second data field
    mvcMessaging.notify("view:changeButton", createPayload(2, Constants.DOWN), true);
  }//GEN-LAST:event_downButton2ActionPerformed

  /**
   * @param args the command line arguments
   */

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton downButton1;
  private javax.swing.JButton downButton2;
  private javax.swing.JButton fillButton;
  private javax.swing.JButton jButton1;
  private javax.swing.JButton jButton2;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel10;
  private javax.swing.JLabel jLabel11;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JLabel jLabel3;
  private javax.swing.JLabel jLabel4;
  private javax.swing.JLabel jLabel5;
  private javax.swing.JLabel jLabel6;
  private javax.swing.JLabel jLabel7;
  private javax.swing.JLabel jLabel8;
  private javax.swing.JLabel jLabel9;
  private javax.swing.JToggleButton jToggleButton1;
  private javax.swing.JTextField textField;
  private javax.swing.JButton upButton1;
  private javax.swing.JButton upButton2;
  // End of variables declaration//GEN-END:variables
}
package com.mrjaffesclass.apcs.mvc.template;

/**
 * This is the message payload class.  Instantiate this class when sending
 * field / value message data for the up/down buttons
 * 
 * @author Roger Jaffe
 * @version 1.0
 */
public class MessagePayload {
  
  private final int field;
  private final int direction;
  
  /**
   * Class constructor
   * @param _field Text field 1 or 2
   * @param _direction Direction (Constants.UP or Constants.DOWN)
   */
  public MessagePayload(int _field, int _direction) {
    field = _field;
    direction = _direction;
  }
  
  /**
   * Getter method for the direction
   * @return Field value
   */
  public int getDirection() {
    return direction;
  }
  
  /**
   * Getter method for the 
   * @return 
   */
  public int getField() {
    return field;
  }
  
}
package com.mrjaffesclass.apcs.mvc.template;

import java.util.*;
import com.mrjaffesclass.apcs.messages.*;

/**
 * The model represents the data that the app uses.
 * @author Roger Jaffe
 * @version 1.0
 */
public class Model implements MessageMailbox {

  // Messaging system for the MVC
  private final Messaging mvcMessaging;

  // Model's data variables
  private int variable1;
  private int variable2;

  /**
   * Model constructor: Create the data representation of the program
   * @param messages Messaging class instantiated by the Controller for 
   *   local messages between Model, View, and controller
   */
  public Model(Messaging messages) {
    mvcMessaging = messages;
  }
  
  /**
   * Initialize the model here and subscribe to any required messages
   */
  public void init() {
    mvcMessaging.subscribe("controller:changeButton", this);
    setVariable1(10);
    setVariable2(-10);
  }
  
  @Override
  public void messageHandler(String messageName, Object messagePayload) {
    if (messagePayload != null) {
      System.out.println("RCV (model): "+messageName+" | "+messagePayload.toString());
    } else {
      System.out.println("RCV (model): "+messageName+" | No data sent");
    }
    MessagePayload payload = (MessagePayload)messagePayload;
    int field = payload.getField();
    int direction = payload.getDirection();
    
    if (direction == Constants.UP) {
      if (field == 1) {
        setVariable1(getVariable1()+Constants.UP);
      } else {
        setVariable2(getVariable2()+Constants.UP);
      }
    } else {
      if (field == 1) {
        setVariable1(getVariable1()+Constants.DOWN);
      } else {
        setVariable2(getVariable2()+Constants.DOWN);
      }      
    }
  }

  /**
   * Getter function for variable 1
   * @return Value of variable1
   */
  public int getVariable1() {
    return variable1;
  }

  /**
   * Setter function for variable 1
   * @param v New value of variable1
   */
  public void setVariable1(int v) {
    variable1 = v;
    mvcMessaging.notify("model:variable1Changed", variable1, true);
  }
  
  /**
   * Getter function for variable 1
   * @return Value of variable2
   */
  public int getVariable2() {
    return variable2;
  }
  
  /**
   * Setter function for variable 2
   * @param v New value of variable 2
   */
  public void setVariable2(int v) {
    variable2 = v;
    mvcMessaging.notify("model:variable2Changed", variable2, true);
  }

}