// PlayingCardsAnimationsApplication.java (c) Kari Laitinen // http://www.naturalprogramming.com /* 2014-11-27 File created. 2014-11-27 Last modification. This program is an improved version of PlayingCardsApplication.java. This version contains the following additional features: - turning of cards is animated */ 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 JFrame-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 ) ; } } } class PlayingCardsAnimationsFrame extends JFrame implements WindowListener { PlayingCardsPanel playing_cards_panel ; public PlayingCardsAnimationsFrame() { // The files that contain card face images 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. Toolkit toolkit = Toolkit.getDefaultToolkit() ; ImageStore.card_back_image = toolkit.getImage( "playing_cards_images/card_back.png" ) ; // By using a MediaTracker object, we can ensure that all pictures // are loaded from the files 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 = toolkit.getImage( 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. " ) ; } setTitle( "PlayingCardsAnimationsApplication.java" ) ; setSize( 910, 600 ) ; playing_cards_panel = new PlayingCardsPanel() ; getContentPane().add( playing_cards_panel ) ; addWindowListener( this ) ; } public void windowClosing( WindowEvent event ) { playing_cards_panel.stop_animation_thread() ; setVisible( false ) ; System.exit( 0 ) ; } public void windowActivated( WindowEvent event ) { } public void windowDeactivated( WindowEvent event ) { } public void windowClosed( WindowEvent event ) { playing_cards_panel.stop_animation_thread() ; } public void windowDeiconified( WindowEvent event ) { playing_cards_panel.start_animation_thread() ; } public void windowIconified( WindowEvent event ) { playing_cards_panel.stop_animation_thread() ; } public void windowOpened( WindowEvent event ) { playing_cards_panel.start_animation_thread() ; } } public class PlayingCardsAnimationsApplication { public static void main( String[] not_in_use ) { new PlayingCardsAnimationsFrame().setVisible( true ) ; } }