import java.awt.*; import java.awt.event.*; import java.net.URL; import javax.swing.*; /** * This program is a simple card game. The user sees a card and * tries to predict whether the next card will be higher or * lower. Aces are the lowest-valued cards. If the user makes * three correct predictions, the user wins. If not, the * user loses. * * This class defines a panel, but it also contains a main() * routine that makes it possible to run the program as a * stand-alone application. In also contains a public nested * class, HighLowWithImages.Applet that can be used as an applet version * of the program. When used as an applet, the size of * the applet should be 395 pixels wide and about 240 pixels high. * The height is larger than strictly necessary to allow for * variations in the size of buttons from one platform to another. * * This program depends on several additional source code files: * Card.java, Hand.java, and Deck.java. It also requires the image * file, cards.png, which contains the images of the playing cards. * (The file cards.png is taken from the Gnome Desktop.) */ public class HighLowWithImages extends JPanel { /** * The main routine simply opens a window that shows a HighLowWithImages panel. */ public static void main(String[] args) { JFrame window = new JFrame("HighLowWithImages"); HighLowWithImages content = new HighLowWithImages(); window.setContentPane(content); window.pack(); // Set size of window to preferred size of its contents. window.setResizable(false); // User can't change the window's size. window.setLocation(100,100); window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); window.setVisible(true); } /** * The public static class HighLowWithImages$Applet represents this program * as an applet. The applet's init() method simply sets the content * pane of the applet to be a HighLowWithImages. To use the applet on * a web page, use code="HighLowWithImages$Applet.class" as the name of * the class. */ public static class Applet extends JApplet { public void init() { setContentPane( new HighLowWithImages() ); } } /** * The constructor lays out the panel. A CardPanel occupies the CENTER * position of the panel (where CardPanel is a subclass of JPanel that is * defined below). On the bottom is a panel that holds three buttons. * The CardPanel listens for ActionEvents from the buttons and does all * the real work of the program. */ public HighLowWithImages() { setBackground( new Color(130,50,40) ); setLayout( new BorderLayout(3,3) ); CardPanel board = new CardPanel(); add(board, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); buttonPanel.setBackground( new Color(220,200,180) ); add(buttonPanel, BorderLayout.SOUTH); JButton higher = new JButton( "Higher" ); higher.addActionListener(board); buttonPanel.add(higher); JButton lower = new JButton( "Lower" ); lower.addActionListener(board); buttonPanel.add(lower); JButton newGame = new JButton( "New Game" ); newGame.addActionListener(board); buttonPanel.add(newGame); setBorder(BorderFactory.createLineBorder( new Color(130,50,40), 3) ); } // end constructor /** * A nested class that displays the cards and does all the work * of keeping track of the state and responding to user events. */ private class CardPanel extends JPanel implements ActionListener { Deck deck; // A deck of cards to be used in the game. Hand hand; // The cards that have been dealt. String message; // A message drawn on the canvas, which changes // to reflect the state of the game. boolean gameInProgress; // Set to true when a game begins and to false // when the game ends. Font bigFont; // Font that will be used to display the message. Image cardImages; // Contains the image of all 52 cards /** * Constructor creates fonts, sets the foreground and background * colors and starts the first game. It also sets a "preferred * size" for the panel. This size is respected when the program * is run as an application, since the pack() method is used to * set the size of the window. */ CardPanel() { loadImage(); setBackground( new Color(0,120,0) ); setForeground( Color.GREEN ); bigFont = new Font("Serif", Font.BOLD, 15); setPreferredSize( new Dimension(15+4*(15+79), 185)); doNewGame(); } // end constructor /** * Respond when the user clicks on a button by calling the appropriate * method. Note that the buttons are created and listening is set * up in the constructor of the HighLowPanel class. */ public void actionPerformed(ActionEvent evt) { String command = evt.getActionCommand(); if (command.equals("Higher")) doHigher(); else if (command.equals("Lower")) doLower(); else if (command.equals("New Game")) doNewGame(); } // end actionPerformed() /** * Called by actionPerformmed() when user clicks "Higher" button. * Check the user's prediction. Game ends if user guessed * wrong or if the user has made three correct predictions. */ void doHigher() { if (gameInProgress == false) { // If the game has ended, it was an error to click "Higher", // So set up an error message and abort processing. message = "Click \"New Game\" to start a new game!"; repaint(); return; } hand.addCard( deck.dealCard() ); // Deal a card to the hand. int cardCt = hand.getCardCount(); Card thisCard = hand.getCard( cardCt - 1 ); // Card just dealt. Card prevCard = hand.getCard( cardCt - 2 ); // The previous card. if ( thisCard.getValue() < prevCard.getValue() ) { gameInProgress = false; message = "Too bad! You lose."; } else if ( thisCard.getValue() == prevCard.getValue() ) { gameInProgress = false; message = "Too bad! You lose on ties."; } else if ( cardCt == 4) { gameInProgress = false; message = "You win! You made three correct guesses."; } else { message = "Got it right! Try for " + cardCt + "."; } repaint(); } // end doHigher() /** * Called by actionPerformmed() when user clicks "Lower" button. * Check the user's prediction. Game ends if user guessed * wrong or if the user has made three correct predictions. */ void doLower() { if (gameInProgress == false) { // If the game has ended, it was an error to click "Lower", // So set up an error message and abort processing. message = "Click \"New Game\" to start a new game!"; repaint(); return; } hand.addCard( deck.dealCard() ); // Deal a card to the hand. int cardCt = hand.getCardCount(); Card thisCard = hand.getCard( cardCt - 1 ); // Card just dealt. Card prevCard = hand.getCard( cardCt - 2 ); // The previous card. if ( thisCard.getValue() > prevCard.getValue() ) { gameInProgress = false; message = "Too bad! You lose."; } else if ( thisCard.getValue() == prevCard.getValue() ) { gameInProgress = false; message = "Too bad! You lose on ties."; } else if ( cardCt == 4) { gameInProgress = false; message = "You win! You made three correct guesses."; } else { message = "Got it right! Try for " + cardCt + "."; } repaint(); } // end doLower() /** * Called by the constructor, and called by actionPerformed() if * the use clicks the "New Game" button. Start a new game. */ void doNewGame() { if (gameInProgress) { // If the current game is not over, it is an error to try // to start a new game. message = "You still have to finish this game!"; repaint(); return; } deck = new Deck(); // Create the deck and hand to use for this game. hand = new Hand(); deck.shuffle(); hand.addCard( deck.dealCard() ); // Deal the first card into the hand. message = "Is the next card higher or lower?"; gameInProgress = true; repaint(); } // end doNewGame() /** * This method draws the message at the bottom of the * panel, and it draws all of the dealt cards spread out * across the canvas. If the game is in progress, an extra * card is drawn face down representing the card to be dealt next. */ public void paintComponent(Graphics g) { super.paintComponent(g); if (cardImages == null) { g.drawString("Error: Can't get card images!", 10,30); return; } g.setFont(bigFont); g.drawString(message,15,168); int cardCt = hand.getCardCount(); for (int i = 0; i < cardCt; i++) drawCard(g, hand.getCard(i), 15 + i * (15+79), 15); if (gameInProgress) drawCard(g, null, 15 + cardCt * (15+79), 15); } // end paintComponent() /** * Draws a card in a 79x123 pixel rectangle with its * upper left corner at a specified point (x,y). Drawing the card * requires the image file "cards.png". * @param g The non-null graphics context used for drawing the card. If g is * null, a NullPointerException will be thrown. * @param card The card that is to be drawn. If the value is null, then a * face-down card is drawn. * @param x the x-coord of the upper left corner of the card * @param y the y-coord of the upper left corner of the card */ public void drawCard(Graphics g, Card card, int x, int y) { int cx; // x-coord of upper left corner of the card inside cardsImage int cy; // y-coord of upper left corner of the card inside cardsImage if (card == null) { cy = 4*123; // coords for a face-down card. cx = 2*79; } else { cx = (card.getValue()-1)*79; switch (card.getSuit()) { case Card.CLUBS: cy = 0; break; case Card.DIAMONDS: cy = 123; break; case Card.HEARTS: cy = 2*123; break; default: // spades cy = 3*123; break; } } g.drawImage(cardImages,x,y,x+79,y+123,cx,cy,cx+79,cy+123,this); } /** * Load the image from the file "cards.png", which must be somewhere * on the classpath for this program. If the file is found, then * cardImages will refer to the Image. If not, then cardImages * will be null. */ private void loadImage() { ClassLoader cl = HighLowWithImages.class.getClassLoader(); URL imageURL = cl.getResource("cards.png"); if (imageURL != null) cardImages = Toolkit.getDefaultToolkit().createImage(imageURL); } } // end nested class CardPanel } // end class HighLowWithImages