简体   繁体   中英

Java Code - Solitaire

I've created a solitaire game in Java.

My question is: How can I fill objects (hearts, spades, diamonds, club) on every card with black or red color?

Here is the code I have now:

// draw the card
  public void draw (Graphics g, int x, int y) {
    // clear rectangle, draw border
    g.clearRect(x, y, width, height);
    g.setColor(Color.black);
    g.drawRect(x, y, width, height);

    // draw body of card
    if (faceUp()) 
      {
    if (color() == red)
      g.setColor(Color.red);
        else
      g.setColor(Color.black);

    g.drawString(names[rank()], x+3, y+15);

    if (suit() == heart) 
      {
        g.drawLine(x+25, y+30, x+35, y+20);
        g.drawLine(x+35, y+20, x+45, y+30);
        g.drawLine(x+45, y+30, x+25, y+60);
        g.drawLine(x+25, y+60, x+5, y+30);
        g.drawLine(x+5, y+30, x+15, y+20);
        g.drawLine(x+15, y+20, x+25, y+30);
        //    g.fill(Color.red);
      }
    else if (suit() == spade) 
      {
        g.drawLine(x+25, y+20, x+40, y+50);
        g.drawLine(x+40, y+50, x+10, y+50);
        g.drawLine(x+10, y+50, x+25, y+20);
        g.drawLine(x+23, y+45, x+20, y+60);
        g.drawLine(x+20, y+60, x+30, y+60);
        g.drawLine(x+30, y+60, x+27, y+45); 
      }
    else if (suit() == diamond) 
      {
        g.drawLine(x+25, y+20, x+40, y+40);
        g.drawLine(x+40, y+40, x+25, y+60);
        g.drawLine(x+25, y+60, x+10, y+40);
        g.drawLine(x+10, y+40, x+25, y+20);
      }
    else if (suit() == club) 
      {
        g.drawOval(x+20, y+25, 10, 10);
        g.drawOval(x+25, y+35, 10, 10);
        g.drawOval(x+15, y+35, 10, 10);
        g.drawLine(x+23, y+45, x+20, y+55);
        g.drawLine(x+20, y+55, x+30, y+55);
        g.drawLine(x+30, y+55, x+27, y+45); 
      }
      }
    else // face down 
      {
    g.setColor(Color.black);
    g.drawLine(x+15, y+5, x+15, y+65);
    g.drawLine(x+35, y+5, x+35, y+65);
    g.drawLine(x+5, y+20, x+45, y+20);
    g.drawLine(x+5, y+35, x+45, y+35);
    g.drawLine(x+5, y+50, x+45, y+50);
      }
  }
}

I took your snippet, and made a snippet of my own. The snippet only does a fill of the heart suit, using Graphics.fillPolygon . I've commented out the old drawing of lines in the snippet, so you can compare with what you did. The other cards I'll leave up to you.

import java.awt.*;
import javax.swing.*;

public class CardFrame {
    enum CardColor{red,black};
    enum CardSuit{heart,diamond,spade,club}
    public static void main( String[] args )
    {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JPanel cardDisplay = new JPanel() {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(50,100);
                    }
                    @Override
                    protected void paintComponent(Graphics g) {
                        super.paintComponent(g);
                        draw(g,0,0);
                    }

                    private int width = 50;
                    private int height = 80;

                    private boolean faceUp() {
                        return true;
                    }
                    private CardColor color() {
                        return CardColor.red;
                    }
                    private CardSuit suit() {
                        return CardSuit.heart;
                    }
                    private int rank() {
                        return 0;
                    }
                    private String[] names = {"1","2","3","4","5","6","7","8","9","10","J","Q","K","A"};
                    private void draw(Graphics g, int x, int y) {
                        // clear rectangle, draw border
                        g.clearRect(x, y, width, height);
                        g.setColor(Color.black);
                        g.drawRect(x, y, width, height);

                        // draw body of card
                        if (faceUp()) {
                            if (color() == CardColor.red)
                                g.setColor(Color.red);
                            else
                                g.setColor(Color.black);

                            g.drawString(names[rank()], x + 3, y + 15);

                            if (suit() == CardSuit.heart) {
//                              g.drawLine(x + 25, y + 30, x + 35, y + 20);
//                              g.drawLine(x + 35, y + 20, x + 45, y + 30);
//                              g.drawLine(x + 45, y + 30, x + 25, y + 60);
//                              g.drawLine(x + 25, y + 60, x + 5, y + 30);
//                              g.drawLine(x + 5, y + 30, x + 15, y + 20);
//                              g.drawLine(x + 15, y + 20, x + 25, y + 30);
                                int[] xPoints = new int[]{x + 5,x + 15,x + 25,x + 35,x + 45,x + 25};
                                int[] yPoints = new int[]{y + 30,y + 20,y + 30,y + 20,y + 30,y + 60};
                                g.fillPolygon(xPoints, yPoints, 6);
                            } else if (suit() == CardSuit.spade) {
                                // ...
                            } else if (suit() == CardSuit.diamond) {
                                // ...
                            } else if (suit() == CardSuit.club) {
                                //
                            }
                        } else // face down
                        {
                            // ...
                        }
                    }
                };

                JFrame frm = new JFrame();
                frm.setContentPane(cardDisplay);
                frm.pack();
                frm.setVisible(true);
            }
        });
    }
}

Result:

在此处输入图片说明

Some of my suggestions from an answer deleted from your recent question :

  • First of all, there is no such thing as "static constructors", static initializer blocks, yes, but these don't begin with the class name as a constructor would and so don't look like constructors and more importantly, they don't behave as constructors -- they don't create an instance of the class. Rather they are used for class-specific (not instance-specific) code that is called on class loading.
  • Myself, I'd create my card specific instance on GUI-card creation, thus I think that this code should go in a constructor wherever you create your GUI representation of a card. Note that this may be (and should be) separate from your creation of logical cards, although I don't do this in the example below.
  • Since the card backing image is class-specific and not Card instance-specific, I'd create my card backing image either in a static initializer block, or as a singleton instance . This way it should be created once and once only. In the example below, I use the singleton pattern (some say "anti"-pattern). It should be safe to use this in a Swing GUI, since Swing is single threaded, and so it should only be called in one thread.
  • As per my comment in your original question, the slowest thing that your code currently does is to read in image files repeatedly, and you want to avoid doing that by storing your images in variables and then using them as needed, as the example below shows.
  • A side recommendation (not related to your original question): consider using RenderingHints to smooth out any drawing of text or graphics that you might be doing. For example see code below.
  • Also use enums for your card Ranks and Suits. This is the canonical example used when discussing the subject of enums, and as you'll see below, they lend themselves so well to use of enums.
  • Your code uses a lot of "magic" numbers, in other words, lots of unexplained number literals such as g.drawString(club, x + 12, y + 45); . So someone just glancing at your code is going to wonder, what do 12 and 45 represent? Better to avoid this by using variables or constants with logical names to make your code more self-commenting. For example, something like: g2.drawString(s.getSymbol(), SYMBOL_X, SYMBOL_Y); where you can guess that SYMBOL_X is the x location of the symbol.

For example, please check out this code which can be copied into a single file and run:

import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.*;

/**
 * 
 * @author Hovercraftfullofeels
 * link: https://stackoverflow.com/a/35351199/522444
 *
 */    
public class PlayingCardExample {

    private static void createAndShowGui() {
        PlayingCardPanel mainPanel = new PlayingCardPanel();

        JFrame frame = new JFrame("Playing Card Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            createAndShowGui();
        });
    }
}

@SuppressWarnings("serial")
class PlayingCardPanel extends JPanel {
    private static final Color BG = Color.GREEN.darker().darker();
    private static final int GAP = 15;
    private MyDeck myDeck = new MyDeck();

    public PlayingCardPanel() {
        setBackground(BG);
        int rows = Suit.values().length;
        int cols = Rank.values().length;
        setLayout(new GridLayout(rows, cols, GAP, GAP));
        setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));

        while (myDeck.size() > 0) {
            Card myCard = myDeck.deal();
            final CardLabel cardLabel = new CardLabel(myCard);
            add(cardLabel.getLabel());
            cardLabel.addMouseListener(new CardListener(cardLabel));
        }
    }
}

// class to allow us to flip cards on mouse press
class CardListener extends MouseAdapter {
    private CardLabel cardLabel;

    public CardListener(CardLabel cardLabel) {
        this.cardLabel = cardLabel;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        boolean faceDown = ! cardLabel.isFaceDown();
        cardLabel.setFaceDown(faceDown);
    }
}

class CardLabel {
    private JLabel label = new JLabel();
    private Card myCard;
    private boolean faceDown = true;

    public CardLabel(Card myCard) {
        this.myCard = myCard;
        setFaceDown(true);
    }

    public void addMouseListener(MouseListener listener) {
        label.addMouseListener(listener);
    }

    public boolean isFaceDown() {
        return faceDown;
    }

    public void setFaceDown(boolean faceDown) {
        this.faceDown = faceDown;
        // get my singleton icon:
        Icon cardBackIcon = CardBack.getInstance().getIcon();
        Icon icon = faceDown ? cardBackIcon : myCard.getIcon();
        label.setIcon(icon);
    }

    public JLabel getLabel() {
        return label;
    }

    public Card getMyCard() {
        return myCard;
    }

}

// singleton class to create the backing image shared by all cards
class CardBack {
    private static final Color BG = Color.WHITE;
    private static final Color COLOR = Color.BLUE;
    private static final int W = 10;
    private static final float STROKE_WIDTH = 3f;
    private static CardBack instance = null;
    private BufferedImage image;
    private Icon icon;

    // singleton constructor is private and so is only called by this class itself
    private CardBack() {
        BufferedImage repeatImg = new BufferedImage(W, W, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = repeatImg.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setBackground(BG);
        g2.clearRect(0, 0, W, W);
        g2.setStroke(new BasicStroke(STROKE_WIDTH));
        g2.setColor(COLOR);
        g2.drawLine(0, 0, W, W);
        g2.drawLine(0, W, W, 0);
        g2.dispose();

        int width = Card.WIDTH;
        int height = Card.HEIGHT;
        int imageType = BufferedImage.TYPE_INT_ARGB;
        image = new BufferedImage(width, height, imageType);
        g2 = image.createGraphics();
        Rectangle2D anchor = new Rectangle2D.Double(0, 0, W, W);
        TexturePaint texturePaint = new TexturePaint(repeatImg, anchor);
        g2.setPaint(texturePaint);
        g2.fillRect(0, 0, width, height);
        g2.dispose();
        icon = new ImageIcon(image);
    }

    public BufferedImage getImage() {
        return image;
    }

    public Icon getIcon() {
        return icon;
    }

    public static CardBack getInstance() {
        // create the instance in a lazy fashion -- only create it if it has not
        // yet been created. Thus, it should only be created *once*
        if (instance == null) {
            instance = new CardBack();
        }
        return instance;
    }
}

class MyDeck {
    List<Card> cards = new ArrayList<>();

    public MyDeck() {
        initialize();
        shuffle();
    }

    public final void initialize() {
        cards.clear();
        for (Rank rank : Rank.values()) {
            for (Suit suit : Suit.values()) {
                cards.add(new Card(rank, suit));
            }
        }
    }

    public int size() {
        return cards.size();
    }

    public Card deal() {
        if (cards.size() > 0) {
            return cards.remove(0);
        } else {
            // TODO: better to use exceptions here!
            // String text = "cards size is " + cards.size();
            // throw new MyDeckException(text);
            return null;
        }
    }

    public void shuffle() {
        Collections.shuffle(cards);
    }
}

// Quick an dirty code below. If this were a "real" program,
// I'd probably create a class without image or icon
// and then use a wrapper or decorator class to add the image information
// since this would be GUI library specific
class Card {
    public static final int WIDTH = 50;
    public static final int HEIGHT = 70;
    private static final Font TEXT_FONT = new Font(Font.DIALOG, Font.BOLD, 14);
    private static final Font SYMBOL_FONT = TEXT_FONT.deriveFont(Font.PLAIN, 28f);
    private static final int NAME_X = 3;
    private static final int NAME_Y = 15;
    private static final int SYMBOL_X = 12;
    private static final int SYMBOL_Y = 45;
    private Rank rank;
    private Suit suit;
    private BufferedImage image;
    private Icon icon;

    public Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;

        // create each card's image and icon once on Card creation
        image = createImage(rank, suit);
        icon = new ImageIcon(image);
    }

    private static BufferedImage createImage(Rank r, Suit s) {
        BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        g2.setColor(java.awt.Color.WHITE);
        g2.fillRect(0, 0, WIDTH, HEIGHT);
        g2.setColor(java.awt.Color.BLACK);
        g2.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);

        g2.setColor(s.getColor());
        g2.setFont(TEXT_FONT);
        g2.drawString(r.getName(), NAME_X, NAME_Y);

        g2.setFont(SYMBOL_FONT);
        g2.drawString(s.getSymbol(), SYMBOL_X, SYMBOL_Y);

        g2.dispose();
        return img;
    }

    public Rank getRank() {
        return rank;
    }

    public Suit getSuit() {
        return suit;
    }

    public BufferedImage getImage() {
        return image;
    }

    public Icon getIcon() {
        return icon;
    }
}

enum Rank {
    ACE("A"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"), SEVEN("7"), EIGHT("8"), 
    NINE("9"), TEN("10"), JACK("J"), QUEEN("Q"), KING("K");
    private String name;

    private Rank(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

// Suit enum will hold its own color and symbol 
enum Suit {
    CLUB("Club", "\u2663", Color.BLACK), 
    DIAMOND("Diamond", "\u2666", Color.RED), 
    HEART("Heart", "\u2665", Color.RED), 
    SPADE("Spade", "\u2660", Color.BLACK);

    private String name;
    private String symbol;
    private Color color;

    private Suit(String name, String symbol, Color color) {
        this.name = name;
        this.symbol = symbol;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public String getSymbol() {
        return symbol;
    }

    public Color getColor() {
        return color;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM