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.



站长地图