Java Programming for absolute beginner- P26 pot

20 237 0
Java Programming for absolute beginner- P26 pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

there’s no mine in here, it reveals the cell and dispatches a MineCellEvent.REVEALED event. If there is a mine in here, it sets the color to col- ors[MINE] and dispatches a MineCellEvent.DETONATED event. Another anony- mous inner class listens for MouseEvents. The superclass, JPRButton3D, doesn’t do anything with right mouse clicks, but this MouseAdapter does. It either flags or unflags this cell based on whether the cell is already flagged and dispatches the corresponding event. Another thing the MineCell class needed to take care of was to prevent action events from mine cells that are flagged. You don’t want to let the player click a flagged cell and blow up, right? Nor do you want the cell to be animated. You want it to be concrete that if this cell is flagged, you can’t click it with the left mouse button, period. To accomplish this, the MineCell class overrides the processMouseEvent(MouseEvent) method. If this cell is not flagged or if you’re right-clicking it, just go ahead and let the MouseEvent pass, but if this cell is flagged and you’re trying to left-click it, stop it dead in its tracks: public void processMouseEvent(MouseEvent e) { if (!flagged || e.getModifiers() == MouseEvent.BUTTON3_MASK) super.processMouseEvent(e); } Here is the full source code listing for MineCell.java: /* * MineCell * Defines one cell of the MinePatrol Game. */ import java.awt.*; import java.awt.event.*; import java.util.Vector; import jpr.lightweight.JPRButton3D; public class MineCell extends JPRButton3D { protected int contents; public final static int EMPTY = 0; public final static int MINE = 9; //These colors are indexed by the contents Color[EMPTY] is for //revealed cells and colors[MINE] is for detonated cells protected Color[] colors; protected boolean hidden, detonated, flagged; //acts as the background color when the cell becomes visible protected static Image flagImg, mineImg, explodeImg; protected Vector listeners; public MineCell() { this(EMPTY); } 458 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 458 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. public MineCell(int contains) { super(); colors = new Color[] {getBackground(), Color.blue, Color.cyan, Color.green, Color.magenta, Color.yellow, Color.orange, Color.red, Color.black, Color.red.darker().darker()}; setFont(new Font("Arial", Font.BOLD, 16)); resetContents(contains); listeners = new Vector(); addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { MineCellEvent mce; if (flagged) return; if (contents < MINE) { setHidden(false); mce = new MineCellEvent(MineCell.this, MineCellEvent.REVEALED); } else { detonated = true; setBackground(colors[MINE]); setControlColor(colors[MINE]); setHidden(false); mce = new MineCellEvent(MineCell.this, MineCellEvent.DETONATED); } (new EventThread(mce)).start(); } }); addMouseListener(new MouseAdapter() { MineCellEvent mce; public void mousePressed(MouseEvent e) { if (e.getModifiers() == MouseEvent.BUTTON3_MASK && hidden) { if (flagged) { flagged = false; repaint(); mce = new MineCellEvent(MineCell.this, MineCellEvent.UNFLAGGED); } else { flagged = true; repaint(); mce = new MineCellEvent(MineCell.this, MineCellEvent.FLAGGED); } (new EventThread(mce)).start(); } } }); } 459 C h a p t e r 12 C r e a t i n g Y o u r O w n C o m p o n e n t s a n d P a c k a g e s JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 459 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. protected class EventThread extends Thread { MineCellEvent e; EventThread(MineCellEvent mce) { e = mce; } public void run() { switch(e.getID()) { case MineCellEvent.REVEALED: for (int i=0; i < listeners.size(); i++) { ((MineCellListener)listeners.elementAt(i)).mineCellRevealed(e); } break; case MineCellEvent.FLAGGED: for (int i=0; i < listeners.size(); i++) { ((MineCellListener)listeners.elementAt(i)).mineCellFlagged(e); } break; case MineCellEvent.UNFLAGGED: for (int i=0; i < listeners.size(); i++) { ((MineCellListener)listeners.elementAt(i)).mineCellUnflagged(e); } break; case MineCellEvent.DETONATED: for (int i=0; i < listeners.size(); i++) { ((MineCellListener)listeners.elementAt(i)).mineCellDetonated(e); } break; } } } public void setContents(int contains) { contents = contains >= EMPTY && contains <= MINE ? contains : EMPTY; setForeground(colors[contents]); } public void resetContents(int contains) { setContents(contains); setHidden(true); detonated = false; } public int getContents() { return contents; } public void setHidden(boolean h) { hidden = h; if (h) { setBackground(SystemColor.control); setControlColor(SystemColor.control); } 460 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 460 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. else if (!detonated) { setBackground(colors[EMPTY]); setControlColor(colors[EMPTY]); } flagged = false; setEnabled(h); repaint(); } public boolean isHidden() { return hidden; } public boolean isFlagged() { return flagged; } public static void setImages(Image f, Image m, Image e) { flagImg = f; mineImg = m; explodeImg = e; } public void paint(Graphics g) { super.paint(g); if (!hidden || flagged) drawContents(g); } protected void drawContents(Graphics g) { Image img = null; if (contents == MINE || flagged) { if (flagged) img = flagImg; else if (contents == MINE && detonated) img = explodeImg; else if (contents == MINE && !detonated) img = mineImg; if (img != null) { g.drawImage(img, (getSize().width - img.getWidth(this)) / 2, (getSize().height - img.getHeight(this)) /2, this); } } else if (contents != EMPTY) { FontMetrics fm = g.getFontMetrics(); g.setColor(getForeground()); g.drawString(String.valueOf(contents), (getSize().width - fm.stringWidth(String.valueOf(contents))) / 2, (getSize().height + fm.getHeight()) / 2 - fm.getDescent()); } } public void processMouseEvent(MouseEvent e) { if (!flagged || e.getModifiers() == MouseEvent.BUTTON3_MASK) super.processMouseEvent(e); } 461 C h a p t e r 12 C r e a t i n g Y o u r O w n C o m p o n e n t s a n d P a c k a g e s JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 461 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. public void addMineCellListener(MineCellListener mcl) { listeners.addElement(mcl); } public void removeMineCellListener(MineCellListener mcl) { listeners.removeElement(mcl); } } Testing the MineCell Class The MineCellTest class constructs 12 MineCell instances in an array. When it con- structs the array of MineCells it sets the contents of the array to the value of the subscript, so the MineCell at cells[0] has its contents set to 0 (MineCell.EMPTY), the MineCell at cells[1] has its contents set to 1, and so on. Because the indices of the array go higher than nine, the last three mines are set to nine, ( Mine- Cell.MINE ). MineCellTest also implements MineCellLisetener and adds itself as a listener of all the MineCells. Next it loads the images flag.gif, mine.gif, and explode.gif using MediaTracker and sets the images by calling MineCell.setIm- ages(Image, Image, Image) . It implements the MineCellListener methods to notify you of what events are occurring by updating its label each time an event is heard. If a cell detonates, all the other cells are revealed too, so you can see the mine graphic in the other two cells that contain mines. You can see a run of this in Figure 12.6. Here is the source code: /* * MineCellTest * Tests the MineCell class */ import java.awt.*; public class MineCellTest extends GUIFrame implements MineCellListener { MineCell[] cells; Label statusLabel; public MineCellTest() { super("MineCell Test"); cells = new MineCell[12]; Panel cellPanel = new Panel(); cellPanel.setLayout(new GridLayout(3, 0)); for (int i=0; i < cells.length; i++) { cells[i] = new MineCell(i < 10 ? i : 9); cells[i].addMineCellListener(this); cells[i].setSize(50, 50); cellPanel.add(cells[i]); } 462 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 462 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. add(cellPanel, BorderLayout.CENTER); MediaTracker mt = new MediaTracker(this); Image[] imgs = { Toolkit.getDefaultToolkit().getImage("flag.gif"), Toolkit.getDefaultToolkit().getImage("mine.gif"), Toolkit.getDefaultToolkit().getImage("explode.gif") }; for (int i=0; i < imgs.length; i++) { mt.addImage(imgs[i], i); } try { mt.waitForAll(); } catch (InterruptedException e) {} MineCell.setImages(imgs[0], imgs[1], imgs[2]); statusLabel = new Label(); add(statusLabel, BorderLayout.SOUTH); pack(); setVisible(true); } public static void main(String args[]) { new MineCellTest(); } public void mineCellRevealed(MineCellEvent e) { statusLabel.setText("Revealed"); } public void mineCellFlagged(MineCellEvent e) { statusLabel.setText("Flagged"); } public void mineCellUnflagged(MineCellEvent e) { statusLabel.setText("Unflagged"); } public void mineCellDetonated(MineCellEvent e) { statusLabel.setText("Detonated"); for (int i=0; i < cells.length; i++) { cells[i].setHidden(false); } } } 463 C h a p t e r 12 C r e a t i n g Y o u r O w n C o m p o n e n t s a n d P a c k a g e s FIGURE 12.6 A test of the MineCell class is a success. JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 463 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Creating the Mine Field Classes Similar to the mine cell classes, the mine field classes consist of an event class, MineFieldEvent, a listener interface, MineFieldListener, and the MineField class itself. The MineFieldEvent Class There are four types of events that MineFields can trigger indicated by Mine- FieldEvent static constants, as follows: • SOLVED is for when the entire field of mine cells is solved, such as when all the cells are either flagged or revealed and no one blew up. • RANDOM indicates that the MineField was randomized, which means that the mines that are hidden within the mine field were rearranged ran- domly. • The DETONATED constant indicates that one of the field’s MineCell objects exploded. • FLAG_COUNT_CHANGED occurs when the player flags or unflags a cell within the MineField. The constructor accepts the object that triggered the thread and also the integer flag that indicates what type of event it fired. The eventID member holds this value and you can access its value by calling the getID() method. Here is the source code listing for MineFieldEvent.java: /* * MineFieldEvent * Encapsulates events fired by MineFields */ public class MineFieldEvent extends java.util.EventObject { protected int eventID; // event id constants public final static int SOLVED = 0, RANDOMIZED = 1, DETONATED = 2, FLAG_COUNT_CHANGED = 3; public MineFieldEvent(Object source, int id) { super(source); eventID = id; } public int getID() { return eventID; } } 464 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 464 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. The MineFieldListener Interface The MineFieldListener interface provides methods that correspond to the types of events that are defined in the MineFieldEvent class. There’s not much to explain here, so I’ll just list the short source code file: /* * MineFieldListener * Interface for listening for MineFieldEvents */ public interface MineFieldListener { public void mineFieldSolved(MineFieldEvent e); public void mineFieldRandomized(MineFieldEvent e); public void mineFieldDetonated(MineFieldEvent e); public void mineFieldFlagCountChanged(MineFieldEvent e); } The MineField Class The MineField class lays out a grid of MineCells, randomly places a set number of mines into the cells, and then listens for MineCellEvents. It also has an inner EventThread class that is uses to fire MineFieldEvents for all its listeners, which it keeps in a Vector. You know, the same model you’ve been using for custom event handling since the last chapter. It should be fresh in your mind from just reading the MineCell class which does the same kind of thing. The MineField class’s members are declared as follows: protected int rows, cols, mines, flagged, revealed; protected AudioClip revealClip, flagClip, unflagClip, explodeClip; protected MineCell[][] cells; protected Hashtable pointIndex; protected Vector listeners; Its integers are rows and cols, which keep track of the number of rows and columns in this MineField, mines keeps track of the number of mines, flagged counts the number of cells that are flagged, and revealed counts the number of cells that are revealed. It also declares four AudioClip objects that play sounds. You can tell by their names when they are played. The cells[][] array is a two- dimensional array of MineCells that make up this MineField. The pointIndex object is a Hashtable. The Hashtable class is provided in the java.util package. It allows you to index an object by another object. The MineField class makes use of the Hashtable by storing the cells[][] indices as Point objects in the Hashtable stored by the MineCells that are stored at that point in the cells[][] array. It’s a mouthful, eh? Here’s what I’m talkin’ bout, Willis. Say that MineCell a is stored at cells[1][2]. Unlike the matrix[][] array of the PlayArea class from the previous chapter, this array stores by [row][column] 465 C h a p t e r 12 C r e a t i n g Y o u r O w n C o m p o n e n t s a n d P a c k a g e s JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 465 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. because you don’t need to mess around with the actual component (x, y) coordi- nates like you did for the BlockGame application. So MineCell a is at row 1, col- umn 2 of the cells[][] array. Conceptually, x coordinates move left to right, specifying the columns, and y coordinates move up and down, specifying the rows. The Point in the cells[][] array where the cell is stored is x=2, y=1. So I construct that point and store the Point into the Hashtable indexed by the MineCell a (cells[1][2] stores a and (Point)pointIndex.get(a) returns the point (2, 1)). Why is this information useful, you ask? When a MineCell triggers an event you can get a reference to that MineCell by calling getSource(), but what is its posi- tion in the MineField? You don’t know. You’d have to loop through every index of the cells[][] array and compare e.getSource() == cells[row][column] to find it. You need to know where it is because you have to start checking the cells around it to see if they can be revealed. Remember from the beginning of the chapter that if you click an empty cell, all the surrounding empty cells are revealed and also any empty cells that surround those cells are revealed too, up until it reaches cells that have some sort of mine adjacent to them, those cells don’t have their adjacent cells revealed. To make getting the location quick and painless, you just store the location and index it by the MineCell object it contains, and then you can get any MineCell’s coordi- nates by checking the pointIndex Hashtable. The pointIndex Hashtable is constructed by passing in its initial size, rows * cols . A hash table is a general computer science term that refers to mapping keys to values. In our case, we are mapping the MineCell object (the key) to its Point location (the value). A Hashtable stores a value by a key by calling the put(Object, Object) method. The first argument is the key and the second argu- ment is the value. To retrieve the value based on its key, you then call the get(Object) method, passing in the key, and it returns the value. So in this case passing in the MineCell will return the Point object that stores where the given MineCell is positioned. The randomizeField() method first loops on all the MineCells and sets their con- tents to MineCell.EMPTY. Then for each of the mines, it generates a random num- ber based on the total number of cells. Because there is only one random number, the row is determined by dividing the random number, index, by the number of columns (if there are 10 columns and the random number is 11, 11/10 is 1, so it would be the second row, dividing by the number of rows would not necessarily be correct). The column number is the remainder ( index % cols), so given the previous example, the column number would be 1, (the second column from the left). So once it has a row and a column figured out, it looks to see 466 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 466 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. whether there is a mine there already (it wouldn’t be for the first mine, but could be for subsequent mines). If not, it puts one there: cells[index / cols][index % cols].resetContents(MineCell.MINE); If there already is one there it just continuously generates new random numbers until it can find an index that is not occupied by a mine already. Here is the loop that does this: for (int m=0; m < mines; m++) { do { index = rand.nextInt(rows * cols); } while (cells[index / cols][index % cols].getContents() != MineCell.EMPTY); cells[index / cols][index % cols].resetContents(MineCell.MINE); } After all the mines are set in place, the number clues are set. This is done by loop- ing on each cell. For each cell, it counts the number of mines that are in the eight cells that surround it and sets the contents of the cell to that number. If there are zero, this results in MineCell.EMPTY which is equal to zero. Here is the code that accomplishes this feat: protected void setNumberClues() { int nMines; for (int r=0; r < cells.length; r++) { for (int c=0; c < cells[r].length; c++) { if (cells[r][c].getContents() != MineCell.MINE) { nMines = 0; //count the number of mines surrounding this cell for (int dr = r - 1; dr <= r + 1; dr++) { //prevent ArrayIndexOutOfBoundsException //continue puts control back to the beginning of the loop if (dr < 0 || dr >= cells.length) continue; for (int dc = c - 1; dc <= c + 1; dc++) { if (dc < 0 || dc >= cells[dr].length) continue; if (cells[dr][dc].getContents() == MineCell.MINE) nMines++; } } cells[r][c].resetContents(nMines); } } } } You can see that the nested for loops iterate through all the cells and if the cell doesn’t have a mine in it, it counts the number of mines that surround it. The dr and dc variables loop on the cells that are adjacent to this cell (a 3-by-3 cell grid area, actually). Every time it encounters a mine, it increments nMines, and then it sets the contents of this cell (the center cell of the 3-by-3 grid) to nMines. The continue statements return control to the beginning of the innermost loop in 467 C h a p t e r 12 C r e a t i n g Y o u r O w n C o m p o n e n t s a n d P a c k a g e s JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 467 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... See Figure 12.8 for a visual of how this should work JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 470 Java Programming for the Absolute Beginner 470 The number of flags that you have is equal to the number of mines that are in the MineField So if all the cells that are left over are flagged, they must all be mines Mission accomplished Here is the full source code listing for MineField .java /* * MineField... she wins the game The source code listing for MinePatrol is as follows: /* * MinePatrol * The MinePatrol Game Driver */ import import import import import java. awt.*; java. awt.event.*; java. applet.Applet; java. net.URL; java. net.MalformedURLException; public class MinePatrol extends GUIFrame implements MineFieldListener, ActionListener { TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase... MineFieldEvent.RANDOMIZED))).start(); JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 472 Java Programming for the Absolute Beginner 472 EventThread(MineFieldEvent mfe) { e = mfe; } public void run() { switch(e.getID()) { case MineFieldEvent.SOLVED: for (int i=0; i < listeners.size(); i++) { ((MineFieldListener)listeners.elementAt(i)).mineFieldSolved(e); } break; case MineFieldEvent.RANDOMIZED: for (int i=0; i < listeners.size();...JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 468 Java Programming for the Absolute Beginner 468 instances where an ArrayIndexOutOfBoundExceptions might occur, like the topleft cell There is no cell to the left of or above this cell Therefore, before it checks a cell that is out of the array’s bounds, it just uses the continue... MineFieldEvent(this, MineFieldEvent.FLAG_COUNT_CHANGED))).start(); TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark Creating Your Own Components and Packages } JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 474 474 Java Programming for the Absolute Beginner checkIfSolved(); } public void mineCellUnflagged(MineCellEvent e) {... minefield; protected Button resetButton; protected Label flagCountLabel; JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 476 Java Programming for the Absolute Beginner 476 public void mineFieldFlagCountChanged(MineFieldEvent e) { flagCountLabel.setText("Flags: " + (minefield.getMineCount() - minefield.getFlagCount())); } public void actionPerformed(ActionEvent e) { minefield.randomize(); } } Summary In this chapter,... int nMines; for (int r=0; r < cells.length; r++) { for (int c=0; c < cells[r].length; c++) { if (cells[r][c].getContents() != MineCell.MINE) { nMines = 0; //count the number of mines surrounding this cell for (int dr = r - 1; dr = cells.length) continue; for (int dc... fireEvent) { Random rand = new Random(); int index; //initialize empty for (int r=0; r < cells.length; r++) { for (int c=0; c < cells[r].length; c++) { cells[r][c].resetContents(MineCell.EMPTY); } } TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 471 471 public void randomize()... showSafeArea(MineCell start) { //get the point index for the starting cell Point p = (Point)pointIndex.get(start); for (int r = p.y - 1; r = cells.length) continue; for (int c = p.x - 1; c = cells[r].length || !cells[r][c].isHidden()) continue; if (cells[r][c].isFlagged()) { flagged ; TEAM LinG - Live, Informative, Non-cost and Genuine! Please... ActionListener { TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark JavaProgAbsBeg-12.qxd 2/25/03 8:58 AM Page 475 475 for (int i=0; i < imgs.length; i++) { mt.addImage(imgs[i], i); } try { mt.waitForAll(); } catch (InterruptedException e) {} minefield.setImages(imgs[0], imgs[1], imgs[2]); try { minefield.setAudioClips( Applet.newAudioClip(new . for MinePatrol is as follows: /* * MinePatrol * The MinePatrol Game Driver */ import java. awt.*; import java. awt.event.*; import java. applet.Applet; import java. net.URL; import java. net.MalformedURLException; public. accomplished. Here is the full source code listing for MineField .java /* * MineField * Maintains a grid of MineCells */ import java. awt.*; import java. util.*; import java. applet.AudioClip; public class MineField. is the full source code listing for MineCell .java: /* * MineCell * Defines one cell of the MinePatrol Game. */ import java. awt.*; import java. awt.event.*; import java. util.Vector; import jpr.lightweight.JPRButton3D; public

Ngày đăng: 03/07/2014, 05:20

Tài liệu cùng người dùng

Tài liệu liên quan