// PlayingCardsAnimationsApplet.java by Kari Laitinen // http://www.naturalprogramming.com /* 2013-01-31 File created. 2013-02-03 Last modification. With the applet of this program the user can deal cards and shuffle the card deck. The cards can be turned by clicking them with the mouse. This program shows how to: - build an application with several classes - how to manage many images - shuffle an ArrayList-based array - store and retrieve objects in a data structure called HashMap This program in an augmented version of PlayingCardsApplet.java. This version contains the following additional features: - turning of cards is animated NOTE: This program is still 'under construction'. */ import java.awt.* ; import java.awt.event.* ; import javax.swing.* ; import java.util.ArrayList ; import java.util.Collections ; import java.util.HashMap ; // ImageStore is a class that contains only static members. // It will be used to store references to Image objects that // need to be accessed from the Card class. The Image objects // are created in the JApplet-based class. class ImageStore { static Image card_back_image ; static HashMap card_face_images ; } class Card { int card_rank ; int card_suit ; Image card_image ; Point card_position = null ; int animation_count = 0 ; // face-up = card suit and rank are visible // face-down = card suit and rank are not visible public static int CARD_IS_FACE_UP = 1 ; public static int CARD_IS_FACE_DOWN = 2 ; public static int CARD_IS_TURNING_UP = 3 ; public static int CARD_IS_TURNING_DOWN = 4 ; int card_state = CARD_IS_FACE_DOWN ; // The following static data fields can be accessed from // other classes by writing Card.HEARTS, Card.DIAMONDS, etc. public static final int HEARTS = 1 ; public static final int DIAMONDS = 2 ; public static final int SPADES = 3 ; public static final int CLUBS = 4 ; public static final int CARD_WIDTH = 150 ; public static final int CARD_HEIGHT = 215 ; public Card ( int given_card_rank, int given_card_suit ) { card_rank = given_card_rank ; card_suit = given_card_suit ; // A reference to an Image object will be retrieved by using // a string as a key. For example, with the string "spades1" the // image of the Ace of Spades is found. if ( card_suit == HEARTS ) { card_image = ImageStore.card_face_images.get( "hearts" + card_rank ) ; } else if ( card_suit == DIAMONDS ) { card_image = ImageStore.card_face_images.get( "diamonds" + card_rank ) ; } else if ( card_suit == SPADES ) { card_image = ImageStore.card_face_images.get( "spades" + card_rank ) ; } else if ( card_suit == CLUBS ) { card_image = ImageStore.card_face_images.get( "clubs" + card_rank ) ; } } public int get_rank() { return card_rank ; } public int get_suit() { return card_suit ; } public void turn_card() { if ( card_state == CARD_IS_FACE_DOWN ) { card_state = CARD_IS_TURNING_UP ; animation_count = 0 ; } else if ( card_state == CARD_IS_FACE_UP ) { card_state = CARD_IS_TURNING_DOWN ; animation_count = 0 ; } } /*** public void turn_card_face_up() { card_state = CARD_IS_FACE_UP ; } public void turn_card_face_down() { card_state = CARD_IS_FACE_DOWN ; } ***/ public boolean card_is_face_up() { return ( card_state == CARD_IS_FACE_UP ) ; } public boolean card_is_face_down() { return ( card_state == CARD_IS_FACE_DOWN ) ; } public void set_card_position( Point given_position ) { card_position = given_position ; } public String get_suit_as_string() { String string_to_return = "" ; switch( card_suit ) { case HEARTS : string_to_return = "Hearts" ; break ; case DIAMONDS : string_to_return = "Diamonds" ; break ; case SPADES : string_to_return = "Spades" ; break ; case CLUBS : string_to_return = "Clubs" ; break ; default: string_to_return = "Program error!!!" ; } return string_to_return ; } // With the following methods it is possible to compare // "this" card to anohter card. // Making general comparisons between cards is somewhat // difficult as different card games value cards in // different ways. All the following methods are not // suitable for all card games. One known problem is that // an Ace is considered the smallest card by the methods. public boolean belongs_to_suit_of( Card another_card ) { return ( card_suit == another_card.card_suit ) ; } public boolean does_not_belong_to_suit_of( Card another_card ) { return ( card_suit != another_card.card_suit ) ; } public boolean is_smaller_than( Card another_card ) { return ( card_rank < another_card.card_rank ) ; } public boolean is_greater_than( Card another_card ) { return ( card_rank > another_card.card_rank ) ; } public boolean has_equal_rank_as( Card another_card ) { return ( card_rank == another_card.card_rank ) ; } public boolean has_different_rank_as( Card another_card ) { return ( card_rank != another_card.card_rank ) ; } public boolean contains_point( Point given_point ) { return ( card_position != null && given_point.x >= card_position.x && given_point.x <= card_position.x + CARD_WIDTH && given_point.y >= card_position.y && given_point.y <= card_position.y + CARD_HEIGHT ) ; } public void draw( Graphics graphics ) { if ( card_state == CARD_IS_FACE_UP ) { graphics.drawImage( card_image, card_position.x, card_position.y, null ) ; } else if ( card_state == CARD_IS_FACE_DOWN ) { graphics.drawImage( ImageStore.card_back_image, card_position.x, card_position.y, null ) ; } else if ( card_state == CARD_IS_TURNING_UP ) { animation_count ++ ; if ( animation_count <= 4 ) { int narrowing_card_width = CARD_WIDTH / animation_count ; graphics.drawImage( ImageStore.card_back_image, card_position.x, card_position.y, narrowing_card_width, CARD_HEIGHT, null ) ; } else if ( animation_count <= 8 ) { int growing_card_width = CARD_WIDTH / ( 9 - animation_count ) ; graphics.drawImage( card_image, card_position.x, card_position.y, growing_card_width, CARD_HEIGHT, null ) ; } else { card_state = CARD_IS_FACE_UP ; graphics.drawImage( card_image, card_position.x, card_position.y, null ) ; } } else if ( card_state == CARD_IS_TURNING_DOWN ) { animation_count ++ ; if ( animation_count <= 4 ) { int narrowing_card_width = CARD_WIDTH / animation_count ; graphics.drawImage( card_image, card_position.x, card_position.y, narrowing_card_width, CARD_HEIGHT, null ) ; } else if ( animation_count <= 8 ) { int growing_card_width = CARD_WIDTH / ( 9 - animation_count ) ; graphics.drawImage( ImageStore.card_back_image, card_position.x, card_position.y, growing_card_width, CARD_HEIGHT, null ) ; } else { card_state = CARD_IS_FACE_DOWN ; graphics.drawImage( ImageStore.card_back_image, card_position.x, card_position.y, null ) ; } } } } class CardDeck { static int[] suits = { Card.HEARTS, Card.DIAMONDS, Card.SPADES, Card.CLUBS } ; ArrayList cards_in_this_deck = new ArrayList() ; public CardDeck() { for ( int suit_index = 0 ; suit_index < 4 ; suit_index ++ ) { for ( int card_rank = 1 ; card_rank < 14 ; card_rank ++ ) { add_card( new Card( card_rank, suits[ suit_index ] ) ) ; } } } public void shuffle() { // The cards in an ArrayList-based dynamic array can be // ordered randomly by using the static method shuffle() of // the Collections class. Collections.shuffle( cards_in_this_deck ) ; } public void add_card( Card given_card ) { if ( cards_in_this_deck.size() < 52 ) { cards_in_this_deck.add( given_card ) ; } } public Card get_card() { // Value null is returned if there are no available cards // in the deck. // If cards are left in the deck, the last card in the array // is returned, and the returned card is removed from the deck. Card card_to_return = null ; if ( cards_in_this_deck.size() > 0 ) { // ArrayList method remove() returns a reference to the // object that it removes from the array. card_to_return = cards_in_this_deck.remove( cards_in_this_deck.size() - 1 ) ; } return card_to_return ; } } class PlayingCardsPanel extends JPanel implements ActionListener, MouseListener, Runnable { Thread animation_thread ; boolean thread_must_be_executed ; JButton button_to_deal_cards = new JButton( "DEAL" ) ; JButton button_to_shuffle_deck = new JButton( "SHUFFLE" ) ; CardDeck card_deck = new CardDeck() ; ArrayList row_of_cards = new ArrayList() ; Card lonesome_card ; Card selected_card ; // As a bacground color for the playing area we'll use a // brighter version of the green color that is used in the // HTML page of this applet. Color background_color_of_playing_area = new Color( 0x41C300 ).brighter() ; public PlayingCardsPanel() { setLayout( new BorderLayout() ) ; setBackground( background_color_of_playing_area ) ; JPanel panel_for_buttons = new JPanel() ; panel_for_buttons.setBackground( background_color_of_playing_area ) ; button_to_deal_cards.setBackground( background_color_of_playing_area.darker() ) ; button_to_shuffle_deck.setBackground( background_color_of_playing_area.darker() ) ; button_to_deal_cards.addActionListener( this ) ; button_to_shuffle_deck.addActionListener( this ) ; panel_for_buttons.add( button_to_deal_cards ) ; panel_for_buttons.add( button_to_shuffle_deck ) ; add( "South", panel_for_buttons ) ; addMouseListener( this ) ; } public void start_animation_thread() { if ( animation_thread == null ) { thread_must_be_executed = true ; animation_thread = new Thread( this ) ; animation_thread.start() ; } } public void stop_animation_thread() { if ( animation_thread != null ) { thread_must_be_executed = false ; animation_thread.interrupt() ; animation_thread = null ; } } public void run() { while ( thread_must_be_executed == true ) { repaint() ; try { Thread.sleep( 40 ) ; } catch ( InterruptedException caught_exception ) { // No actions to handle the exception. } } } public void actionPerformed( ActionEvent event ) { if ( event.getSource() instanceof JButton ) { if ( event.getSource() == button_to_deal_cards ) { // We'll first empty the ArrayList-based array. row_of_cards.clear() ; for ( int card_index = 0 ; card_index < 5 ; card_index ++ ) { Card new_card = card_deck.get_card() ; Point card_position = new Point( 40 + ( Card.CARD_WIDTH + 20 ) * card_index, 50 ) ; new_card.set_card_position( card_position ) ; row_of_cards.add( new_card ) ; } lonesome_card = card_deck.get_card() ; lonesome_card.set_card_position( new Point( 188, 300 ) ); } else if ( event.getSource() == button_to_shuffle_deck ) { card_deck.shuffle() ; } repaint() ; } } public void mousePressed( MouseEvent event ) { Point clicked_point = event.getPoint() ; if ( row_of_cards.size() == 5 ) { // There are five cards in the row. We'll check whether // any of the cards in the row were clicked. for ( int card_index = 0 ; card_index < row_of_cards.size() ; card_index ++ ) { if ( row_of_cards.get( card_index ). contains_point( clicked_point ) ) { row_of_cards.get( card_index ).turn_card() ; // selected_card will point to the clicked card. // In this program, however, selected_card is not // used for any purpose. selected_card = row_of_cards.get( card_index ) ; } } if ( lonesome_card != null && lonesome_card.contains_point( clicked_point ) ) { lonesome_card.turn_card() ; } repaint() ; } } public void mouseReleased( MouseEvent event ) {} public void mouseClicked( MouseEvent event ) {} public void mouseEntered( MouseEvent event ) {} public void mouseExited( MouseEvent event ) {} public void paintComponent( Graphics graphics ) { super.paintComponent( graphics ) ; // If the ArrayList pointed by row_of_cards contains cards // we'll draw them. if ( row_of_cards.size() > 0 ) { // We'll use the Java version of a 'foreach' loop to // draw the cards. A foreach loop processes all elements in // a collection, such as an ArrayList. In Java, the 'foreach' // loops are written with the 'standard' keyword 'for'. for ( Card card_in_row : row_of_cards ) { card_in_row.draw( graphics ) ; } // The lonesome card will be drawn only if there were // cards in the row. if ( lonesome_card != null ) { lonesome_card.draw( graphics ) ; } } else { graphics.drawString( "CLICK THE CARDS AFTER DEALING.", 100, 100 ) ; } } } public class PlayingCardsAnimationsApplet extends JApplet { PlayingCardsPanel playing_cards_panel ; public void init() { // The files that contain card face images are have // names such as hearts1.png, hearts2.png, hearts3.png, etc. // They are located in a folder named playing_cards_images. // Next we'll programmatically create the file names and // corresponding Image objects. // The Image objects will be stored in a data structure called // HashMap so that the file name body can be used as a key // to get references to the Image objects. ImageStore.card_back_image = getImage( getCodeBase(), "playing_cards_images/card_back.png" ) ; // By using a MediaTracker object, we can ensure that all pictures // are loaded from the server before we start showing them. MediaTracker picture_tracker = new MediaTracker( this ) ; picture_tracker.addImage( ImageStore.card_back_image, 0 ) ; ImageStore.card_face_images = new HashMap() ; String[] words_in_image_file_names = { "hearts", "diamonds", "spades", "clubs" } ; for ( int suit_index = 0 ; suit_index < 4 ; suit_index ++ ) { for ( int card_rank = 1 ; card_rank < 14 ; card_rank ++ ) { String image_file_name = "playing_cards_images/" + words_in_image_file_names[ suit_index ] + card_rank + ".png" ; Image card_image = getImage( getCodeBase(), image_file_name ) ; picture_tracker.addImage( card_image, suit_index * 1000 + card_rank ) ; String key_for_image = words_in_image_file_names[ suit_index ] + card_rank ; ImageStore.card_face_images.put( key_for_image, card_image ) ; } } try { picture_tracker.waitForAll() ; } catch ( InterruptedException caught_exception ) { System.out.print( "\n InterruptedException not handled. " ) ; } playing_cards_panel = new PlayingCardsPanel() ; getContentPane().add( playing_cards_panel ) ; } public void start() { playing_cards_panel.start_animation_thread() ; } public void stop() { playing_cards_panel.stop_animation_thread() ; } }