/* 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 program is almost the same as the applet, HighLowGUI2.java, modified to use a JFrame instead of a JApplet. As a demonstration, a WindowListener has been added to ask whether the user really wants to quit, when the user closes the window. */ import java.awt.*; import java.awt.event.*; import javax.swing.*; public class HighLowFrame extends JFrame { Image cardImages; // This will be an image that contains pictures // of all the playing cards. It is provided // as a parameter to the constructor of this // class of this class and is used in the // drawCard method in nested class HighLowCanvas. // (The images are stored in a file named // "smallcards.gif".) boolean runStandAlone; // This is set to true in the main routine, // to indicate that the frame is being run // as a stand-alone program (rather than being // opened by an applet, for example). If this // variable is true when the user closes the // window, then System.exit() will be called // to end the program. public static void main(String[] args) { // Main program simply opens a frame belonging // to this class, and sets the variable "runStandAlone" // in that frame to true. The image file for the // cards is loaded using a "Toolkit" and is provided // as a parameter to the constructor. Image cards = Toolkit.getDefaultToolkit().getImage("smallcards.gif"); HighLowFrame game = new HighLowFrame(cards); game.runStandAlone = true; } public HighLowFrame(Image cards) { // The constructor. The parameter must be an image // containing the card images. I've done things this // way because images have to be loaded in different // ways in applets and in stand-alone programs. // A HighLowCanvas occupies the CENTER position of the layout. // On the bottom is a panel that holds three buttons. The // HighLowCanvas object listens for ActionEvents from the // buttons and does all the real work of the program. super("High Low Card Game"); // Call constructor to give // a title to the window. cardImages = cards; setBackground( new Color(130,50,40) ); HighLowCanvas board = new HighLowCanvas(); getContentPane().add(board, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); buttonPanel.setBackground( new Color(220,200,180) ); getContentPane().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); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); // Closing is taken care of by the following WindowListener, // so we don't want any default close operation. addWindowListener( new WindowAdapter() { // This window listener responds when the user // clicks the window's close box by giving the // user a chance to change his mind. public void windowClosing(WindowEvent evt) { int response = JOptionPane.showConfirmDialog(null, "Do you really want to quit?"); if (response == JOptionPane.YES_OPTION) { dispose(); if (runStandAlone) System.exit(0); } } } ); pack(); // Make the frame just large enough to hold // the components that it contains. setResizable(false); // Don't allow the user to change // the size of the frame. setLocation(150,100); show(); // Make the frame appear on the screen. } // end constructor class HighLowCanvas extends JPanel implements ActionListener { // A nested class that displays the cards and does all the work // of keeping track of the state and responding to user events. 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. Font smallFont; // Font that will be used to draw the cards. HighLowCanvas() { // Constructor. Creates fonts, sets the foreground and // background colors, and starts the first game. // ALSO SETS PREFERRED SIZE so that the size can be // used when the frame is packed. setPreferredSize(new Dimension(310,120)); setBackground( new Color(0,120,0) ); setForeground( Color.green ); smallFont = new Font("SansSerif", Font.PLAIN, 12); bigFont = new Font("Serif", Font.BOLD, 14); doNewGame(); } // end constructor public void actionPerformed(ActionEvent evt) { // Respond when the user clicks on a button by calling // the appropriate procedure. Note that the canvas is // registered as a listener in frame's constructor. String command = evt.getActionCommand(); if (command.equals("Higher")) doHigher(); else if (command.equals("Lower")) doLower(); else if (command.equals("New Game")) doNewGame(); } // end actionPerformed() void doHigher() { // 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. 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\" first!"; 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 = "OK! You win!"; } else { message = "Right! Try for " + cardCt + "."; } repaint(); } // end doHigher() void doLower() { // 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. 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\" first!"; 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 = "Great! You win!"; } else { message = "Right! Try for " + cardCt + "."; } repaint(); } // end doLower() void doNewGame() { // Called by the constructor, and called by actionPerformed() if // the use clicks the "New Game" button. Start a new game. if (gameInProgress) { // If the current game is not over, it is an error to try // to start a new game. message = "Finish the game first!"; 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() public void paintComponent(Graphics g) { // This method draws the message at the bottom of the // canvas, and it draws all of the dealt cards spread out // across the canvas. If the game is in progress, an // extra card is dealt representing the card to be dealt next. super.paintComponent(g); g.setFont(bigFont); g.drawString(message,10,getSize().height-10); g.setFont(smallFont); int cardCt = hand.getCardCount(); for (int i = 0; i < cardCt; i++) drawCard(g, hand.getCard(i), 30 + i * 70, 15); if (gameInProgress) drawCard(g, null, 30 + cardCt * 70, 15); } // end paintComponent() void drawCard(Graphics g, Card card, int x, int y) { // Draws a card as a 40 by 60 rectangle with // upper left corner at (x,y). The card is drawn // in the graphics context g. If card is null, then // a face-down card is drawn. The cards are taken // from an image file, smallcards.gif. if (card == null) { // Draw a face-down card g.setColor(Color.blue); g.fillRect(x,y,40,60); g.setColor(Color.white); g.drawRect(x+3,y+3,33,53); g.drawRect(x+4,y+4,31,51); } else { int row = 0; switch (card.getSuit()) { case Card.CLUBS: row = 0; break; case Card.HEARTS: row = 1; break; case Card.SPADES: row = 2; break; case Card.DIAMONDS: row = 3; break; } int sx, sy; // coords of upper left corner in the source image. sx = 40*(card.getValue() - 1); sy = 60*row; g.drawImage(cardImages, x, y, x+40, y+60, sx, sy, sx+40, sy+60, this); } } } // end nested class HighLowCanvas } // end class HighLowFrame