CS300讲解、辅导java Matching Game
- 首页 >> Java编程P02 Matching Game
Programming II (CS300) Fall 2019
Pair Programming: ALLOWED
Due: 9:59PM on September 18th
P02 Matching Game
Overview
This assignment involves developing a graphical application that represents a simple matching
game with cards. Our game is a one player memory game in which 12 cards are initially laid
face down on a surface. Each turn, the player selects two face down cards, which are then
flipped over. If the two cards match, they remain face up. Otherwise, they are both flipped
face down. The player wins the game when he succeeds to turn over all pairs of matching cards.
Fig.1 illustrates an example of what this program might look like when it is done.
(a) Initial Display Window (b) Cards Not Matched
(c) Cards Matched (d) All Pairs of Cards Matched
Fig. 1. Matching Game Display Window
c 2019 AYARI Ben Hadj Kacem and Dahl - University of Wisconsin - Madison.
Learning Objectives
The goal of this assignment is to practice working with predefined objects (for instance Card
objects), and to develop the basis for a simple interactive graphical application. The Graphical
User Interface (GUI) application will be written using a provided PApplet object and PImage
objects defined within the graphic processing library. This assignment will also give you
experience working with callback methods to define how your program responds to mouse-based
input or key-pressed.
Grading Rubric
5 points Pre-Assignment Quiz: Generally, you will not have access to this write-up
without first completing this pre-assignment quiz through Canvas.
20 points Immediate Automated Tests: Upon submission of your assignment
to Gradescope, you will receive feedback from automated grading tests
about whether specific parts of your submission conform to this write-up
specification. If these tests detect problems in your code, they will attempt to
give you some feedback about the kind of defect that they noticed. Note that
passing all of these tests does NOT mean your program is otherwise correct.
To become more confident of this, you should run additional tests of your own.
25 points Additional Automated Tests: When your manual grading feedback
appears on Gradescope, you will also see the feedback from these additional
automated grading tests. These tests are similar to the Immediate Automated
Tests, but may test different parts of your submission in different ways.
1 Additional Assignment Requirements
• The ONLY import statements that you may include in this assignment are:
import java.io.File;
import java.util.Random;
import processing.core.PApplet;
import processing.core.PImage;
• You MUST NOT add any additional fields either instance or static to your program, and
any public methods either static or instance to your program, other than those defined
in this write-up.
• You CAN define local variables that you may need to implement the methods defined in
this program.
• You CAN define private static methods to help implement the different public static
methods defined in this program, if needed.
2
2 GETTING STARTED
To get started, let’s first create a new Java8 project within Eclipse. You can name this project
whatever you like, but P02 Matching Game is a descriptive choice. Then, create a new class
named MatchingGame with a public static void main(String[] args) method stub. This class
represents the main class in your program. This process is described near the end of the
Eclipse installation instructions here. Do not include a package statement at the top of your
MatchingGame class (leave it in the default package).
2.1 Download CS300MatchingGame.jar file and add it to your project
build path
We have prepared a jar file that contains the processing library, along with a few extra object
types to help you build this and future assignments. Download this CS300MatchingGame.jar
file and copy it into the project folder that you just created. Then, right-click on this file in the
“Package Explorer” within Eclipse, choose “Build Path” and then “Add to Build Path” from
the menu. Note: If the .jar file is not immediately visible within Eclipse’s Package Explorer,
try right-clicking on your project folder and selecting “Refresh”.
(Note that for Chrome users on MAC, Chrome may block the the jar file and incorrectly
reports it as a malicious file. To be able to copy the downloaded jar file, Go to “chrome://downloads/”
and click on “Show in folder” to open the folder where your jar file is located.)
If the “Build Path” entry is missing when you right click on the jar file in the “Package
Explorer”, follow the next set of instructions to add the jar file to the build path:
1. Right-click on the project and choose “properties”.
2. Click on the “Java Build Path” option in the left side menu.
3. From the Java Build Path window, click on the “Libraries” Tab.
4. You can add the “CS300MatchingGame.jar” file located in your project folder by clicking
“Add JARs...” from the right side menu.
5. Click on “Apply” button.
2.2 Check your project setup
Now, to test that the CS300MatchingGame.jar file library is added appropriately to the build
path of your project, try running your program with the following method being called from
main() method .
Utility.runApplication(); // starts the application
3
If everything is working correctly, you should see a blank window that appears with the title,
“Matching Cards Game” as depicted in Fig. 2. Please consult piazza or one of the consultants,
if you have any problems with this setup before proceeding.
Fig. 2. Matching Game - Blank Screen Window
Note that the startApplication() method from the provided Utility class, provided in the
CS300MatchingGame jar file, creates the main window for the application, and then repeatedly
updates its appearance and checks for user input. It also checks if specific callback methods
have been defined in the MatchingGameclass. Callback methods specify additional computation
that should happen when the program begins, the user pressed a key or a mouse button, and
every time the display window is repeatedly redrawn to the screen.
2.3 Download images for your Matching Game application
Download the following images.zip compressed folder and extract its content to your project
folder. Make sure that the extraction operation results into a folder named images which
contains 6 image files with the exact following names: apple.png, ball.png, peach.png, refFlower.png,
shark.png, and yellowFlower.png.
2.4 Overview of the class Card
The Card class represents the data type for card objects that will be created and used in our
MatchingGame application. The javadoc documentation for this class is provided here. Make
sure to read the description of the constructor and the different methods implemented in the
Card class carefully. You do not need to implement this class or any of its declared methods.
The class Card is entirely provided for you in CS300MatchingGame.jar.
4
3 VISUALIZE THE GAME DISPLAY WINDOW
3.1 Declare MatchingGame Class Fields
Declare final class fields: Let’s first declare the different constants (final class fields) that
we will be using in our MatchingGame application. Add the following final class fields to your
MatchingGame class. Make sure to put them outside of any method, including the main().
The top of the class body would be a good placement where to declare them.
// Congratulations message
private final static String CONGRA_MSG = "CONGRATULATIONS! YOU WON!";
// Cards not matched message
private final static String NOT_MATCHED = "CARDS NOT MATCHED. Try again!";
// Cards matched message
private final static String MATCHED = "CARDS MATCHED! Good Job!";
// 2D-array which stores cards coordinates on the window display
private final static float[][] CARDS_COORDINATES =
new float[][] {{170, 170}, {324, 170}, {478, 170}, {632, 170},
{170, 324}, {324, 324}, {478, 324}, {632, 324},
{170, 478}, {324, 478}, {478, 478}, {632, 478}};
// Array that stores the card images filenames
private final static String[] CARD_IMAGES_NAMES = new String[] {"apple.png",
"ball.png", "peach.png", "redFlower.png", "shark.png", "yellowFlower.png"};
Declare variable class fields: Let’s now declare the class variables that we are going to use
while developing our game. Add the following static fields to your MatchingGame class. Put
them outside of any method including, the main(). Putting these static variables just after the
final ones would be a good place.
private static PApplet processing; // PApplet object that represents
// the graphic display window
private static Card[] cards; // one dimensional array of cards
private static PImage[] images; // array of images of the different cards
private static Random randGen; // generator of random numbers
private static Card selectedCard1; // First selected card
private static Card selectedCard2; // Second selected card
private static boolean winner; // boolean evaluated true if the game is won,
// and false otherwise
private static int matchedCardsCount; // number of cards matched so far
// in one session of the game
private static String message; // Displayed message to the display window
Note that you are allowed to import only the following classes to your MatchingGame class.
5
3.2 Define the setup callback method
Recall that Utility.startApplication() creates the display window, sets its dimension, and checks
for callback methods. A callback methods specifies additional computation that should happen
when the program begins, the user pressed a key or a mouse button, and every time the display
window is repeatedly redrawn to the screen.The first callback method that we will define in
this program is the setup() method. You should define this method in your MatchingGame
class with EXACTLY the following signature:
/**
* Defines the initial environment properties of this game as the program starts
*/
public static void setup(PApplet processing)
Note that setup() method will be run as a result of your call to Utility.startApplication() from
main() method.
The setup() method receives an argument of type PApplet that is passed to it from the
Utility class. It’s used to define initial environment properties such as screen size and to load
background images and fonts as the program starts. There can only be one setup() method
in the whole program and it is run once when the program starts.
To convince yourself that this method is being called once when your program starts up, try
adding a print statement to the startup method, then run your program. After this test, you
can remove the print statement before proceeding.
3.3 Set the background color of the display window
Notice that we defined a static class variable named processing of type PApplet. This variable
refers to an object that contains most of the drawing functionality from the processing library.
Since this processing object is one that you cannot create at this level on your own, the
Utility.startApplication() method will pass it as parameter into the setup() method.
To set the background color for the display window using the processing PApplet object, follow
the next set of instructions in the setup() method.
1. Set the processing class variable to the one passed as input parameter.
2. Using the processing reference, call the background() method defined in the processing
library to set the background color for our Matching Game display window as follows:
// Set the color used for the background of the Processing window
processing.background(245, 255, 250); // Mint cream color
6
We have chosen mint cream color, which RGB code is (245, 255, 250) as a background color
for our matching game. You can try to change this background color to another color of your
choice to see how it works. After that, set the color again to mint cream.
4 DRAW CARDS
4.1 Create and initialize the array images
First, make sure that you have a folder called “images” in your “Project Explorer” and that it
contains six images. If it is not the case, go up to subsection 2.3. Recall also that the static field
CARD IMAGES NAMES is a perfect size array that holds 6 String objects which represent
the names of the images stored in the “images” folder. Now, in the setup() method, create the
static field images array such that it has exactly the same length as CARD IMAGES NAMES
array, and stores references of type PImage. Next, let’s initialize its content.
Note that the java graphic processing library defines the PImage class as a datatype used for
storing images. A PImage object is used to represent an image that can be loaded from a file
by calling loadImage() method, and then drawn to the screen at a given position by calling
image() method. Processing can display .gif, .jpg, .tga, and .png images.
For instance, the following line of code loads CARD IMAGES NAMES[0] (which refers to
“apple.png”) as a PImage object and stores its reference into images[0].
//load apple.png image file as PImage object and store its reference into images[0]
images[0] = processing.loadImage("images" + File.separator + CARD\_IMAGES\_NAMES[0]);
Now, in the setup() callback method, load all the image files whose names are stored into
CARD IMAGES NAMES array and store their references into the images array by calling the
method loadImage() defined in the processing library.
Draw an image to the display window: You can call the method image() defined in the
processing library from the setup() method in order to draw one image to the center of the
screen. The following line of code, added after initializing the array images, draws an apple to
the center of the screen as shown in Fig. 3.
// Draw an image of an apple at the center of the screen
processing.image(images[0], processing.width / 2, processing.height / 2);
// width [resp. height]: System variable of the processing library
// that stores the width [resp. height] of the display window.
Note the importance of adding this code after calling the background() method, instead of
before. You can also load and draw another different image (for instance images[1]) to the
center of the screen in a similar way. You can also change the position where the image can
be drawn to the screen. After that, remove the lines of code that draw any image to the
screen from your setup() method before proceeding to the next steps. In your setup() method,
Fig. 3. Image of an Apple at the Center of the Screen
leave only the code that sets the processing static field of MatchingGame class, sets the color of
the background of the display window, creates images array, and initializes its content. When
you run your program, you should have an empty mint cream display window.
4.2 Define initGame method
Let’s now initialize the other class variables that we have defined in our MatchingGame class.
To do so, we are going to define a method called initGame with exactly the following signature.
/**
* Initializes the Game
*/
public static void initGame()
Make sure to initialize the following static fields in your initGame() method with respect to the
default values provided in the table below.
static field initial and restart value
randGen new Random(Utility.getSeed())
selectedCard1, selectedCard2 null
matchedCardsCount 0
winner false
message empty String
Note that Utility.getSeed() is a static method defined in Utility class. It returns a random int
8
that MUST be used as a seed to create a Random object.
You have to call initGame() method from the setup() method and each time the game is
restarted. Notice the important of calling initGame() method after the lines of code that
create and initialize the array images, instead of before.
4.3 Create cards array
The flat surface of our matching game is a 3 × 4 grid of cards. It can hold up to 12 cards
whose coordinates (x,y) are predefined in the static final field CARDS COORDINATES. In
initGame() method, create the array cards whose length MUST be CARD IMAGES NAMES.length*2.
4.4 Draw one card to the display window
Let’s now draw one card at a specific position of the game display surface. To do so, in initGame()
method, create an instance of the Card class and stores its reference into a position of your choice of
cards array (for instance at index 0). You can set its x and y coordinates to one of the coordinate
positions stored in the CARDS COORDINATES array. Notice that the Card class defines only one
constructor, which takes three input parameters: a reference of type PImage that refers to the image
of the card, and x, and y of type float representing the coordinates or position where the card will
be drawn to the screen. Notice also isVisible() and setVisible() methods. A card is set visible when it
is facing up. A card is not visible (meaning facing down) by default when it is created. The select()
method selects the card, while deselect() method turns off the card selection.
(a) Facing Down Card at Position 0 (b) Facing Up Card at Position 0
Fig. 4. One Card Drawn to the Display Window
For instance, the following segment of code added to your initGame(), draws a card facing down at
position 0 of CARDS COORDINATES array, as illustrated in Fig. 4(a).
cards[3] = new Card(images[2], CARDS_COORDINATES[0][0], CARDS_COORDINATES[0][1]);
cards[3].draw();
9
(a) All Cards Facing Down (b) All Cards Facing Up
Fig. 5. Grid of Cards
If you set the card to be visible before calling its draw() method, you will have as output the screen
shown in Fig. 4(b). Before moving to the next step remove all the lines of code that create any card
from your initGame() method and draw it. cards array should contain only null references.
cards[3] = new Card(images[2], CARDS_COORDINATES[0][0], CARDS_COORDINATES[0][1]);
cards[3].setVisible(true);
cards[3].draw();
4.5 Create and draw the cards
In this step, we are going to draw 12 cards assigned to random positions within the 3 × 4 grid display
surface. Note that in your implementation, it is highly recommended to avoid use specific values to
refer to the length of an array. For instance, you can access the capacity of the cards array in your
program through the field length defined for Java Arrays.
4.5.1 Mix up the cards
Now, in the initGame() method, initialize the content of the perfect size array cards such that every
PImage reference defined in images array must be assigned to ONLY TWO cards located at different
positions selected randomly from the CARDS COORDINATES array. Two cards should not have the
same position.
4.5.2 Draw the cards
Now, let’s draw the mixed up cards to the screen. When created, a card is by default facing down.
At this stage, try to set each card visible before drawing it, so you can see the distribution of cards
10
over the grid when drawn. You may have an output similar to the one illustrated in Fig. 5.
5 RESTART THE GAME BY PRESSING N-Key
We would like to initialize the game with a new distribution of the cards each time the key ’N’ or ’n’
is pressed. To do this, define the callback method keyPressed() with exactly the following signature.
/**
* Callback method called each time the user presses a key
*/
public static void keyPressed()
Note that each time the user presses any key, the keyPressed() callback method will be called automatically.
You can check which key was pressed using the “key” field within the processing object (processing.key).
Now, run your program. You should have an initial distribution of cards. Then, each time you
press the N-key, the distribution of cards must change. Fig. 6 illustrates an example of two different
distribution of cards.
(a) Initial Distribution of Cards (b) Cards Distribution after N-Key is pressed
Fig. 6. Example of Restarting Game
Before moving to the next step, make sure to remove any line of code that sets the visibility of cards
to true. When you run your program all cards should be laying in rows, face down.
11
6 ENABLE SELECTING AND MATCHING CARDS
6.1 Enable selecting cards
Let’s now enable the selection of cards. First, we would like to implement the following simple behavior:
set a card to be visible and select it when the mouse is pressed and is over the card. We expect so
that the display window will be redrawn each time the mouse is pressed and is over a card. To do so,
we need to implement another callback method called draw.
6.1.1 Implement the callback draw() method
Add to the MatchingGame class a draw() method with exactly the following signature. This method
will continuously draw the application display window and updates its content with respect to any
change or any event that affects its appearance.
/**
* Callback method draws continuously this application window display
*/
public static void draw()
To convince yourself that this method is continuously called by the Utility class as a result of your
calling Utility.startApplication(), as long as the application runs, try adding a print statement to
the definition of this method. Then, run the program and check the console. After this test, you
can remove the print statement. Note also that this method should never be called explicitly in this
program.
Now, move the statement that sets the color of the background from setup() method to this draw()
method. Move also the code that draws the different cards from initGame() method to draw() method.
Finally, call displayMessage(message) to draw the class variable message to the application display
window. We provide you in the following with the implementation details of displayMessage() method.
/**
* Displays a given message to the display window
* @param message to be displayed to the display window
*/
public static void displayMessage(String message) {
processing.fill(0);
processing.textSize(20);
processing.text(message, processing.width / 2, 50);
processing.textSize(12);
}
12
6.1.2 Select and turn over a card
In our matching game, initially, all cards laying down on the game surface (meaning not visible and
not selected). If the mouse is pressed and is over a card laying down, this card must be turned over
and selected. To implement this behavior, let’s first implement the isMouseOver() method with exactly
the following signature.
/**
* Checks whether the mouse is over a given Card
* @return true if the mouse is over the storage list, false otherwise
*/
public static boolean isMouseOver(Card card)
The isMouseOver() method should return true if the mouse is over the image of the card object which
reference is passed to it as input parameter, and false otherwise. To implement this method, use width
and height fields defined within the image of the card to determine whether the mouse is over it. You
can access the mouse position through the fields mouseX and mouseY inside the processing PApplet
class field. mouseX and mouseY are variables of the processing library that always contain the current
horizontal and vertical coordinate of the mouse respectively. To get a reference to the image of the
card, call the instance method getImage() defined in Card class. As illustrated in Fig. 7, the center of
the image is the position (x,y) of the card within the display window.
Fig. 7. Card Image Dimensions
Now, add the mousePressed() callback method to your MatchingCards class with exactly the following
signature. This method runs each time the mouse is pressed.
/**
* Callback method called each time the user presses the mouse
*/
public static void mousePressed()
In the mousePressed() method, check if the mouse is over a card. If it is the case, set the card visible
and select it. Note that Card class defines a method called select() that selects the card.
13
6.2 Matching cards
6.2.1 Implement and test matchingCards() method
Now, implement the following matchingCards() method with exactly the following signature. This
method checks whether two given cards match. Note that two cards match if they have the same
image.
/**
* Checks whether two cards match or not
* @param card1 reference to the first card
* @param card2 reference to the second card
* @return true if card1 and card2 image references are the same, false otherwise
*/
public static boolean matchingCards(Card card1, Card card2)
6.2.2 Update mousePressed() method
Now, using the methods that you have already implemented, implement the in mousePressed() method
with respect to the following matching game rules.
1. The user can select and turn over any two cards.
2. If the two cards match, keep them visible.
3. If they don’t match, turn them back over.
4. The player wins the game when all the cards have been matched.
It is also worth noting that if the player is a winner, pressing the mouse won’t have any effect.
Feel free to organize this functionality into whatever custom private static methods you see fit. But,
make sure that running your program should result in an interaction display window comparable to
one shown in Fig. 1.
7 Assignment Submission
Congratulations on finishing this CS300 assignment! After verifying that your work is correct,
and written clearly in a style that is consistent with the course style guide, you should submit your
final work through gradescope.com. The only file that you must submit is MatchingGame.java. Your
score for this assignment will be based on your ”active” submission made prior to the hard deadline
of Due: 9:59PM on September 18th. The second portion of your grade for this assignment will be
determined by running that same submission against additional offline automated grading tests after
the submission deadline. It is worth noting that even though P02 won’t be graded by humans, make
sure that your program’s organization, clarity, commenting is conform to the course style guide.
Extra Challenges
Here are some suggestions for interesting ways to extend this memory game, after you have completed,
backed up, and submitted the graded portion of this assignment. No extra credit will be awarded
for implementing these features, but they should provide you with some valuable practice and
experience. DO NOT submit such extensions via gradescope.
1. You can add statistics to this game that can be displayed when the user wins the game, for
instance, the number of updates of the display window, or number of clicks.
2. Try to expand this Matching Game such that it can work with different size of grid of cards
(2 × 2), (3 × 2), (3 × 3), etc, and different images.