2
\$\begingroup\$

I made a simple Patience/Solitaire card game. The cards don't use images but JLabels and JPanels. The game is finished when all cards are on their suit piles.

Card movement:

  • mousePressed : card -> TOP (suit pile).
  • mouseDragged : card(s) -> COLUMN.

I would like some feedback on my code.

package patience;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JFrame;

public class Patience extends JFrame {
    final public static Dimension CARD_SIZE = new Dimension(52, 69);
    final public static int CARD_CLOSED = CARD_SIZE.height / 6, CARD_OPEN = CARD_SIZE.height / 3;
    final public static String SUIT[] = {"\u2660", "\u2665", "\u2663", "\u2666"},  ACE = "A", KING = "K";
    final public static Object[] RANK = {ACE, 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", KING};
    private Game game = new Game();
   
    Patience() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBackground(new Color(-16726016));
        setResizable(false);
        setContentPane(game);
        setSize(402, 522);
        setVisible(true);
        JButton newGame = new JButton("\u21bb");      
        newGame.setBorder(null);
        newGame.setFocusable(false);
        newGame.setContentAreaFilled(false);
        newGame.setFont(new Font(Font.DIALOG, Font.BOLD, CARD_SIZE.height / 2));
        newGame.addActionListener(e -> {
            game = new Game();
            setContentPane(game);
            revalidate();
            game.STOCK.requestFocus();
        });
        getLayeredPane().add(newGame).setBounds(10, 440, 40, 40);
    }
    
    public static void main(String[] args) {
        new Patience().setLocationRelativeTo(null);
    }
    
}

package patience;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Card extends JPanel {
    final private JPanel FRONT = new JPanel(new BorderLayout());
    final public Color COLOR;
    final public String SUIT;
    final public Object RANK;
    
    Card(String suit, Object rank) {
        super(null);
        COLOR = new Color[] {Color.black, Color.red}[Arrays.asList(Patience.SUIT).indexOf(suit) % 2];
        SUIT = suit;
        RANK = rank;
        setBorder(BorderFactory.createLineBorder(Color.white, 2));
        setBackground(Color.blue);
        setSize(Patience.CARD_SIZE);
        JLabel left = new JLabel("" + rank), right = new JLabel(suit), south = new JLabel(suit, JLabel.CENTER);
        left.setFont(new Font(Font.DIALOG, Font.BOLD, 25));
        left.setForeground(COLOR);
        left.setLayout(new BorderLayout());
        left.add(right, BorderLayout.EAST);
        right.setFont(new Font(Font.DIALOG, Font.PLAIN, 22));
        right.setForeground(COLOR);
        south.setFont(new Font(Font.DIALOG, Font.PLAIN, 36));
        south.setForeground(COLOR);
        FRONT.add(left, BorderLayout.CENTER);
        FRONT.add(south, BorderLayout.SOUTH);
        FRONT.setBorder(BorderFactory.createLineBorder(Color.gray));
        FRONT.setBackground(Color.white);
        FRONT.setSize(getSize());
        add(FRONT).setVisible(false);
    }
    
    public boolean isOpen() {
        return FRONT.isVisible();
    }
    
    public void flip() {
        FRONT.setVisible(!FRONT.isVisible());
    }
    
}
package patience;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class Game extends JLayeredPane {
    final public JButton STOCK = new JButton();
    final private JPanel OPEN = new JPanel(null), COLUMN[] = new JPanel[7];
    final private JLabel[] TOP = new JLabel[Patience.SUIT.length];
    
    Game() {
        STOCK.setContentAreaFilled(false);
        STOCK.setBorder(BorderFactory.createLineBorder(Color.darkGray, 2));
        STOCK.setLayout(null);
        STOCK.addActionListener(e -> {
            if (STOCK.getComponentCount() > 0) {
                ((Card) OPEN.add(STOCK.getComponent(0), 0)).flip();
            } else if (OPEN.getComponentCount() > 0) {
                for (Component card : OPEN.getComponents()) {
                    ((Card) STOCK.add(card, 0)).flip();
                }
                repaint();
            }
        });
        add(STOCK).setBounds(10, 10, Patience.CARD_SIZE.width, Patience.CARD_SIZE.height);

        for (String suit : Patience.SUIT) {
            for (Object rank : Patience.RANK) {
                STOCK.add(new Card(suit, rank), (int) (Math.random() * (STOCK.getComponentCount() + 1)));
            }
        }

        OPEN.setOpaque(false);
        add(OPEN).setBounds(STOCK.getX() + STOCK.getWidth() + 2, STOCK.getY(), Patience.CARD_SIZE.width, Patience.CARD_SIZE.height);

        for (int i = 0; i < TOP.length; i++) {
            TOP[i] = new JLabel(new String[] {"\u2664", "\u2661", "\u2667", "\u2662"}[i], JLabel.CENTER);
            TOP[i].setFont(new Font(Font.DIALOG, Font.BOLD, Patience.CARD_SIZE.height / 2));
            add(TOP[i]).setBounds(STOCK.getX() + (3 + i) * (OPEN.getX() - STOCK.getX()), STOCK.getY(), Patience.CARD_SIZE.width, Patience.CARD_SIZE.height);
        }

        for (int i = 0; i < COLUMN.length; i++) {
            COLUMN[i] = new JPanel(null);
            COLUMN[i].setOpaque(false);
            COLUMN[i].addContainerListener(new ContainerAdapter() {
                @Override
                public void componentAdded(ContainerEvent e) {
                    if (e.getContainer().getComponentCount() > 1) {
                        e.getChild().setLocation(0, e.getContainer().getComponent(1).getY() + new int[] {Patience.CARD_CLOSED, Patience.CARD_OPEN}[Boolean.compare(((Card) e.getContainer().getComponent(1)).isOpen(), false)]);
                    }
                }
            });
            add(COLUMN[i]).setBounds(STOCK.getX() + i * (OPEN.getX() - STOCK.getX()), STOCK.getY() + STOCK.getHeight() + STOCK.getY(), Patience.CARD_SIZE.width, i * Patience.CARD_CLOSED + 11 * Patience.CARD_OPEN + Patience.CARD_SIZE.height);
            for (int row = 0; row <= i; row++) {
                COLUMN[i].add(STOCK.getComponent(0), 0);
            }
            ((Card) COLUMN[i].getComponent(0)).flip();
        }

        addMouseListener(new MouseAdapter() {
            long time = System.currentTimeMillis();
            @Override
            public void mousePressed(MouseEvent e) {
                try {
                    Container pressed = (Container) getComponentAt(e.getPoint());
                    if (getLayer(pressed) == 0) {
                        Card card = (Card) pressed.getComponentAt(pressed.getMousePosition());
                        if (card.isOpen()) {
                            JPanel dragged = new JPanel(null);
                            dragged.setBounds(pressed.getX(), pressed.getY() + card.getY(), pressed.getWidth(), pressed.getComponent(0).getY() + pressed.getComponent(0).getHeight() - card.getY());
                            for (int i = pressed.getComponentZOrder(card), y = 0; i >= 0; i--, y += Patience.CARD_OPEN) {
                                dragged.add(pressed.getComponent(i), 0).setLocation(0, y);
                            }
                            MouseMotionAdapter mma = new MouseMotionAdapter() {
                                int x = dragged.getX() - e.getX(), y = dragged.getY() - e.getY();
                                @Override
                                public void mouseDragged(MouseEvent e) {
                                    dragged.setLocation(Math.max(0, Math.min(e.getX() + x, getWidth() - dragged.getWidth())), Math.max(0, Math.min(e.getY() + y, getHeight() - dragged.getHeight())));
                                }
                            };
                            add(dragged, new Integer(1));
                            addMouseMotionListener(mma);
                            addMouseListener(new MouseAdapter() {
                                Container next = pressed;
                                @Override
                                public void mouseReleased(MouseEvent e) {
                                    removeMouseListener(this);
                                    removeMouseMotionListener(mma);
                                    remove(dragged);
                                    Container released = (Container) getComponentAt(e.getPoint());
                                    if (released == pressed) {
                                        JLabel suit = TOP[Arrays.asList(Patience.SUIT).indexOf(card.SUIT)];
                                        if (card == dragged.getComponent(0) && card.RANK == Patience.RANK[suit.getComponentCount()]) {
                                            next = suit;
                                        }
                                    } else if (Arrays.asList(COLUMN).contains(released) && card.RANK != Patience.ACE) {
                                        if (card.RANK == Patience.KING) {
                                            if (released.getComponentCount() == 0) {
                                                next = released;
                                            }
                                        } else if (released.getComponentCount() >= 1) {
                                            Card last = (Card) released.getComponent(0);
                                            if (last.isOpen() && last.COLOR != card.COLOR && last.RANK == Patience.RANK[Arrays.asList(Patience.RANK).indexOf(card.RANK) + 1]) {
                                                next = released;
                                            }
                                        }
                                    }
                                    for (int i = dragged.getComponentZOrder(card); i >= 0; i--) {
                                        next.add(dragged.getComponent(i), 0);
                                    }
                                    if (TOP[0].getComponentCount() == Patience.RANK.length && TOP[1].getComponentCount() == Patience.RANK.length && TOP[2].getComponentCount() == Patience.RANK.length && TOP[3].getComponentCount() == Patience.RANK.length) {
                                        time = (System.currentTimeMillis() - time) / 1000;
                                        JLabel completed = new JLabel((time / 60) + " min " + (time % 60) + " sec", JLabel.CENTER);
                                        completed.setFont(new Font(Font.DIALOG, Font.BOLD, Patience.CARD_SIZE.height / 3));
                                        completed.setSize(getSize());
                                        add(completed, new Integer(1));
                                    }
                                    repaint();
                                }
                            });
                        } else if (card == pressed.getComponent(0)) {
                            card.flip();
                        }
                    }
                } catch (Exception ex) { }
            }
        });
    }
    
}
\$\endgroup\$
1
  • \$\begingroup\$ i would really LOVE to see a follow up question on your improvement (heart) \$\endgroup\$ Commented Sep 29, 2022 at 5:18

1 Answer 1

1
\$\begingroup\$

separation of concerns / MVC / Single responsibility

you mix your Model and your presentation, you should try to apply to the well known pattern MVC / MVVP / ...

→ violation of these lead to bloated classes

iosp

you mix integration methods with operation methods.

→ violation of iosp these leads to bloated methods

minor issue: proper use of enum

final public static Object[] RANK = {ACE, 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", KING};

use Enum instead

summary: use OOP

you should use a more object orientated programming (using oo analysis & oo design)

all your code is pressed into three classes - OMG

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.